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'>
80 const char kDatabaseVersionKey
[] = "INITDATA_DB_VERSION";
81 const char kNextRegIdKey
[] = "INITDATA_NEXT_REGISTRATION_ID";
82 const char kNextResIdKey
[] = "INITDATA_NEXT_RESOURCE_ID";
83 const char kNextVerIdKey
[] = "INITDATA_NEXT_VERSION_ID";
84 const char kUniqueOriginKey
[] = "INITDATA_UNIQUE_ORIGIN:";
86 const char kRegKeyPrefix
[] = "REG:";
87 const char kRegUserDataKeyPrefix
[] = "REG_USER_DATA:";
88 const char kRegHasUserDataKeyPrefix
[] = "REG_HAS_USER_DATA:";
89 const char kRegIdToOriginKeyPrefix
[] = "REGID_TO_ORIGIN:";
90 const char kResKeyPrefix
[] = "RES:";
91 const char kKeySeparator
= '\x00';
93 const char kUncommittedResIdKeyPrefix
[] = "URES:";
94 const char kPurgeableResIdKeyPrefix
[] = "PRES:";
96 const int64 kCurrentSchemaVersion
= 2;
98 class ServiceWorkerEnv
: public leveldb_env::ChromiumEnv
{
101 : ChromiumEnv("LevelDBEnv.ServiceWorker", false /* make_backup */) {}
104 base::LazyInstance
<ServiceWorkerEnv
>::Leaky g_service_worker_env
=
105 LAZY_INSTANCE_INITIALIZER
;
107 bool RemovePrefix(const std::string
& str
,
108 const std::string
& prefix
,
110 if (!StartsWithASCII(str
, prefix
, true))
113 *out
= str
.substr(prefix
.size());
117 std::string
CreateRegistrationKeyPrefix(const GURL
& origin
) {
118 return base::StringPrintf("%s%s%c", kRegKeyPrefix
,
119 origin
.GetOrigin().spec().c_str(), kKeySeparator
);
122 std::string
CreateRegistrationKey(int64 registration_id
,
123 const GURL
& origin
) {
124 return CreateRegistrationKeyPrefix(origin
)
125 .append(base::Int64ToString(registration_id
));
128 std::string
CreateResourceRecordKeyPrefix(int64 version_id
) {
129 return base::StringPrintf("%s%s%c",
131 base::Int64ToString(version_id
).c_str(),
135 std::string
CreateResourceRecordKey(int64 version_id
,
137 return CreateResourceRecordKeyPrefix(version_id
).append(
138 base::Int64ToString(resource_id
));
141 std::string
CreateUniqueOriginKey(const GURL
& origin
) {
142 return base::StringPrintf("%s%s", kUniqueOriginKey
,
143 origin
.GetOrigin().spec().c_str());
146 std::string
CreateResourceIdKey(const char* key_prefix
, int64 resource_id
) {
147 return base::StringPrintf(
148 "%s%s", key_prefix
, base::Int64ToString(resource_id
).c_str());
151 std::string
CreateUserDataKeyPrefix(int64 registration_id
) {
152 return base::StringPrintf("%s%s%c",
153 kRegUserDataKeyPrefix
,
154 base::Int64ToString(registration_id
).c_str(),
158 std::string
CreateUserDataKey(int64 registration_id
,
159 const std::string
& user_data_name
) {
160 return CreateUserDataKeyPrefix(registration_id
).append(user_data_name
);
163 std::string
CreateHasUserDataKeyPrefix(const std::string
& user_data_name
) {
164 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix
,
165 user_data_name
.c_str(), kKeySeparator
);
168 std::string
CreateHasUserDataKey(int64 registration_id
,
169 const std::string
& user_data_name
) {
170 return CreateHasUserDataKeyPrefix(user_data_name
)
171 .append(base::Int64ToString(registration_id
));
174 std::string
CreateRegistrationIdToOriginKey(int64 registration_id
) {
175 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix
,
176 base::Int64ToString(registration_id
).c_str());
179 void PutRegistrationDataToBatch(
180 const ServiceWorkerDatabase::RegistrationData
& input
,
181 leveldb::WriteBatch
* batch
) {
184 // Convert RegistrationData to ServiceWorkerRegistrationData.
185 ServiceWorkerRegistrationData data
;
186 data
.set_registration_id(input
.registration_id
);
187 data
.set_scope_url(input
.scope
.spec());
188 data
.set_script_url(input
.script
.spec());
189 data
.set_version_id(input
.version_id
);
190 data
.set_is_active(input
.is_active
);
191 data
.set_has_fetch_handler(input
.has_fetch_handler
);
192 data
.set_last_update_check_time(input
.last_update_check
.ToInternalValue());
193 data
.set_resources_total_size_bytes(input
.resources_total_size_bytes
);
196 bool success
= data
.SerializeToString(&value
);
198 GURL origin
= input
.scope
.GetOrigin();
199 batch
->Put(CreateRegistrationKey(data
.registration_id(), origin
), value
);
202 void PutResourceRecordToBatch(
203 const ServiceWorkerDatabase::ResourceRecord
& input
,
205 leveldb::WriteBatch
* batch
) {
207 DCHECK_GE(input
.size_bytes
, 0);
209 // Convert ResourceRecord to ServiceWorkerResourceRecord.
210 ServiceWorkerResourceRecord record
;
211 record
.set_resource_id(input
.resource_id
);
212 record
.set_url(input
.url
.spec());
213 record
.set_size_bytes(input
.size_bytes
);
216 bool success
= record
.SerializeToString(&value
);
218 batch
->Put(CreateResourceRecordKey(version_id
, input
.resource_id
), value
);
221 void PutUniqueOriginToBatch(const GURL
& origin
,
222 leveldb::WriteBatch
* batch
) {
223 // Value should be empty.
224 batch
->Put(CreateUniqueOriginKey(origin
), "");
227 void PutPurgeableResourceIdToBatch(int64 resource_id
,
228 leveldb::WriteBatch
* batch
) {
229 // Value should be empty.
230 batch
->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix
, resource_id
), "");
233 ServiceWorkerDatabase::Status
ParseId(
234 const std::string
& serialized
,
238 if (!base::StringToInt64(serialized
, &id
) || id
< 0)
239 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
241 return ServiceWorkerDatabase::STATUS_OK
;
244 ServiceWorkerDatabase::Status
ParseDatabaseVersion(
245 const std::string
& serialized
,
248 const int kFirstValidVersion
= 1;
250 if (!base::StringToInt64(serialized
, &version
) ||
251 version
< kFirstValidVersion
) {
252 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
254 if (kCurrentSchemaVersion
< version
) {
255 DLOG(ERROR
) << "ServiceWorkerDatabase has newer schema version"
256 << " than the current latest version: "
257 << version
<< " vs " << kCurrentSchemaVersion
;
258 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
261 return ServiceWorkerDatabase::STATUS_OK
;
264 ServiceWorkerDatabase::Status
ParseRegistrationData(
265 const std::string
& serialized
,
266 ServiceWorkerDatabase::RegistrationData
* out
) {
268 ServiceWorkerRegistrationData data
;
269 if (!data
.ParseFromString(serialized
))
270 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
272 GURL
scope_url(data
.scope_url());
273 GURL
script_url(data
.script_url());
274 if (!scope_url
.is_valid() ||
275 !script_url
.is_valid() ||
276 scope_url
.GetOrigin() != script_url
.GetOrigin()) {
277 DLOG(ERROR
) << "Scope URL '" << data
.scope_url() << "' and/or script url '"
279 << "' are invalid or have mismatching origins.";
280 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
283 // Convert ServiceWorkerRegistrationData to RegistrationData.
284 out
->registration_id
= data
.registration_id();
285 out
->scope
= scope_url
;
286 out
->script
= script_url
;
287 out
->version_id
= data
.version_id();
288 out
->is_active
= data
.is_active();
289 out
->has_fetch_handler
= data
.has_fetch_handler();
290 out
->last_update_check
=
291 base::Time::FromInternalValue(data
.last_update_check_time());
292 out
->resources_total_size_bytes
= data
.resources_total_size_bytes();
294 return ServiceWorkerDatabase::STATUS_OK
;
297 ServiceWorkerDatabase::Status
ParseResourceRecord(
298 const std::string
& serialized
,
299 ServiceWorkerDatabase::ResourceRecord
* out
) {
301 ServiceWorkerResourceRecord record
;
302 if (!record
.ParseFromString(serialized
))
303 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
305 GURL
url(record
.url());
307 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
309 // Convert ServiceWorkerResourceRecord to ResourceRecord.
310 out
->resource_id
= record
.resource_id();
312 out
->size_bytes
= record
.size_bytes();
313 return ServiceWorkerDatabase::STATUS_OK
;
316 ServiceWorkerDatabase::Status
LevelDBStatusToStatus(
317 const leveldb::Status
& status
) {
319 return ServiceWorkerDatabase::STATUS_OK
;
320 else if (status
.IsNotFound())
321 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
322 else if (status
.IsIOError())
323 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
;
324 else if (status
.IsCorruption())
325 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
327 return ServiceWorkerDatabase::STATUS_ERROR_FAILED
;
330 int64_t AccumulateResourceSizeInBytes(
331 const std::vector
<ServiceWorkerDatabase::ResourceRecord
>& resources
) {
332 int64_t total_size_bytes
= 0;
333 for (const auto& resource
: resources
)
334 total_size_bytes
+= resource
.size_bytes
;
335 return total_size_bytes
;
340 const char* ServiceWorkerDatabase::StatusToString(
341 ServiceWorkerDatabase::Status status
) {
343 case ServiceWorkerDatabase::STATUS_OK
:
344 return "Database OK";
345 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
:
346 return "Database not found";
347 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
:
348 return "Database IO error";
349 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
:
350 return "Database corrupted";
351 case ServiceWorkerDatabase::STATUS_ERROR_FAILED
:
352 return "Database operation failed";
353 case ServiceWorkerDatabase::STATUS_ERROR_MAX
:
355 return "Database unknown error";
358 return "Database unknown error";
361 ServiceWorkerDatabase::RegistrationData::RegistrationData()
362 : registration_id(kInvalidServiceWorkerRegistrationId
),
363 version_id(kInvalidServiceWorkerVersionId
),
365 has_fetch_handler(false),
366 resources_total_size_bytes(0) {
369 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
372 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath
& path
)
374 next_avail_registration_id_(0),
375 next_avail_resource_id_(0),
376 next_avail_version_id_(0),
377 state_(UNINITIALIZED
) {
378 sequence_checker_
.DetachFromSequence();
381 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
382 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
386 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetNextAvailableIds(
387 int64
* next_avail_registration_id
,
388 int64
* next_avail_version_id
,
389 int64
* next_avail_resource_id
) {
390 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
391 DCHECK(next_avail_registration_id
);
392 DCHECK(next_avail_version_id
);
393 DCHECK(next_avail_resource_id
);
395 Status status
= LazyOpen(false);
396 if (IsNewOrNonexistentDatabase(status
)) {
397 *next_avail_registration_id
= 0;
398 *next_avail_version_id
= 0;
399 *next_avail_resource_id
= 0;
402 if (status
!= STATUS_OK
)
405 status
= ReadNextAvailableId(kNextRegIdKey
, &next_avail_registration_id_
);
406 if (status
!= STATUS_OK
)
408 status
= ReadNextAvailableId(kNextVerIdKey
, &next_avail_version_id_
);
409 if (status
!= STATUS_OK
)
411 status
= ReadNextAvailableId(kNextResIdKey
, &next_avail_resource_id_
);
412 if (status
!= STATUS_OK
)
415 *next_avail_registration_id
= next_avail_registration_id_
;
416 *next_avail_version_id
= next_avail_version_id_
;
417 *next_avail_resource_id
= next_avail_resource_id_
;
421 ServiceWorkerDatabase::Status
422 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set
<GURL
>* origins
) {
423 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
424 DCHECK(origins
->empty());
426 Status status
= LazyOpen(false);
427 if (IsNewOrNonexistentDatabase(status
))
429 if (status
!= STATUS_OK
)
432 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
433 for (itr
->Seek(kUniqueOriginKey
); itr
->Valid(); itr
->Next()) {
434 status
= LevelDBStatusToStatus(itr
->status());
435 if (status
!= STATUS_OK
) {
436 HandleReadResult(FROM_HERE
, status
);
441 std::string origin_str
;
442 if (!RemovePrefix(itr
->key().ToString(), kUniqueOriginKey
, &origin_str
))
445 GURL
origin(origin_str
);
446 if (!origin
.is_valid()) {
447 status
= STATUS_ERROR_CORRUPTED
;
448 HandleReadResult(FROM_HERE
, status
);
453 origins
->insert(origin
);
456 HandleReadResult(FROM_HERE
, status
);
460 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetRegistrationsForOrigin(
462 std::vector
<RegistrationData
>* registrations
,
463 std::vector
<std::vector
<ResourceRecord
>>* opt_resources_list
) {
464 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
465 DCHECK(registrations
->empty());
467 Status status
= LazyOpen(false);
468 if (IsNewOrNonexistentDatabase(status
))
470 if (status
!= STATUS_OK
)
473 std::string prefix
= CreateRegistrationKeyPrefix(origin
);
474 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
475 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
476 status
= LevelDBStatusToStatus(itr
->status());
477 if (status
!= STATUS_OK
) {
478 HandleReadResult(FROM_HERE
, status
);
479 registrations
->clear();
480 if (opt_resources_list
)
481 opt_resources_list
->clear();
485 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
488 RegistrationData registration
;
489 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
490 if (status
!= STATUS_OK
) {
491 HandleReadResult(FROM_HERE
, status
);
492 registrations
->clear();
493 if (opt_resources_list
)
494 opt_resources_list
->clear();
497 registrations
->push_back(registration
);
499 if (opt_resources_list
) {
500 std::vector
<ResourceRecord
> resources
;
501 status
= ReadResourceRecords(registration
.version_id
, &resources
);
502 if (status
!= STATUS_OK
) {
503 HandleReadResult(FROM_HERE
, status
);
504 registrations
->clear();
505 opt_resources_list
->clear();
508 opt_resources_list
->push_back(resources
);
512 HandleReadResult(FROM_HERE
, status
);
516 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetAllRegistrations(
517 std::vector
<RegistrationData
>* registrations
) {
518 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
519 DCHECK(registrations
->empty());
521 Status status
= LazyOpen(false);
522 if (IsNewOrNonexistentDatabase(status
))
524 if (status
!= STATUS_OK
)
527 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
528 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
529 status
= LevelDBStatusToStatus(itr
->status());
530 if (status
!= STATUS_OK
) {
531 HandleReadResult(FROM_HERE
, status
);
532 registrations
->clear();
536 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, NULL
))
539 RegistrationData registration
;
540 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
541 if (status
!= STATUS_OK
) {
542 HandleReadResult(FROM_HERE
, status
);
543 registrations
->clear();
546 registrations
->push_back(registration
);
549 HandleReadResult(FROM_HERE
, status
);
553 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistration(
554 int64 registration_id
,
556 RegistrationData
* registration
,
557 std::vector
<ResourceRecord
>* resources
) {
558 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
559 DCHECK(registration
);
562 Status status
= LazyOpen(false);
563 if (IsNewOrNonexistentDatabase(status
))
564 return STATUS_ERROR_NOT_FOUND
;
565 if (status
!= STATUS_OK
)
568 RegistrationData value
;
569 status
= ReadRegistrationData(registration_id
, origin
, &value
);
570 if (status
!= STATUS_OK
)
573 status
= ReadResourceRecords(value
.version_id
, resources
);
574 if (status
!= STATUS_OK
)
577 // ResourceRecord must contain the ServiceWorker's main script.
578 if (resources
->empty())
579 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
581 *registration
= value
;
585 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationOrigin(
586 int64 registration_id
,
588 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
591 Status status
= LazyOpen(true);
592 if (IsNewOrNonexistentDatabase(status
))
593 return STATUS_ERROR_NOT_FOUND
;
594 if (status
!= STATUS_OK
)
598 status
= LevelDBStatusToStatus(
599 db_
->Get(leveldb::ReadOptions(),
600 CreateRegistrationIdToOriginKey(registration_id
), &value
));
601 if (status
!= STATUS_OK
) {
602 HandleReadResult(FROM_HERE
,
603 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
608 if (!parsed
.is_valid()) {
609 status
= STATUS_ERROR_CORRUPTED
;
610 HandleReadResult(FROM_HERE
, status
);
615 HandleReadResult(FROM_HERE
, STATUS_OK
);
619 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteRegistration(
620 const RegistrationData
& registration
,
621 const std::vector
<ResourceRecord
>& resources
,
622 RegistrationData
* old_registration
,
623 std::vector
<int64
>* newly_purgeable_resources
) {
624 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
625 DCHECK(old_registration
);
626 DCHECK(!resources
.empty());
627 Status status
= LazyOpen(true);
628 old_registration
->version_id
= kInvalidServiceWorkerVersionId
;
629 if (status
!= STATUS_OK
)
632 leveldb::WriteBatch batch
;
633 BumpNextRegistrationIdIfNeeded(registration
.registration_id
, &batch
);
634 BumpNextVersionIdIfNeeded(registration
.version_id
, &batch
);
636 PutUniqueOriginToBatch(registration
.scope
.GetOrigin(), &batch
);
638 DCHECK_EQ(AccumulateResourceSizeInBytes(resources
),
639 registration
.resources_total_size_bytes
)
640 << "The total size in the registration must match the cumulative "
641 << "sizes of the resources.";
643 PutRegistrationDataToBatch(registration
, &batch
);
644 batch
.Put(CreateRegistrationIdToOriginKey(registration
.registration_id
),
645 registration
.scope
.GetOrigin().spec());
647 // Used for avoiding multiple writes for the same resource id or url.
648 std::set
<int64
> pushed_resources
;
649 std::set
<GURL
> pushed_urls
;
650 for (std::vector
<ResourceRecord
>::const_iterator itr
= resources
.begin();
651 itr
!= resources
.end(); ++itr
) {
652 if (!itr
->url
.is_valid())
653 return STATUS_ERROR_FAILED
;
655 // Duplicated resource id or url should not exist.
656 DCHECK(pushed_resources
.insert(itr
->resource_id
).second
);
657 DCHECK(pushed_urls
.insert(itr
->url
).second
);
659 PutResourceRecordToBatch(*itr
, registration
.version_id
, &batch
);
661 // Delete a resource from the uncommitted list.
662 batch
.Delete(CreateResourceIdKey(
663 kUncommittedResIdKeyPrefix
, itr
->resource_id
));
664 // Delete from the purgeable list in case this version was once deleted.
666 CreateResourceIdKey(kPurgeableResIdKeyPrefix
, itr
->resource_id
));
669 // Retrieve a previous version to sweep purgeable resources.
670 status
= ReadRegistrationData(registration
.registration_id
,
671 registration
.scope
.GetOrigin(),
673 if (status
!= STATUS_OK
&& status
!= STATUS_ERROR_NOT_FOUND
)
675 if (status
== STATUS_OK
) {
676 DCHECK_LT(old_registration
->version_id
, registration
.version_id
);
677 status
= DeleteResourceRecords(
678 old_registration
->version_id
, newly_purgeable_resources
, &batch
);
679 if (status
!= STATUS_OK
)
682 // Currently resource sharing across versions and registrations is not
683 // supported, so resource ids should not be overlapped between
684 // |registration| and |old_registration|.
685 std::set
<int64
> deleted_resources(newly_purgeable_resources
->begin(),
686 newly_purgeable_resources
->end());
687 DCHECK(base::STLSetIntersection
<std::set
<int64
> >(
688 pushed_resources
, deleted_resources
).empty());
691 return WriteBatch(&batch
);
694 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateVersionToActive(
695 int64 registration_id
,
696 const GURL
& origin
) {
697 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
698 Status status
= LazyOpen(false);
699 if (IsNewOrNonexistentDatabase(status
))
700 return STATUS_ERROR_NOT_FOUND
;
701 if (status
!= STATUS_OK
)
703 if (!origin
.is_valid())
704 return STATUS_ERROR_FAILED
;
706 RegistrationData registration
;
707 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
708 if (status
!= STATUS_OK
)
711 registration
.is_active
= true;
713 leveldb::WriteBatch batch
;
714 PutRegistrationDataToBatch(registration
, &batch
);
715 return WriteBatch(&batch
);
718 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateLastCheckTime(
719 int64 registration_id
,
721 const base::Time
& time
) {
722 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
723 Status status
= LazyOpen(false);
724 if (IsNewOrNonexistentDatabase(status
))
725 return STATUS_ERROR_NOT_FOUND
;
726 if (status
!= STATUS_OK
)
728 if (!origin
.is_valid())
729 return STATUS_ERROR_FAILED
;
731 RegistrationData registration
;
732 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
733 if (status
!= STATUS_OK
)
736 registration
.last_update_check
= time
;
738 leveldb::WriteBatch batch
;
739 PutRegistrationDataToBatch(registration
, &batch
);
740 return WriteBatch(&batch
);
743 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteRegistration(
744 int64 registration_id
,
746 RegistrationData
* deleted_version
,
747 std::vector
<int64
>* newly_purgeable_resources
) {
748 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
749 DCHECK(deleted_version
);
750 deleted_version
->version_id
= kInvalidServiceWorkerVersionId
;
751 Status status
= LazyOpen(false);
752 if (IsNewOrNonexistentDatabase(status
))
754 if (status
!= STATUS_OK
)
756 if (!origin
.is_valid())
757 return STATUS_ERROR_FAILED
;
759 leveldb::WriteBatch batch
;
761 // Remove |origin| from unique origins if a registration specified by
762 // |registration_id| is the only one for |origin|.
763 // TODO(nhiroki): Check the uniqueness by more efficient way.
764 std::vector
<RegistrationData
> registrations
;
765 status
= GetRegistrationsForOrigin(origin
, ®istrations
, nullptr);
766 if (status
!= STATUS_OK
)
769 if (registrations
.size() == 1 &&
770 registrations
[0].registration_id
== registration_id
) {
771 batch
.Delete(CreateUniqueOriginKey(origin
));
774 // Delete a registration specified by |registration_id|.
775 batch
.Delete(CreateRegistrationKey(registration_id
, origin
));
776 batch
.Delete(CreateRegistrationIdToOriginKey(registration_id
));
778 // Delete resource records and user data associated with the registration.
779 for (const auto& registration
: registrations
) {
780 if (registration
.registration_id
== registration_id
) {
781 *deleted_version
= registration
;
782 status
= DeleteResourceRecords(
783 registration
.version_id
, newly_purgeable_resources
, &batch
);
784 if (status
!= STATUS_OK
)
787 status
= DeleteUserDataForRegistration(registration_id
, &batch
);
788 if (status
!= STATUS_OK
)
794 return WriteBatch(&batch
);
797 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadUserData(
798 int64 registration_id
,
799 const std::string
& user_data_name
,
800 std::string
* user_data
) {
801 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
802 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
803 DCHECK(!user_data_name
.empty());
806 Status status
= LazyOpen(false);
807 if (IsNewOrNonexistentDatabase(status
))
808 return STATUS_ERROR_NOT_FOUND
;
809 if (status
!= STATUS_OK
)
812 const std::string key
= CreateUserDataKey(registration_id
, user_data_name
);
813 status
= LevelDBStatusToStatus(
814 db_
->Get(leveldb::ReadOptions(), key
, user_data
));
815 HandleReadResult(FROM_HERE
,
816 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
820 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteUserData(
821 int64 registration_id
,
823 const std::string
& user_data_name
,
824 const std::string
& user_data
) {
825 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
826 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
827 DCHECK(!user_data_name
.empty());
829 Status status
= LazyOpen(false);
830 if (IsNewOrNonexistentDatabase(status
))
831 return STATUS_ERROR_NOT_FOUND
;
832 if (status
!= STATUS_OK
)
835 // There should be the registration specified by |registration_id|.
836 RegistrationData registration
;
837 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
838 if (status
!= STATUS_OK
)
841 leveldb::WriteBatch batch
;
842 batch
.Put(CreateUserDataKey(registration_id
, user_data_name
), user_data
);
843 batch
.Put(CreateHasUserDataKey(registration_id
, user_data_name
), "");
844 return WriteBatch(&batch
);
847 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteUserData(
848 int64 registration_id
,
849 const std::string
& user_data_name
) {
850 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
851 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
852 DCHECK(!user_data_name
.empty());
854 Status status
= LazyOpen(false);
855 if (IsNewOrNonexistentDatabase(status
))
857 if (status
!= STATUS_OK
)
860 leveldb::WriteBatch batch
;
861 batch
.Delete(CreateUserDataKey(registration_id
, user_data_name
));
862 batch
.Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
863 return WriteBatch(&batch
);
866 ServiceWorkerDatabase::Status
867 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
868 const std::string
& user_data_name
,
869 std::vector
<std::pair
<int64
, std::string
>>* user_data
) {
870 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
871 DCHECK(user_data
->empty());
873 Status status
= LazyOpen(false);
874 if (IsNewOrNonexistentDatabase(status
))
876 if (status
!= STATUS_OK
)
879 std::string key_prefix
= CreateHasUserDataKeyPrefix(user_data_name
);
880 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
881 for (itr
->Seek(key_prefix
); itr
->Valid(); itr
->Next()) {
882 status
= LevelDBStatusToStatus(itr
->status());
883 if (status
!= STATUS_OK
) {
884 HandleReadResult(FROM_HERE
, status
);
889 std::string registration_id_string
;
890 if (!RemovePrefix(itr
->key().ToString(), key_prefix
,
891 ®istration_id_string
)) {
895 int64 registration_id
;
896 status
= ParseId(registration_id_string
, ®istration_id
);
897 if (status
!= STATUS_OK
) {
898 HandleReadResult(FROM_HERE
, status
);
904 status
= LevelDBStatusToStatus(
905 db_
->Get(leveldb::ReadOptions(),
906 CreateUserDataKey(registration_id
, user_data_name
), &value
));
907 if (status
!= STATUS_OK
) {
908 HandleReadResult(FROM_HERE
, status
);
912 user_data
->push_back(std::make_pair(registration_id
, value
));
915 HandleReadResult(FROM_HERE
, status
);
919 ServiceWorkerDatabase::Status
920 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set
<int64
>* ids
) {
921 return ReadResourceIds(kUncommittedResIdKeyPrefix
, ids
);
924 ServiceWorkerDatabase::Status
925 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set
<int64
>& ids
) {
926 return WriteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
929 ServiceWorkerDatabase::Status
930 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set
<int64
>& ids
) {
931 return DeleteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
934 ServiceWorkerDatabase::Status
935 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set
<int64
>* ids
) {
936 return ReadResourceIds(kPurgeableResIdKeyPrefix
, ids
);
939 ServiceWorkerDatabase::Status
940 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set
<int64
>& ids
) {
941 return WriteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
944 ServiceWorkerDatabase::Status
945 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set
<int64
>& ids
) {
946 return DeleteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
949 ServiceWorkerDatabase::Status
950 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
951 const std::set
<int64
>& ids
) {
952 leveldb::WriteBatch batch
;
953 Status status
= DeleteResourceIdsInBatch(
954 kUncommittedResIdKeyPrefix
, ids
, &batch
);
955 if (status
!= STATUS_OK
)
957 status
= WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix
, ids
, &batch
);
958 if (status
!= STATUS_OK
)
960 return WriteBatch(&batch
);
963 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteAllDataForOrigins(
964 const std::set
<GURL
>& origins
,
965 std::vector
<int64
>* newly_purgeable_resources
) {
966 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
967 Status status
= LazyOpen(false);
968 if (IsNewOrNonexistentDatabase(status
))
970 if (status
!= STATUS_OK
)
972 leveldb::WriteBatch batch
;
974 for (const GURL
& origin
: origins
) {
975 if (!origin
.is_valid())
976 return STATUS_ERROR_FAILED
;
978 // Delete from the unique origin list.
979 batch
.Delete(CreateUniqueOriginKey(origin
));
981 std::vector
<RegistrationData
> registrations
;
982 status
= GetRegistrationsForOrigin(origin
, ®istrations
, nullptr);
983 if (status
!= STATUS_OK
)
986 // Delete registrations, resource records and user data.
987 for (const RegistrationData
& data
: registrations
) {
988 batch
.Delete(CreateRegistrationKey(data
.registration_id
, origin
));
989 batch
.Delete(CreateRegistrationIdToOriginKey(data
.registration_id
));
991 status
= DeleteResourceRecords(
992 data
.version_id
, newly_purgeable_resources
, &batch
);
993 if (status
!= STATUS_OK
)
996 status
= DeleteUserDataForRegistration(data
.registration_id
, &batch
);
997 if (status
!= STATUS_OK
)
1002 return WriteBatch(&batch
);
1005 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DestroyDatabase() {
1006 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1007 Disable(FROM_HERE
, STATUS_OK
);
1009 leveldb::Options options
;
1010 if (path_
.empty()) {
1012 options
.env
= env_
.get();
1014 // In-memory database not initialized.
1018 options
.env
= g_service_worker_env
.Pointer();
1022 LevelDBStatusToStatus(leveldb::DestroyDB(path_
.AsUTF8Unsafe(), options
));
1023 ServiceWorkerMetrics::RecordDestroyDatabaseResult(status
);
1027 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::LazyOpen(
1028 bool create_if_missing
) {
1029 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1031 // Do not try to open a database if we tried and failed once.
1032 if (state_
== DISABLED
)
1033 return STATUS_ERROR_FAILED
;
1037 // When |path_| is empty, open a database in-memory.
1038 bool use_in_memory_db
= path_
.empty();
1040 if (!create_if_missing
) {
1041 // Avoid opening a database if it does not exist at the |path_|.
1042 if (use_in_memory_db
||
1043 !base::PathExists(path_
) ||
1044 base::IsDirectoryEmpty(path_
)) {
1045 return STATUS_ERROR_NOT_FOUND
;
1049 leveldb::Options options
;
1050 options
.create_if_missing
= create_if_missing
;
1051 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
1052 if (use_in_memory_db
) {
1053 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1054 options
.env
= env_
.get();
1056 options
.env
= g_service_worker_env
.Pointer();
1059 leveldb::DB
* db
= NULL
;
1060 Status status
= LevelDBStatusToStatus(
1061 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
1062 HandleOpenResult(FROM_HERE
, status
);
1063 if (status
!= STATUS_OK
) {
1065 // TODO(nhiroki): Should we retry to open the database?
1071 status
= ReadDatabaseVersion(&db_version
);
1072 if (status
!= STATUS_OK
)
1074 DCHECK_LE(0, db_version
);
1076 if (db_version
> 0 && db_version
< kCurrentSchemaVersion
) {
1077 switch (db_version
) {
1079 status
= UpgradeDatabaseSchemaFromV1ToV2();
1080 if (status
!= STATUS_OK
)
1083 // Intentionally fall-through to other version upgrade cases.
1085 // Either the database got upgraded to the current schema version, or some
1086 // upgrade step failed which would have caused this method to abort.
1087 DCHECK_EQ(db_version
, kCurrentSchemaVersion
);
1091 state_
= INITIALIZED
;
1095 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1096 ServiceWorkerDatabase::Status status
) {
1097 if (status
== STATUS_ERROR_NOT_FOUND
)
1099 if (status
== STATUS_OK
&& state_
== UNINITIALIZED
)
1104 ServiceWorkerDatabase::Status
1105 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1106 Status status
= STATUS_OK
;
1107 leveldb::WriteBatch batch
;
1109 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1110 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1111 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
1112 status
= LevelDBStatusToStatus(itr
->status());
1113 if (status
!= STATUS_OK
) {
1114 HandleReadResult(FROM_HERE
, status
);
1119 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, &key
))
1122 std::vector
<std::string
> parts
;
1123 base::SplitStringDontTrim(key
, kKeySeparator
, &parts
);
1124 if (parts
.size() != 2) {
1125 status
= STATUS_ERROR_CORRUPTED
;
1126 HandleReadResult(FROM_HERE
, status
);
1130 int64 registration_id
;
1131 status
= ParseId(parts
[1], ®istration_id
);
1132 if (status
!= STATUS_OK
) {
1133 HandleReadResult(FROM_HERE
, status
);
1137 batch
.Put(CreateRegistrationIdToOriginKey(registration_id
), parts
[0]);
1140 // Update schema version manually instead of relying on WriteBatch to make
1141 // sure each upgrade step only updates it to the actually correct version.
1142 batch
.Put(kDatabaseVersionKey
, base::Int64ToString(2));
1143 status
= LevelDBStatusToStatus(
1144 db_
->Write(leveldb::WriteOptions(), &batch
));
1145 HandleWriteResult(FROM_HERE
, status
);
1149 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadNextAvailableId(
1151 int64
* next_avail_id
) {
1153 DCHECK(next_avail_id
);
1156 Status status
= LevelDBStatusToStatus(
1157 db_
->Get(leveldb::ReadOptions(), id_key
, &value
));
1158 if (status
== STATUS_ERROR_NOT_FOUND
) {
1159 // Nobody has gotten the next resource id for |id_key|.
1161 HandleReadResult(FROM_HERE
, STATUS_OK
);
1163 } else if (status
!= STATUS_OK
) {
1164 HandleReadResult(FROM_HERE
, status
);
1168 status
= ParseId(value
, next_avail_id
);
1169 HandleReadResult(FROM_HERE
, status
);
1173 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationData(
1174 int64 registration_id
,
1176 RegistrationData
* registration
) {
1177 DCHECK(registration
);
1179 const std::string key
= CreateRegistrationKey(registration_id
, origin
);
1181 Status status
= LevelDBStatusToStatus(
1182 db_
->Get(leveldb::ReadOptions(), key
, &value
));
1183 if (status
!= STATUS_OK
) {
1186 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
1190 status
= ParseRegistrationData(value
, registration
);
1191 HandleReadResult(FROM_HERE
, status
);
1195 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceRecords(
1197 std::vector
<ResourceRecord
>* resources
) {
1198 DCHECK(resources
->empty());
1200 Status status
= STATUS_OK
;
1201 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1203 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1204 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1205 Status status
= LevelDBStatusToStatus(itr
->status());
1206 if (status
!= STATUS_OK
) {
1207 HandleReadResult(FROM_HERE
, status
);
1212 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
1215 ResourceRecord resource
;
1216 status
= ParseResourceRecord(itr
->value().ToString(), &resource
);
1217 if (status
!= STATUS_OK
) {
1218 HandleReadResult(FROM_HERE
, status
);
1222 resources
->push_back(resource
);
1225 HandleReadResult(FROM_HERE
, status
);
1229 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceRecords(
1231 std::vector
<int64
>* newly_purgeable_resources
,
1232 leveldb::WriteBatch
* batch
) {
1235 Status status
= STATUS_OK
;
1236 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1238 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1239 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1240 status
= LevelDBStatusToStatus(itr
->status());
1241 if (status
!= STATUS_OK
) {
1242 HandleReadResult(FROM_HERE
, status
);
1246 const std::string key
= itr
->key().ToString();
1247 std::string unprefixed
;
1248 if (!RemovePrefix(key
, prefix
, &unprefixed
))
1252 status
= ParseId(unprefixed
, &resource_id
);
1253 if (status
!= STATUS_OK
) {
1254 HandleReadResult(FROM_HERE
, status
);
1258 // Remove a resource record.
1261 // Currently resource sharing across versions and registrations is not
1262 // supported, so we can purge this without caring about it.
1263 PutPurgeableResourceIdToBatch(resource_id
, batch
);
1264 newly_purgeable_resources
->push_back(resource_id
);
1267 HandleReadResult(FROM_HERE
, status
);
1271 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceIds(
1272 const char* id_key_prefix
,
1273 std::set
<int64
>* ids
) {
1274 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1275 DCHECK(id_key_prefix
);
1276 DCHECK(ids
->empty());
1278 Status status
= LazyOpen(false);
1279 if (IsNewOrNonexistentDatabase(status
))
1281 if (status
!= STATUS_OK
)
1284 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1285 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
1286 status
= LevelDBStatusToStatus(itr
->status());
1287 if (status
!= STATUS_OK
) {
1288 HandleReadResult(FROM_HERE
, status
);
1293 std::string unprefixed
;
1294 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
1298 status
= ParseId(unprefixed
, &resource_id
);
1299 if (status
!= STATUS_OK
) {
1300 HandleReadResult(FROM_HERE
, status
);
1304 ids
->insert(resource_id
);
1307 HandleReadResult(FROM_HERE
, status
);
1311 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIds(
1312 const char* id_key_prefix
,
1313 const std::set
<int64
>& ids
) {
1314 leveldb::WriteBatch batch
;
1315 Status status
= WriteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1316 if (status
!= STATUS_OK
)
1318 return WriteBatch(&batch
);
1321 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIdsInBatch(
1322 const char* id_key_prefix
,
1323 const std::set
<int64
>& ids
,
1324 leveldb::WriteBatch
* batch
) {
1325 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1326 DCHECK(id_key_prefix
);
1328 Status status
= LazyOpen(true);
1329 if (status
!= STATUS_OK
)
1334 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1335 itr
!= ids
.end(); ++itr
) {
1336 // Value should be empty.
1337 batch
->Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
1339 // std::set is sorted, so the last element is the largest.
1340 BumpNextResourceIdIfNeeded(*ids
.rbegin(), batch
);
1344 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIds(
1345 const char* id_key_prefix
,
1346 const std::set
<int64
>& ids
) {
1347 leveldb::WriteBatch batch
;
1348 Status status
= DeleteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1349 if (status
!= STATUS_OK
)
1351 return WriteBatch(&batch
);
1354 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1355 const char* id_key_prefix
,
1356 const std::set
<int64
>& ids
,
1357 leveldb::WriteBatch
* batch
) {
1358 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1359 DCHECK(id_key_prefix
);
1361 Status status
= LazyOpen(false);
1362 if (IsNewOrNonexistentDatabase(status
))
1364 if (status
!= STATUS_OK
)
1367 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1368 itr
!= ids
.end(); ++itr
) {
1369 batch
->Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
1374 ServiceWorkerDatabase::Status
1375 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1376 int64 registration_id
,
1377 leveldb::WriteBatch
* batch
) {
1379 Status status
= STATUS_OK
;
1380 const std::string prefix
= CreateUserDataKeyPrefix(registration_id
);
1382 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1383 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1384 status
= LevelDBStatusToStatus(itr
->status());
1385 if (status
!= STATUS_OK
) {
1386 HandleReadResult(FROM_HERE
, status
);
1390 const std::string key
= itr
->key().ToString();
1391 std::string user_data_name
;
1392 if (!RemovePrefix(key
, prefix
, &user_data_name
))
1395 batch
->Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
1400 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadDatabaseVersion(
1401 int64
* db_version
) {
1403 Status status
= LevelDBStatusToStatus(
1404 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
));
1405 if (status
== STATUS_ERROR_NOT_FOUND
) {
1406 // The database hasn't been initialized yet.
1408 HandleReadResult(FROM_HERE
, STATUS_OK
);
1412 if (status
!= STATUS_OK
) {
1413 HandleReadResult(FROM_HERE
, status
);
1417 status
= ParseDatabaseVersion(value
, db_version
);
1418 HandleReadResult(FROM_HERE
, status
);
1422 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteBatch(
1423 leveldb::WriteBatch
* batch
) {
1425 DCHECK_NE(DISABLED
, state_
);
1427 if (state_
== UNINITIALIZED
) {
1428 // Write the database schema version.
1429 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
1430 state_
= INITIALIZED
;
1433 Status status
= LevelDBStatusToStatus(
1434 db_
->Write(leveldb::WriteOptions(), batch
));
1435 HandleWriteResult(FROM_HERE
, status
);
1439 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1440 int64 used_id
, leveldb::WriteBatch
* batch
) {
1442 if (next_avail_registration_id_
<= used_id
) {
1443 next_avail_registration_id_
= used_id
+ 1;
1444 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
1448 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1449 int64 used_id
, leveldb::WriteBatch
* batch
) {
1451 if (next_avail_resource_id_
<= used_id
) {
1452 next_avail_resource_id_
= used_id
+ 1;
1453 batch
->Put(kNextResIdKey
, base::Int64ToString(next_avail_resource_id_
));
1457 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1458 int64 used_id
, leveldb::WriteBatch
* batch
) {
1460 if (next_avail_version_id_
<= used_id
) {
1461 next_avail_version_id_
= used_id
+ 1;
1462 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
1466 bool ServiceWorkerDatabase::IsOpen() {
1470 void ServiceWorkerDatabase::Disable(
1471 const tracked_objects::Location
& from_here
,
1473 if (status
!= STATUS_OK
) {
1474 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
1475 << " with error: " << StatusToString(status
);
1476 DLOG(ERROR
) << "ServiceWorkerDatabase is disabled.";
1482 void ServiceWorkerDatabase::HandleOpenResult(
1483 const tracked_objects::Location
& from_here
,
1485 if (status
!= STATUS_OK
)
1486 Disable(from_here
, status
);
1487 ServiceWorkerMetrics::CountOpenDatabaseResult(status
);
1490 void ServiceWorkerDatabase::HandleReadResult(
1491 const tracked_objects::Location
& from_here
,
1493 if (status
!= STATUS_OK
)
1494 Disable(from_here
, status
);
1495 ServiceWorkerMetrics::CountReadDatabaseResult(status
);
1498 void ServiceWorkerDatabase::HandleWriteResult(
1499 const tracked_objects::Location
& from_here
,
1501 if (status
!= STATUS_OK
)
1502 Disable(from_here
, status
);
1503 ServiceWorkerMetrics::CountWriteDatabaseResult(status
);
1506 } // namespace content