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/location.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/browser/service_worker/service_worker_database.pb.h"
17 #include "content/browser/service_worker/service_worker_metrics.h"
18 #include "content/common/service_worker/service_worker_types.h"
19 #include "third_party/leveldatabase/env_chromium.h"
20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
21 #include "third_party/leveldatabase/src/include/leveldb/db.h"
22 #include "third_party/leveldatabase/src/include/leveldb/env.h"
23 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
25 // LevelDB database schema
26 // =======================
29 // - int64 value is serialized as a string by base::Int64ToString().
30 // - GURL value is serialized as a string by GURL::spec().
32 // Version 1 (in sorted order)
33 // key: "INITDATA_DB_VERSION"
36 // key: "INITDATA_NEXT_REGISTRATION_ID"
37 // value: <int64 'next_available_registration_id'>
39 // key: "INITDATA_NEXT_RESOURCE_ID"
40 // value: <int64 'next_available_resource_id'>
42 // key: "INITDATA_NEXT_VERSION_ID"
43 // value: <int64 'next_available_version_id'>
45 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
48 // key: "PRES:" + <int64 'purgeable_resource_id'>
51 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
52 // (ex. "REG:http://example.com\x00123456")
53 // value: <ServiceWorkerRegistrationData serialized as a string>
55 // key: "REG_HAS_USER_DATA:" + <std::string 'user_data_name'> + '\x00'
56 // + <int64 'registration_id'>
59 // key: "REG_USER_DATA:" + <int64 'registration_id'> + '\x00'
60 // + <std::string user_data_name>
61 // (ex. "REG_USER_DATA:123456\x00foo_bar")
62 // value: <std::string user_data>
64 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
65 // (ex. "RES:123456\x00654321")
66 // value: <ServiceWorkerResourceRecord serialized as a string>
68 // key: "URES:" + <int64 'uncommitted_resource_id'>
73 // key: "REGID_TO_ORIGIN:" + <int64 'registration_id'>
74 // value: <GURL 'origin'>
79 const char kDatabaseVersionKey
[] = "INITDATA_DB_VERSION";
80 const char kNextRegIdKey
[] = "INITDATA_NEXT_REGISTRATION_ID";
81 const char kNextResIdKey
[] = "INITDATA_NEXT_RESOURCE_ID";
82 const char kNextVerIdKey
[] = "INITDATA_NEXT_VERSION_ID";
83 const char kUniqueOriginKey
[] = "INITDATA_UNIQUE_ORIGIN:";
85 const char kRegKeyPrefix
[] = "REG:";
86 const char kRegUserDataKeyPrefix
[] = "REG_USER_DATA:";
87 const char kRegHasUserDataKeyPrefix
[] = "REG_HAS_USER_DATA:";
88 const char kRegIdToOriginKeyPrefix
[] = "REGID_TO_ORIGIN:";
89 const char kResKeyPrefix
[] = "RES:";
90 const char kKeySeparator
= '\x00';
92 const char kUncommittedResIdKeyPrefix
[] = "URES:";
93 const char kPurgeableResIdKeyPrefix
[] = "PRES:";
95 const int64 kCurrentSchemaVersion
= 2;
97 bool RemovePrefix(const std::string
& str
,
98 const std::string
& prefix
,
100 if (!StartsWithASCII(str
, prefix
, true))
103 *out
= str
.substr(prefix
.size());
107 std::string
CreateRegistrationKey(int64 registration_id
,
108 const GURL
& origin
) {
109 return base::StringPrintf("%s%s%c%s",
111 origin
.spec().c_str(),
113 base::Int64ToString(registration_id
).c_str());
116 std::string
CreateResourceRecordKeyPrefix(int64 version_id
) {
117 return base::StringPrintf("%s%s%c",
119 base::Int64ToString(version_id
).c_str(),
123 std::string
CreateResourceRecordKey(int64 version_id
,
125 return CreateResourceRecordKeyPrefix(version_id
).append(
126 base::Int64ToString(resource_id
));
129 std::string
CreateUniqueOriginKey(const GURL
& origin
) {
130 return base::StringPrintf("%s%s", kUniqueOriginKey
, origin
.spec().c_str());
133 std::string
CreateResourceIdKey(const char* key_prefix
, int64 resource_id
) {
134 return base::StringPrintf(
135 "%s%s", key_prefix
, base::Int64ToString(resource_id
).c_str());
138 std::string
CreateUserDataKeyPrefix(int64 registration_id
) {
139 return base::StringPrintf("%s%s%c",
140 kRegUserDataKeyPrefix
,
141 base::Int64ToString(registration_id
).c_str(),
145 std::string
CreateUserDataKey(int64 registration_id
,
146 const std::string
& user_data_name
) {
147 return CreateUserDataKeyPrefix(registration_id
).append(user_data_name
);
150 std::string
CreateHasUserDataKeyPrefix(const std::string
& user_data_name
) {
151 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix
,
152 user_data_name
.c_str(), kKeySeparator
);
155 std::string
CreateHasUserDataKey(int64 registration_id
,
156 const std::string
& user_data_name
) {
157 return CreateHasUserDataKeyPrefix(user_data_name
)
158 .append(base::Int64ToString(registration_id
));
161 std::string
CreateRegistrationIdToOriginKey(int64 registration_id
) {
162 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix
,
163 base::Int64ToString(registration_id
).c_str());
166 void PutRegistrationDataToBatch(
167 const ServiceWorkerDatabase::RegistrationData
& input
,
168 leveldb::WriteBatch
* batch
) {
171 // Convert RegistrationData to ServiceWorkerRegistrationData.
172 ServiceWorkerRegistrationData data
;
173 data
.set_registration_id(input
.registration_id
);
174 data
.set_scope_url(input
.scope
.spec());
175 data
.set_script_url(input
.script
.spec());
176 data
.set_version_id(input
.version_id
);
177 data
.set_is_active(input
.is_active
);
178 data
.set_has_fetch_handler(input
.has_fetch_handler
);
179 data
.set_last_update_check_time(input
.last_update_check
.ToInternalValue());
180 data
.set_resources_total_size_bytes(input
.resources_total_size_bytes
);
183 bool success
= data
.SerializeToString(&value
);
185 GURL origin
= input
.scope
.GetOrigin();
186 batch
->Put(CreateRegistrationKey(data
.registration_id(), origin
), value
);
189 void PutResourceRecordToBatch(
190 const ServiceWorkerDatabase::ResourceRecord
& input
,
192 leveldb::WriteBatch
* batch
) {
194 DCHECK_GE(input
.size_bytes
, 0);
196 // Convert ResourceRecord to ServiceWorkerResourceRecord.
197 ServiceWorkerResourceRecord record
;
198 record
.set_resource_id(input
.resource_id
);
199 record
.set_url(input
.url
.spec());
200 record
.set_size_bytes(input
.size_bytes
);
203 bool success
= record
.SerializeToString(&value
);
205 batch
->Put(CreateResourceRecordKey(version_id
, input
.resource_id
), value
);
208 void PutUniqueOriginToBatch(const GURL
& origin
,
209 leveldb::WriteBatch
* batch
) {
210 // Value should be empty.
211 batch
->Put(CreateUniqueOriginKey(origin
), "");
214 void PutPurgeableResourceIdToBatch(int64 resource_id
,
215 leveldb::WriteBatch
* batch
) {
216 // Value should be empty.
217 batch
->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix
, resource_id
), "");
220 ServiceWorkerDatabase::Status
ParseId(
221 const std::string
& serialized
,
225 if (!base::StringToInt64(serialized
, &id
) || id
< 0)
226 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
228 return ServiceWorkerDatabase::STATUS_OK
;
231 ServiceWorkerDatabase::Status
ParseDatabaseVersion(
232 const std::string
& serialized
,
235 const int kFirstValidVersion
= 1;
237 if (!base::StringToInt64(serialized
, &version
) ||
238 version
< kFirstValidVersion
) {
239 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
241 if (kCurrentSchemaVersion
< version
) {
242 DLOG(ERROR
) << "ServiceWorkerDatabase has newer schema version"
243 << " than the current latest version: "
244 << version
<< " vs " << kCurrentSchemaVersion
;
245 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
248 return ServiceWorkerDatabase::STATUS_OK
;
251 ServiceWorkerDatabase::Status
ParseRegistrationData(
252 const std::string
& serialized
,
253 ServiceWorkerDatabase::RegistrationData
* out
) {
255 ServiceWorkerRegistrationData data
;
256 if (!data
.ParseFromString(serialized
))
257 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
259 GURL
scope_url(data
.scope_url());
260 GURL
script_url(data
.script_url());
261 if (!scope_url
.is_valid() ||
262 !script_url
.is_valid() ||
263 scope_url
.GetOrigin() != script_url
.GetOrigin()) {
264 DLOG(ERROR
) << "Scope URL '" << data
.scope_url() << "' and/or script url '"
266 << "' are invalid or have mismatching origins.";
267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
270 // Convert ServiceWorkerRegistrationData to RegistrationData.
271 out
->registration_id
= data
.registration_id();
272 out
->scope
= scope_url
;
273 out
->script
= script_url
;
274 out
->version_id
= data
.version_id();
275 out
->is_active
= data
.is_active();
276 out
->has_fetch_handler
= data
.has_fetch_handler();
277 out
->last_update_check
=
278 base::Time::FromInternalValue(data
.last_update_check_time());
279 out
->resources_total_size_bytes
= data
.resources_total_size_bytes();
281 return ServiceWorkerDatabase::STATUS_OK
;
284 ServiceWorkerDatabase::Status
ParseResourceRecord(
285 const std::string
& serialized
,
286 ServiceWorkerDatabase::ResourceRecord
* out
) {
288 ServiceWorkerResourceRecord record
;
289 if (!record
.ParseFromString(serialized
))
290 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
292 GURL
url(record
.url());
294 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
296 // Convert ServiceWorkerResourceRecord to ResourceRecord.
297 out
->resource_id
= record
.resource_id();
299 out
->size_bytes
= record
.size_bytes();
300 return ServiceWorkerDatabase::STATUS_OK
;
303 ServiceWorkerDatabase::Status
LevelDBStatusToStatus(
304 const leveldb::Status
& status
) {
306 return ServiceWorkerDatabase::STATUS_OK
;
307 else if (status
.IsNotFound())
308 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
309 else if (status
.IsIOError())
310 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
;
311 else if (status
.IsCorruption())
312 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
314 return ServiceWorkerDatabase::STATUS_ERROR_FAILED
;
319 const char* ServiceWorkerDatabase::StatusToString(
320 ServiceWorkerDatabase::Status status
) {
322 case ServiceWorkerDatabase::STATUS_OK
:
323 return "Database OK";
324 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
:
325 return "Database not found";
326 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
:
327 return "Database IO error";
328 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
:
329 return "Database corrupted";
330 case ServiceWorkerDatabase::STATUS_ERROR_FAILED
:
331 return "Database operation failed";
332 case ServiceWorkerDatabase::STATUS_ERROR_MAX
:
334 return "Database unknown error";
337 return "Database unknown error";
340 ServiceWorkerDatabase::RegistrationData::RegistrationData()
341 : registration_id(kInvalidServiceWorkerRegistrationId
),
342 version_id(kInvalidServiceWorkerVersionId
),
344 has_fetch_handler(false),
345 resources_total_size_bytes(0) {
348 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
351 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath
& path
)
353 next_avail_registration_id_(0),
354 next_avail_resource_id_(0),
355 next_avail_version_id_(0),
356 state_(UNINITIALIZED
) {
357 sequence_checker_
.DetachFromSequence();
360 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
361 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
365 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetNextAvailableIds(
366 int64
* next_avail_registration_id
,
367 int64
* next_avail_version_id
,
368 int64
* next_avail_resource_id
) {
369 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
370 DCHECK(next_avail_registration_id
);
371 DCHECK(next_avail_version_id
);
372 DCHECK(next_avail_resource_id
);
374 Status status
= LazyOpen(false);
375 if (IsNewOrNonexistentDatabase(status
)) {
376 *next_avail_registration_id
= 0;
377 *next_avail_version_id
= 0;
378 *next_avail_resource_id
= 0;
381 if (status
!= STATUS_OK
)
384 status
= ReadNextAvailableId(kNextRegIdKey
, &next_avail_registration_id_
);
385 if (status
!= STATUS_OK
)
387 status
= ReadNextAvailableId(kNextVerIdKey
, &next_avail_version_id_
);
388 if (status
!= STATUS_OK
)
390 status
= ReadNextAvailableId(kNextResIdKey
, &next_avail_resource_id_
);
391 if (status
!= STATUS_OK
)
394 *next_avail_registration_id
= next_avail_registration_id_
;
395 *next_avail_version_id
= next_avail_version_id_
;
396 *next_avail_resource_id
= next_avail_resource_id_
;
400 ServiceWorkerDatabase::Status
401 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set
<GURL
>* origins
) {
402 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
403 DCHECK(origins
->empty());
405 Status status
= LazyOpen(false);
406 if (IsNewOrNonexistentDatabase(status
))
408 if (status
!= STATUS_OK
)
411 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
412 for (itr
->Seek(kUniqueOriginKey
); itr
->Valid(); itr
->Next()) {
413 status
= LevelDBStatusToStatus(itr
->status());
414 if (status
!= STATUS_OK
) {
415 HandleReadResult(FROM_HERE
, status
);
421 if (!RemovePrefix(itr
->key().ToString(), kUniqueOriginKey
, &origin
))
423 origins
->insert(GURL(origin
));
426 HandleReadResult(FROM_HERE
, status
);
430 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetRegistrationsForOrigin(
432 std::vector
<RegistrationData
>* registrations
) {
433 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
434 DCHECK(registrations
->empty());
436 Status status
= LazyOpen(false);
437 if (IsNewOrNonexistentDatabase(status
))
439 if (status
!= STATUS_OK
)
442 // Create a key prefix for registrations.
443 std::string prefix
= base::StringPrintf(
444 "%s%s%c", kRegKeyPrefix
, origin
.spec().c_str(), kKeySeparator
);
446 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
447 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
448 status
= LevelDBStatusToStatus(itr
->status());
449 if (status
!= STATUS_OK
) {
450 HandleReadResult(FROM_HERE
, status
);
451 registrations
->clear();
455 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
458 RegistrationData registration
;
459 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
460 if (status
!= STATUS_OK
) {
461 HandleReadResult(FROM_HERE
, status
);
462 registrations
->clear();
465 registrations
->push_back(registration
);
468 HandleReadResult(FROM_HERE
, status
);
472 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetAllRegistrations(
473 std::vector
<RegistrationData
>* registrations
) {
474 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
475 DCHECK(registrations
->empty());
477 Status status
= LazyOpen(false);
478 if (IsNewOrNonexistentDatabase(status
))
480 if (status
!= STATUS_OK
)
483 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
484 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
485 status
= LevelDBStatusToStatus(itr
->status());
486 if (status
!= STATUS_OK
) {
487 HandleReadResult(FROM_HERE
, status
);
488 registrations
->clear();
492 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, NULL
))
495 RegistrationData registration
;
496 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
497 if (status
!= STATUS_OK
) {
498 HandleReadResult(FROM_HERE
, status
);
499 registrations
->clear();
502 registrations
->push_back(registration
);
505 HandleReadResult(FROM_HERE
, status
);
509 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistration(
510 int64 registration_id
,
512 RegistrationData
* registration
,
513 std::vector
<ResourceRecord
>* resources
) {
514 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
515 DCHECK(registration
);
518 Status status
= LazyOpen(false);
519 if (IsNewOrNonexistentDatabase(status
))
520 return STATUS_ERROR_NOT_FOUND
;
521 if (status
!= STATUS_OK
)
524 RegistrationData value
;
525 status
= ReadRegistrationData(registration_id
, origin
, &value
);
526 if (status
!= STATUS_OK
)
529 status
= ReadResourceRecords(value
.version_id
, resources
);
530 if (status
!= STATUS_OK
)
533 *registration
= value
;
537 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationOrigin(
538 int64 registration_id
,
540 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
543 Status status
= LazyOpen(true);
544 if (IsNewOrNonexistentDatabase(status
))
545 return STATUS_ERROR_NOT_FOUND
;
546 if (status
!= STATUS_OK
)
550 status
= LevelDBStatusToStatus(
551 db_
->Get(leveldb::ReadOptions(),
552 CreateRegistrationIdToOriginKey(registration_id
), &value
));
553 if (status
!= STATUS_OK
) {
554 HandleReadResult(FROM_HERE
,
555 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
560 if (!parsed
.is_valid()) {
561 status
= STATUS_ERROR_CORRUPTED
;
562 HandleReadResult(FROM_HERE
, status
);
567 HandleReadResult(FROM_HERE
, STATUS_OK
);
571 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteRegistration(
572 const RegistrationData
& registration
,
573 const std::vector
<ResourceRecord
>& resources
,
574 RegistrationData
* old_registration
,
575 std::vector
<int64
>* newly_purgeable_resources
) {
576 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
577 DCHECK(old_registration
);
578 Status status
= LazyOpen(true);
579 old_registration
->version_id
= kInvalidServiceWorkerVersionId
;
580 if (status
!= STATUS_OK
)
583 leveldb::WriteBatch batch
;
584 BumpNextRegistrationIdIfNeeded(registration
.registration_id
, &batch
);
585 BumpNextVersionIdIfNeeded(registration
.version_id
, &batch
);
587 PutUniqueOriginToBatch(registration
.scope
.GetOrigin(), &batch
);
589 int64_t total_size_bytes
= 0;
590 for (const auto& resource
: resources
) {
591 total_size_bytes
+= resource
.size_bytes
;
593 DCHECK_EQ(total_size_bytes
, registration
.resources_total_size_bytes
)
594 << "The total size in the registration must match the cumulative "
595 << "sizes of the resources.";
597 PutRegistrationDataToBatch(registration
, &batch
);
598 batch
.Put(CreateRegistrationIdToOriginKey(registration
.registration_id
),
599 registration
.scope
.GetOrigin().spec());
601 // Used for avoiding multiple writes for the same resource id or url.
602 std::set
<int64
> pushed_resources
;
603 std::set
<GURL
> pushed_urls
;
604 for (std::vector
<ResourceRecord
>::const_iterator itr
= resources
.begin();
605 itr
!= resources
.end(); ++itr
) {
606 if (!itr
->url
.is_valid())
607 return STATUS_ERROR_FAILED
;
609 // Duplicated resource id or url should not exist.
610 DCHECK(pushed_resources
.insert(itr
->resource_id
).second
);
611 DCHECK(pushed_urls
.insert(itr
->url
).second
);
613 PutResourceRecordToBatch(*itr
, registration
.version_id
, &batch
);
615 // Delete a resource from the uncommitted list.
616 batch
.Delete(CreateResourceIdKey(
617 kUncommittedResIdKeyPrefix
, itr
->resource_id
));
618 // Delete from the purgeable list in case this version was once deleted.
620 CreateResourceIdKey(kPurgeableResIdKeyPrefix
, itr
->resource_id
));
623 // Retrieve a previous version to sweep purgeable resources.
624 status
= ReadRegistrationData(registration
.registration_id
,
625 registration
.scope
.GetOrigin(),
627 if (status
!= STATUS_OK
&& status
!= STATUS_ERROR_NOT_FOUND
)
629 if (status
== STATUS_OK
) {
630 DCHECK_LT(old_registration
->version_id
, registration
.version_id
);
631 status
= DeleteResourceRecords(
632 old_registration
->version_id
, newly_purgeable_resources
, &batch
);
633 if (status
!= STATUS_OK
)
636 // Currently resource sharing across versions and registrations is not
637 // supported, so resource ids should not be overlapped between
638 // |registration| and |old_registration|.
639 std::set
<int64
> deleted_resources(newly_purgeable_resources
->begin(),
640 newly_purgeable_resources
->end());
641 DCHECK(base::STLSetIntersection
<std::set
<int64
> >(
642 pushed_resources
, deleted_resources
).empty());
645 return WriteBatch(&batch
);
648 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateVersionToActive(
649 int64 registration_id
,
650 const GURL
& origin
) {
651 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
652 Status status
= LazyOpen(false);
653 if (IsNewOrNonexistentDatabase(status
))
654 return STATUS_ERROR_NOT_FOUND
;
655 if (status
!= STATUS_OK
)
657 if (!origin
.is_valid())
658 return STATUS_ERROR_FAILED
;
660 RegistrationData registration
;
661 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
662 if (status
!= STATUS_OK
)
665 registration
.is_active
= true;
667 leveldb::WriteBatch batch
;
668 PutRegistrationDataToBatch(registration
, &batch
);
669 return WriteBatch(&batch
);
672 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateLastCheckTime(
673 int64 registration_id
,
675 const base::Time
& time
) {
676 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
677 Status status
= LazyOpen(false);
678 if (IsNewOrNonexistentDatabase(status
))
679 return STATUS_ERROR_NOT_FOUND
;
680 if (status
!= STATUS_OK
)
682 if (!origin
.is_valid())
683 return STATUS_ERROR_FAILED
;
685 RegistrationData registration
;
686 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
687 if (status
!= STATUS_OK
)
690 registration
.last_update_check
= time
;
692 leveldb::WriteBatch batch
;
693 PutRegistrationDataToBatch(registration
, &batch
);
694 return WriteBatch(&batch
);
697 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteRegistration(
698 int64 registration_id
,
700 RegistrationData
* deleted_version
,
701 std::vector
<int64
>* newly_purgeable_resources
) {
702 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
703 DCHECK(deleted_version
);
704 deleted_version
->version_id
= kInvalidServiceWorkerVersionId
;
705 Status status
= LazyOpen(false);
706 if (IsNewOrNonexistentDatabase(status
))
708 if (status
!= STATUS_OK
)
710 if (!origin
.is_valid())
711 return STATUS_ERROR_FAILED
;
713 leveldb::WriteBatch batch
;
715 // Remove |origin| from unique origins if a registration specified by
716 // |registration_id| is the only one for |origin|.
717 // TODO(nhiroki): Check the uniqueness by more efficient way.
718 std::vector
<RegistrationData
> registrations
;
719 status
= GetRegistrationsForOrigin(origin
, ®istrations
);
720 if (status
!= STATUS_OK
)
723 if (registrations
.size() == 1 &&
724 registrations
[0].registration_id
== registration_id
) {
725 batch
.Delete(CreateUniqueOriginKey(origin
));
728 // Delete a registration specified by |registration_id|.
729 batch
.Delete(CreateRegistrationKey(registration_id
, origin
));
730 batch
.Delete(CreateRegistrationIdToOriginKey(registration_id
));
732 // Delete resource records and user data associated with the registration.
733 for (const auto& registration
: registrations
) {
734 if (registration
.registration_id
== registration_id
) {
735 *deleted_version
= registration
;
736 status
= DeleteResourceRecords(
737 registration
.version_id
, newly_purgeable_resources
, &batch
);
738 if (status
!= STATUS_OK
)
741 status
= DeleteUserDataForRegistration(registration_id
, &batch
);
742 if (status
!= STATUS_OK
)
748 return WriteBatch(&batch
);
751 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadUserData(
752 int64 registration_id
,
753 const std::string
& user_data_name
,
754 std::string
* user_data
) {
755 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
756 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
757 DCHECK(!user_data_name
.empty());
760 Status status
= LazyOpen(false);
761 if (IsNewOrNonexistentDatabase(status
))
762 return STATUS_ERROR_NOT_FOUND
;
763 if (status
!= STATUS_OK
)
766 const std::string key
= CreateUserDataKey(registration_id
, user_data_name
);
767 status
= LevelDBStatusToStatus(
768 db_
->Get(leveldb::ReadOptions(), key
, user_data
));
769 HandleReadResult(FROM_HERE
,
770 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
774 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteUserData(
775 int64 registration_id
,
777 const std::string
& user_data_name
,
778 const std::string
& user_data
) {
779 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
780 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
781 DCHECK(!user_data_name
.empty());
783 Status status
= LazyOpen(false);
784 if (IsNewOrNonexistentDatabase(status
))
785 return STATUS_ERROR_NOT_FOUND
;
786 if (status
!= STATUS_OK
)
789 // There should be the registration specified by |registration_id|.
790 RegistrationData registration
;
791 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
792 if (status
!= STATUS_OK
)
795 leveldb::WriteBatch batch
;
796 batch
.Put(CreateUserDataKey(registration_id
, user_data_name
), user_data
);
797 batch
.Put(CreateHasUserDataKey(registration_id
, user_data_name
), "");
798 return WriteBatch(&batch
);
801 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteUserData(
802 int64 registration_id
,
803 const std::string
& user_data_name
) {
804 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
805 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
806 DCHECK(!user_data_name
.empty());
808 Status status
= LazyOpen(false);
809 if (IsNewOrNonexistentDatabase(status
))
811 if (status
!= STATUS_OK
)
814 leveldb::WriteBatch batch
;
815 batch
.Delete(CreateUserDataKey(registration_id
, user_data_name
));
816 batch
.Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
817 return WriteBatch(&batch
);
820 ServiceWorkerDatabase::Status
821 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
822 const std::string
& user_data_name
,
823 std::vector
<std::pair
<int64
, std::string
>>* user_data
) {
824 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
825 DCHECK(user_data
->empty());
827 Status status
= LazyOpen(false);
828 if (IsNewOrNonexistentDatabase(status
))
830 if (status
!= STATUS_OK
)
833 std::string key_prefix
= CreateHasUserDataKeyPrefix(user_data_name
);
834 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
835 for (itr
->Seek(key_prefix
); itr
->Valid(); itr
->Next()) {
836 status
= LevelDBStatusToStatus(itr
->status());
837 if (status
!= STATUS_OK
) {
838 HandleReadResult(FROM_HERE
, status
);
843 std::string registration_id_string
;
844 if (!RemovePrefix(itr
->key().ToString(), key_prefix
,
845 ®istration_id_string
)) {
849 int64 registration_id
;
850 status
= ParseId(registration_id_string
, ®istration_id
);
851 if (status
!= STATUS_OK
) {
852 HandleReadResult(FROM_HERE
, status
);
858 status
= LevelDBStatusToStatus(
859 db_
->Get(leveldb::ReadOptions(),
860 CreateUserDataKey(registration_id
, user_data_name
), &value
));
861 if (status
!= STATUS_OK
) {
862 HandleReadResult(FROM_HERE
, status
);
866 user_data
->push_back(std::make_pair(registration_id
, value
));
869 HandleReadResult(FROM_HERE
, status
);
873 ServiceWorkerDatabase::Status
874 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set
<int64
>* ids
) {
875 return ReadResourceIds(kUncommittedResIdKeyPrefix
, ids
);
878 ServiceWorkerDatabase::Status
879 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set
<int64
>& ids
) {
880 return WriteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
883 ServiceWorkerDatabase::Status
884 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set
<int64
>& ids
) {
885 return DeleteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
888 ServiceWorkerDatabase::Status
889 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set
<int64
>* ids
) {
890 return ReadResourceIds(kPurgeableResIdKeyPrefix
, ids
);
893 ServiceWorkerDatabase::Status
894 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set
<int64
>& ids
) {
895 return WriteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
898 ServiceWorkerDatabase::Status
899 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set
<int64
>& ids
) {
900 return DeleteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
903 ServiceWorkerDatabase::Status
904 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
905 const std::set
<int64
>& ids
) {
906 leveldb::WriteBatch batch
;
907 Status status
= DeleteResourceIdsInBatch(
908 kUncommittedResIdKeyPrefix
, ids
, &batch
);
909 if (status
!= STATUS_OK
)
911 status
= WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix
, ids
, &batch
);
912 if (status
!= STATUS_OK
)
914 return WriteBatch(&batch
);
917 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteAllDataForOrigins(
918 const std::set
<GURL
>& origins
,
919 std::vector
<int64
>* newly_purgeable_resources
) {
920 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
921 Status status
= LazyOpen(false);
922 if (IsNewOrNonexistentDatabase(status
))
924 if (status
!= STATUS_OK
)
926 leveldb::WriteBatch batch
;
928 for (const GURL
& origin
: origins
) {
929 if (!origin
.is_valid())
930 return STATUS_ERROR_FAILED
;
932 // Delete from the unique origin list.
933 batch
.Delete(CreateUniqueOriginKey(origin
));
935 std::vector
<RegistrationData
> registrations
;
936 status
= GetRegistrationsForOrigin(origin
, ®istrations
);
937 if (status
!= STATUS_OK
)
940 // Delete registrations, resource records and user data.
941 for (const RegistrationData
& data
: registrations
) {
942 batch
.Delete(CreateRegistrationKey(data
.registration_id
, origin
));
943 batch
.Delete(CreateRegistrationIdToOriginKey(data
.registration_id
));
945 status
= DeleteResourceRecords(
946 data
.version_id
, newly_purgeable_resources
, &batch
);
947 if (status
!= STATUS_OK
)
950 status
= DeleteUserDataForRegistration(data
.registration_id
, &batch
);
951 if (status
!= STATUS_OK
)
956 return WriteBatch(&batch
);
959 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DestroyDatabase() {
960 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
961 Disable(FROM_HERE
, STATUS_OK
);
963 leveldb::Options options
;
966 options
.env
= env_
.get();
968 // In-memory database not initialized.
974 LevelDBStatusToStatus(leveldb::DestroyDB(path_
.AsUTF8Unsafe(), options
));
975 ServiceWorkerMetrics::RecordDestroyDatabaseResult(status
);
979 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::LazyOpen(
980 bool create_if_missing
) {
981 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
983 // Do not try to open a database if we tried and failed once.
984 if (state_
== DISABLED
)
985 return STATUS_ERROR_FAILED
;
989 // When |path_| is empty, open a database in-memory.
990 bool use_in_memory_db
= path_
.empty();
992 if (!create_if_missing
) {
993 // Avoid opening a database if it does not exist at the |path_|.
994 if (use_in_memory_db
||
995 !base::PathExists(path_
) ||
996 base::IsDirectoryEmpty(path_
)) {
997 return STATUS_ERROR_NOT_FOUND
;
1001 leveldb::Options options
;
1002 options
.create_if_missing
= create_if_missing
;
1003 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
1004 if (use_in_memory_db
) {
1005 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1006 options
.env
= env_
.get();
1009 leveldb::DB
* db
= NULL
;
1010 Status status
= LevelDBStatusToStatus(
1011 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
1012 HandleOpenResult(FROM_HERE
, status
);
1013 if (status
!= STATUS_OK
) {
1015 // TODO(nhiroki): Should we retry to open the database?
1021 status
= ReadDatabaseVersion(&db_version
);
1022 if (status
!= STATUS_OK
)
1024 DCHECK_LE(0, db_version
);
1026 if (db_version
> 0 && db_version
< kCurrentSchemaVersion
) {
1027 switch (db_version
) {
1029 status
= UpgradeDatabaseSchemaFromV1ToV2();
1030 if (status
!= STATUS_OK
)
1033 // Intentionally fall-through to other version upgrade cases.
1035 // Either the database got upgraded to the current schema version, or some
1036 // upgrade step failed which would have caused this method to abort.
1037 DCHECK_EQ(db_version
, kCurrentSchemaVersion
);
1041 state_
= INITIALIZED
;
1045 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1046 ServiceWorkerDatabase::Status status
) {
1047 if (status
== STATUS_ERROR_NOT_FOUND
)
1049 if (status
== STATUS_OK
&& state_
== UNINITIALIZED
)
1054 ServiceWorkerDatabase::Status
1055 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1056 Status status
= STATUS_OK
;
1057 leveldb::WriteBatch batch
;
1059 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1060 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1061 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
1062 status
= LevelDBStatusToStatus(itr
->status());
1063 if (status
!= STATUS_OK
) {
1064 HandleReadResult(FROM_HERE
, status
);
1069 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, &key
))
1072 std::vector
<std::string
> parts
;
1073 base::SplitStringDontTrim(key
, kKeySeparator
, &parts
);
1074 if (parts
.size() != 2) {
1075 status
= STATUS_ERROR_CORRUPTED
;
1076 HandleReadResult(FROM_HERE
, status
);
1080 int64 registration_id
;
1081 status
= ParseId(parts
[1], ®istration_id
);
1082 if (status
!= STATUS_OK
) {
1083 HandleReadResult(FROM_HERE
, status
);
1087 batch
.Put(CreateRegistrationIdToOriginKey(registration_id
), parts
[0]);
1090 // Update schema version manually instead of relying on WriteBatch to make
1091 // sure each upgrade step only updates it to the actually correct version.
1092 batch
.Put(kDatabaseVersionKey
, base::Int64ToString(2));
1093 status
= LevelDBStatusToStatus(
1094 db_
->Write(leveldb::WriteOptions(), &batch
));
1095 HandleWriteResult(FROM_HERE
, status
);
1099 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadNextAvailableId(
1101 int64
* next_avail_id
) {
1103 DCHECK(next_avail_id
);
1106 Status status
= LevelDBStatusToStatus(
1107 db_
->Get(leveldb::ReadOptions(), id_key
, &value
));
1108 if (status
== STATUS_ERROR_NOT_FOUND
) {
1109 // Nobody has gotten the next resource id for |id_key|.
1111 HandleReadResult(FROM_HERE
, STATUS_OK
);
1113 } else if (status
!= STATUS_OK
) {
1114 HandleReadResult(FROM_HERE
, status
);
1118 status
= ParseId(value
, next_avail_id
);
1119 HandleReadResult(FROM_HERE
, status
);
1123 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationData(
1124 int64 registration_id
,
1126 RegistrationData
* registration
) {
1127 DCHECK(registration
);
1129 const std::string key
= CreateRegistrationKey(registration_id
, origin
);
1131 Status status
= LevelDBStatusToStatus(
1132 db_
->Get(leveldb::ReadOptions(), key
, &value
));
1133 if (status
!= STATUS_OK
) {
1136 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
1140 status
= ParseRegistrationData(value
, registration
);
1141 HandleReadResult(FROM_HERE
, status
);
1145 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceRecords(
1147 std::vector
<ResourceRecord
>* resources
) {
1148 DCHECK(resources
->empty());
1150 Status status
= STATUS_OK
;
1151 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1153 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1154 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1155 Status status
= LevelDBStatusToStatus(itr
->status());
1156 if (status
!= STATUS_OK
) {
1157 HandleReadResult(FROM_HERE
, status
);
1162 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
1165 ResourceRecord resource
;
1166 status
= ParseResourceRecord(itr
->value().ToString(), &resource
);
1167 if (status
!= STATUS_OK
) {
1168 HandleReadResult(FROM_HERE
, status
);
1172 resources
->push_back(resource
);
1175 HandleReadResult(FROM_HERE
, status
);
1179 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceRecords(
1181 std::vector
<int64
>* newly_purgeable_resources
,
1182 leveldb::WriteBatch
* batch
) {
1185 Status status
= STATUS_OK
;
1186 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1188 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1189 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1190 status
= LevelDBStatusToStatus(itr
->status());
1191 if (status
!= STATUS_OK
) {
1192 HandleReadResult(FROM_HERE
, status
);
1196 const std::string key
= itr
->key().ToString();
1197 std::string unprefixed
;
1198 if (!RemovePrefix(key
, prefix
, &unprefixed
))
1202 status
= ParseId(unprefixed
, &resource_id
);
1203 if (status
!= STATUS_OK
) {
1204 HandleReadResult(FROM_HERE
, status
);
1208 // Remove a resource record.
1211 // Currently resource sharing across versions and registrations is not
1212 // supported, so we can purge this without caring about it.
1213 PutPurgeableResourceIdToBatch(resource_id
, batch
);
1214 newly_purgeable_resources
->push_back(resource_id
);
1217 HandleReadResult(FROM_HERE
, status
);
1221 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceIds(
1222 const char* id_key_prefix
,
1223 std::set
<int64
>* ids
) {
1224 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1225 DCHECK(id_key_prefix
);
1226 DCHECK(ids
->empty());
1228 Status status
= LazyOpen(false);
1229 if (IsNewOrNonexistentDatabase(status
))
1231 if (status
!= STATUS_OK
)
1234 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1235 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
1236 status
= LevelDBStatusToStatus(itr
->status());
1237 if (status
!= STATUS_OK
) {
1238 HandleReadResult(FROM_HERE
, status
);
1243 std::string unprefixed
;
1244 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
1248 status
= ParseId(unprefixed
, &resource_id
);
1249 if (status
!= STATUS_OK
) {
1250 HandleReadResult(FROM_HERE
, status
);
1254 ids
->insert(resource_id
);
1257 HandleReadResult(FROM_HERE
, status
);
1261 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIds(
1262 const char* id_key_prefix
,
1263 const std::set
<int64
>& ids
) {
1264 leveldb::WriteBatch batch
;
1265 Status status
= WriteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1266 if (status
!= STATUS_OK
)
1268 return WriteBatch(&batch
);
1271 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIdsInBatch(
1272 const char* id_key_prefix
,
1273 const std::set
<int64
>& ids
,
1274 leveldb::WriteBatch
* batch
) {
1275 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1276 DCHECK(id_key_prefix
);
1278 Status status
= LazyOpen(true);
1279 if (status
!= STATUS_OK
)
1284 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1285 itr
!= ids
.end(); ++itr
) {
1286 // Value should be empty.
1287 batch
->Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
1289 // std::set is sorted, so the last element is the largest.
1290 BumpNextResourceIdIfNeeded(*ids
.rbegin(), batch
);
1294 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIds(
1295 const char* id_key_prefix
,
1296 const std::set
<int64
>& ids
) {
1297 leveldb::WriteBatch batch
;
1298 Status status
= DeleteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1299 if (status
!= STATUS_OK
)
1301 return WriteBatch(&batch
);
1304 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1305 const char* id_key_prefix
,
1306 const std::set
<int64
>& ids
,
1307 leveldb::WriteBatch
* batch
) {
1308 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1309 DCHECK(id_key_prefix
);
1311 Status status
= LazyOpen(false);
1312 if (IsNewOrNonexistentDatabase(status
))
1314 if (status
!= STATUS_OK
)
1317 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1318 itr
!= ids
.end(); ++itr
) {
1319 batch
->Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
1324 ServiceWorkerDatabase::Status
1325 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1326 int64 registration_id
,
1327 leveldb::WriteBatch
* batch
) {
1329 Status status
= STATUS_OK
;
1330 const std::string prefix
= CreateUserDataKeyPrefix(registration_id
);
1332 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1333 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1334 status
= LevelDBStatusToStatus(itr
->status());
1335 if (status
!= STATUS_OK
) {
1336 HandleReadResult(FROM_HERE
, status
);
1340 const std::string key
= itr
->key().ToString();
1341 std::string user_data_name
;
1342 if (!RemovePrefix(key
, prefix
, &user_data_name
))
1345 batch
->Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
1350 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadDatabaseVersion(
1351 int64
* db_version
) {
1353 Status status
= LevelDBStatusToStatus(
1354 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
));
1355 if (status
== STATUS_ERROR_NOT_FOUND
) {
1356 // The database hasn't been initialized yet.
1358 HandleReadResult(FROM_HERE
, STATUS_OK
);
1362 if (status
!= STATUS_OK
) {
1363 HandleReadResult(FROM_HERE
, status
);
1367 status
= ParseDatabaseVersion(value
, db_version
);
1368 HandleReadResult(FROM_HERE
, status
);
1372 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteBatch(
1373 leveldb::WriteBatch
* batch
) {
1375 DCHECK_NE(DISABLED
, state_
);
1377 if (state_
== UNINITIALIZED
) {
1378 // Write the database schema version.
1379 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
1380 state_
= INITIALIZED
;
1383 Status status
= LevelDBStatusToStatus(
1384 db_
->Write(leveldb::WriteOptions(), batch
));
1385 HandleWriteResult(FROM_HERE
, status
);
1389 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1390 int64 used_id
, leveldb::WriteBatch
* batch
) {
1392 if (next_avail_registration_id_
<= used_id
) {
1393 next_avail_registration_id_
= used_id
+ 1;
1394 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
1398 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1399 int64 used_id
, leveldb::WriteBatch
* batch
) {
1401 if (next_avail_resource_id_
<= used_id
) {
1402 next_avail_resource_id_
= used_id
+ 1;
1403 batch
->Put(kNextResIdKey
, base::Int64ToString(next_avail_resource_id_
));
1407 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1408 int64 used_id
, leveldb::WriteBatch
* batch
) {
1410 if (next_avail_version_id_
<= used_id
) {
1411 next_avail_version_id_
= used_id
+ 1;
1412 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
1416 bool ServiceWorkerDatabase::IsOpen() {
1420 void ServiceWorkerDatabase::Disable(
1421 const tracked_objects::Location
& from_here
,
1423 if (status
!= STATUS_OK
) {
1424 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
1425 << " with error: " << StatusToString(status
);
1426 DLOG(ERROR
) << "ServiceWorkerDatabase is disabled.";
1432 void ServiceWorkerDatabase::HandleOpenResult(
1433 const tracked_objects::Location
& from_here
,
1435 if (status
!= STATUS_OK
)
1436 Disable(from_here
, status
);
1437 ServiceWorkerMetrics::CountOpenDatabaseResult(status
);
1440 void ServiceWorkerDatabase::HandleReadResult(
1441 const tracked_objects::Location
& from_here
,
1443 if (status
!= STATUS_OK
)
1444 Disable(from_here
, status
);
1445 ServiceWorkerMetrics::CountReadDatabaseResult(status
);
1448 void ServiceWorkerDatabase::HandleWriteResult(
1449 const tracked_objects::Location
& from_here
,
1451 if (status
!= STATUS_OK
)
1452 Disable(from_here
, status
);
1453 ServiceWorkerMetrics::CountWriteDatabaseResult(status
);
1456 } // namespace content