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/src/helpers/memenv/memenv.h"
20 #include "third_party/leveldatabase/src/include/leveldb/db.h"
21 #include "third_party/leveldatabase/src/include/leveldb/env.h"
22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
24 // LevelDB database schema
25 // =======================
28 // - int64 value is serialized as a string by base::Int64ToString().
29 // - GURL value is serialized as a string by GURL::spec().
31 // Version 1 (in sorted order)
32 // key: "INITDATA_DB_VERSION"
35 // key: "INITDATA_NEXT_REGISTRATION_ID"
36 // value: <int64 'next_available_registration_id'>
38 // key: "INITDATA_NEXT_RESOURCE_ID"
39 // value: <int64 'next_available_resource_id'>
41 // key: "INITDATA_NEXT_VERSION_ID"
42 // value: <int64 'next_available_version_id'>
44 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
47 // key: "PRES:" + <int64 'purgeable_resource_id'>
50 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
51 // (ex. "REG:http://example.com\x00123456")
52 // value: <ServiceWorkerRegistrationData serialized as a string>
54 // key: "REG_HAS_USER_DATA:" + <std::string 'user_data_name'> + '\x00'
55 // + <int64 'registration_id'>
58 // key: "REG_USER_DATA:" + <int64 'registration_id'> + '\x00'
59 // + <std::string user_data_name>
60 // (ex. "REG_USER_DATA:123456\x00foo_bar")
61 // value: <std::string user_data>
63 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
64 // (ex. "RES:123456\x00654321")
65 // value: <ServiceWorkerResourceRecord serialized as a string>
67 // key: "URES:" + <int64 'uncommitted_resource_id'>
72 // key: "REGID_TO_ORIGIN:" + <int64 'registration_id'>
73 // value: <GURL 'origin'>
78 const char kDatabaseVersionKey
[] = "INITDATA_DB_VERSION";
79 const char kNextRegIdKey
[] = "INITDATA_NEXT_REGISTRATION_ID";
80 const char kNextResIdKey
[] = "INITDATA_NEXT_RESOURCE_ID";
81 const char kNextVerIdKey
[] = "INITDATA_NEXT_VERSION_ID";
82 const char kUniqueOriginKey
[] = "INITDATA_UNIQUE_ORIGIN:";
84 const char kRegKeyPrefix
[] = "REG:";
85 const char kRegUserDataKeyPrefix
[] = "REG_USER_DATA:";
86 const char kRegHasUserDataKeyPrefix
[] = "REG_HAS_USER_DATA:";
87 const char kRegIdToOriginKeyPrefix
[] = "REGID_TO_ORIGIN:";
88 const char kResKeyPrefix
[] = "RES:";
89 const char kKeySeparator
= '\x00';
91 const char kUncommittedResIdKeyPrefix
[] = "URES:";
92 const char kPurgeableResIdKeyPrefix
[] = "PRES:";
94 const int64 kCurrentSchemaVersion
= 2;
96 bool RemovePrefix(const std::string
& str
,
97 const std::string
& prefix
,
99 if (!StartsWithASCII(str
, prefix
, true))
102 *out
= str
.substr(prefix
.size());
106 std::string
CreateRegistrationKey(int64 registration_id
,
107 const GURL
& origin
) {
108 return base::StringPrintf("%s%s%c%s",
110 origin
.spec().c_str(),
112 base::Int64ToString(registration_id
).c_str());
115 std::string
CreateResourceRecordKeyPrefix(int64 version_id
) {
116 return base::StringPrintf("%s%s%c",
118 base::Int64ToString(version_id
).c_str(),
122 std::string
CreateResourceRecordKey(int64 version_id
,
124 return CreateResourceRecordKeyPrefix(version_id
).append(
125 base::Int64ToString(resource_id
));
128 std::string
CreateUniqueOriginKey(const GURL
& origin
) {
129 return base::StringPrintf("%s%s", kUniqueOriginKey
, origin
.spec().c_str());
132 std::string
CreateResourceIdKey(const char* key_prefix
, int64 resource_id
) {
133 return base::StringPrintf(
134 "%s%s", key_prefix
, base::Int64ToString(resource_id
).c_str());
137 std::string
CreateUserDataKeyPrefix(int64 registration_id
) {
138 return base::StringPrintf("%s%s%c",
139 kRegUserDataKeyPrefix
,
140 base::Int64ToString(registration_id
).c_str(),
144 std::string
CreateUserDataKey(int64 registration_id
,
145 const std::string
& user_data_name
) {
146 return CreateUserDataKeyPrefix(registration_id
).append(user_data_name
);
149 std::string
CreateHasUserDataKeyPrefix(const std::string
& user_data_name
) {
150 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix
,
151 user_data_name
.c_str(), kKeySeparator
);
154 std::string
CreateHasUserDataKey(int64 registration_id
,
155 const std::string
& user_data_name
) {
156 return CreateHasUserDataKeyPrefix(user_data_name
)
157 .append(base::Int64ToString(registration_id
));
160 std::string
CreateRegistrationIdToOriginKey(int64 registration_id
) {
161 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix
,
162 base::Int64ToString(registration_id
).c_str());
165 void PutRegistrationDataToBatch(
166 const ServiceWorkerDatabase::RegistrationData
& input
,
167 leveldb::WriteBatch
* batch
) {
170 // Convert RegistrationData to ServiceWorkerRegistrationData.
171 ServiceWorkerRegistrationData data
;
172 data
.set_registration_id(input
.registration_id
);
173 data
.set_scope_url(input
.scope
.spec());
174 data
.set_script_url(input
.script
.spec());
175 data
.set_version_id(input
.version_id
);
176 data
.set_is_active(input
.is_active
);
177 data
.set_has_fetch_handler(input
.has_fetch_handler
);
178 data
.set_last_update_check_time(input
.last_update_check
.ToInternalValue());
179 data
.set_resources_total_size_bytes(input
.resources_total_size_bytes
);
182 bool success
= data
.SerializeToString(&value
);
184 GURL origin
= input
.scope
.GetOrigin();
185 batch
->Put(CreateRegistrationKey(data
.registration_id(), origin
), value
);
188 void PutResourceRecordToBatch(
189 const ServiceWorkerDatabase::ResourceRecord
& input
,
191 leveldb::WriteBatch
* batch
) {
193 DCHECK_GE(input
.size_bytes
, 0);
195 // Convert ResourceRecord to ServiceWorkerResourceRecord.
196 ServiceWorkerResourceRecord record
;
197 record
.set_resource_id(input
.resource_id
);
198 record
.set_url(input
.url
.spec());
199 record
.set_size_bytes(input
.size_bytes
);
202 bool success
= record
.SerializeToString(&value
);
204 batch
->Put(CreateResourceRecordKey(version_id
, input
.resource_id
), value
);
207 void PutUniqueOriginToBatch(const GURL
& origin
,
208 leveldb::WriteBatch
* batch
) {
209 // Value should be empty.
210 batch
->Put(CreateUniqueOriginKey(origin
), "");
213 void PutPurgeableResourceIdToBatch(int64 resource_id
,
214 leveldb::WriteBatch
* batch
) {
215 // Value should be empty.
216 batch
->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix
, resource_id
), "");
219 ServiceWorkerDatabase::Status
ParseId(
220 const std::string
& serialized
,
224 if (!base::StringToInt64(serialized
, &id
) || id
< 0)
225 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
227 return ServiceWorkerDatabase::STATUS_OK
;
230 ServiceWorkerDatabase::Status
ParseDatabaseVersion(
231 const std::string
& serialized
,
234 const int kFirstValidVersion
= 1;
236 if (!base::StringToInt64(serialized
, &version
) ||
237 version
< kFirstValidVersion
) {
238 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
240 if (kCurrentSchemaVersion
< version
) {
241 DLOG(ERROR
) << "ServiceWorkerDatabase has newer schema version"
242 << " than the current latest version: "
243 << version
<< " vs " << kCurrentSchemaVersion
;
244 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
247 return ServiceWorkerDatabase::STATUS_OK
;
250 ServiceWorkerDatabase::Status
ParseRegistrationData(
251 const std::string
& serialized
,
252 ServiceWorkerDatabase::RegistrationData
* out
) {
254 ServiceWorkerRegistrationData data
;
255 if (!data
.ParseFromString(serialized
))
256 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
258 GURL
scope_url(data
.scope_url());
259 GURL
script_url(data
.script_url());
260 if (!scope_url
.is_valid() ||
261 !script_url
.is_valid() ||
262 scope_url
.GetOrigin() != script_url
.GetOrigin()) {
263 DLOG(ERROR
) << "Scope URL '" << data
.scope_url() << "' and/or script url '"
265 << "' are invalid or have mismatching origins.";
266 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
269 // Convert ServiceWorkerRegistrationData to RegistrationData.
270 out
->registration_id
= data
.registration_id();
271 out
->scope
= scope_url
;
272 out
->script
= script_url
;
273 out
->version_id
= data
.version_id();
274 out
->is_active
= data
.is_active();
275 out
->has_fetch_handler
= data
.has_fetch_handler();
276 out
->last_update_check
=
277 base::Time::FromInternalValue(data
.last_update_check_time());
278 out
->resources_total_size_bytes
= data
.resources_total_size_bytes();
280 return ServiceWorkerDatabase::STATUS_OK
;
283 ServiceWorkerDatabase::Status
ParseResourceRecord(
284 const std::string
& serialized
,
285 ServiceWorkerDatabase::ResourceRecord
* out
) {
287 ServiceWorkerResourceRecord record
;
288 if (!record
.ParseFromString(serialized
))
289 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
291 GURL
url(record
.url());
293 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
295 // Convert ServiceWorkerResourceRecord to ResourceRecord.
296 out
->resource_id
= record
.resource_id();
298 out
->size_bytes
= record
.size_bytes();
299 return ServiceWorkerDatabase::STATUS_OK
;
302 ServiceWorkerDatabase::Status
LevelDBStatusToStatus(
303 const leveldb::Status
& status
) {
305 return ServiceWorkerDatabase::STATUS_OK
;
306 else if (status
.IsNotFound())
307 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
;
308 else if (status
.IsIOError())
309 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
;
310 else if (status
.IsCorruption())
311 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
;
313 return ServiceWorkerDatabase::STATUS_ERROR_FAILED
;
318 const char* ServiceWorkerDatabase::StatusToString(
319 ServiceWorkerDatabase::Status status
) {
321 case ServiceWorkerDatabase::STATUS_OK
:
322 return "Database OK";
323 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND
:
324 return "Database not found";
325 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR
:
326 return "Database IO error";
327 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED
:
328 return "Database corrupted";
329 case ServiceWorkerDatabase::STATUS_ERROR_FAILED
:
330 return "Database operation failed";
331 case ServiceWorkerDatabase::STATUS_ERROR_MAX
:
333 return "Database unknown error";
336 return "Database unknown error";
339 ServiceWorkerDatabase::RegistrationData::RegistrationData()
340 : registration_id(kInvalidServiceWorkerRegistrationId
),
341 version_id(kInvalidServiceWorkerVersionId
),
343 has_fetch_handler(false),
344 resources_total_size_bytes(0) {
347 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
350 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath
& path
)
352 next_avail_registration_id_(0),
353 next_avail_resource_id_(0),
354 next_avail_version_id_(0),
355 state_(UNINITIALIZED
) {
356 sequence_checker_
.DetachFromSequence();
359 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
360 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
364 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetNextAvailableIds(
365 int64
* next_avail_registration_id
,
366 int64
* next_avail_version_id
,
367 int64
* next_avail_resource_id
) {
368 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
369 DCHECK(next_avail_registration_id
);
370 DCHECK(next_avail_version_id
);
371 DCHECK(next_avail_resource_id
);
373 Status status
= LazyOpen(false);
374 if (IsNewOrNonexistentDatabase(status
)) {
375 *next_avail_registration_id
= 0;
376 *next_avail_version_id
= 0;
377 *next_avail_resource_id
= 0;
380 if (status
!= STATUS_OK
)
383 status
= ReadNextAvailableId(kNextRegIdKey
, &next_avail_registration_id_
);
384 if (status
!= STATUS_OK
)
386 status
= ReadNextAvailableId(kNextVerIdKey
, &next_avail_version_id_
);
387 if (status
!= STATUS_OK
)
389 status
= ReadNextAvailableId(kNextResIdKey
, &next_avail_resource_id_
);
390 if (status
!= STATUS_OK
)
393 *next_avail_registration_id
= next_avail_registration_id_
;
394 *next_avail_version_id
= next_avail_version_id_
;
395 *next_avail_resource_id
= next_avail_resource_id_
;
399 ServiceWorkerDatabase::Status
400 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set
<GURL
>* origins
) {
401 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
402 DCHECK(origins
->empty());
404 Status status
= LazyOpen(false);
405 if (IsNewOrNonexistentDatabase(status
))
407 if (status
!= STATUS_OK
)
410 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
411 for (itr
->Seek(kUniqueOriginKey
); itr
->Valid(); itr
->Next()) {
412 status
= LevelDBStatusToStatus(itr
->status());
413 if (status
!= STATUS_OK
) {
414 HandleReadResult(FROM_HERE
, status
);
420 if (!RemovePrefix(itr
->key().ToString(), kUniqueOriginKey
, &origin
))
422 origins
->insert(GURL(origin
));
425 HandleReadResult(FROM_HERE
, status
);
429 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetRegistrationsForOrigin(
431 std::vector
<RegistrationData
>* registrations
) {
432 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
433 DCHECK(registrations
->empty());
435 Status status
= LazyOpen(false);
436 if (IsNewOrNonexistentDatabase(status
))
438 if (status
!= STATUS_OK
)
441 // Create a key prefix for registrations.
442 std::string prefix
= base::StringPrintf(
443 "%s%s%c", kRegKeyPrefix
, origin
.spec().c_str(), kKeySeparator
);
445 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
446 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
447 status
= LevelDBStatusToStatus(itr
->status());
448 if (status
!= STATUS_OK
) {
449 HandleReadResult(FROM_HERE
, status
);
450 registrations
->clear();
454 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
457 RegistrationData registration
;
458 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
459 if (status
!= STATUS_OK
) {
460 HandleReadResult(FROM_HERE
, status
);
461 registrations
->clear();
464 registrations
->push_back(registration
);
467 HandleReadResult(FROM_HERE
, status
);
471 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::GetAllRegistrations(
472 std::vector
<RegistrationData
>* registrations
) {
473 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
474 DCHECK(registrations
->empty());
476 Status status
= LazyOpen(false);
477 if (IsNewOrNonexistentDatabase(status
))
479 if (status
!= STATUS_OK
)
482 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
483 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
484 status
= LevelDBStatusToStatus(itr
->status());
485 if (status
!= STATUS_OK
) {
486 HandleReadResult(FROM_HERE
, status
);
487 registrations
->clear();
491 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, NULL
))
494 RegistrationData registration
;
495 status
= ParseRegistrationData(itr
->value().ToString(), ®istration
);
496 if (status
!= STATUS_OK
) {
497 HandleReadResult(FROM_HERE
, status
);
498 registrations
->clear();
501 registrations
->push_back(registration
);
504 HandleReadResult(FROM_HERE
, status
);
508 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistration(
509 int64 registration_id
,
511 RegistrationData
* registration
,
512 std::vector
<ResourceRecord
>* resources
) {
513 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
514 DCHECK(registration
);
517 Status status
= LazyOpen(false);
518 if (IsNewOrNonexistentDatabase(status
))
519 return STATUS_ERROR_NOT_FOUND
;
520 if (status
!= STATUS_OK
)
523 RegistrationData value
;
524 status
= ReadRegistrationData(registration_id
, origin
, &value
);
525 if (status
!= STATUS_OK
)
528 status
= ReadResourceRecords(value
.version_id
, resources
);
529 if (status
!= STATUS_OK
)
532 *registration
= value
;
536 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationOrigin(
537 int64 registration_id
,
539 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
542 Status status
= LazyOpen(true);
543 if (IsNewOrNonexistentDatabase(status
))
544 return STATUS_ERROR_NOT_FOUND
;
545 if (status
!= STATUS_OK
)
549 status
= LevelDBStatusToStatus(
550 db_
->Get(leveldb::ReadOptions(),
551 CreateRegistrationIdToOriginKey(registration_id
), &value
));
552 if (status
!= STATUS_OK
) {
553 HandleReadResult(FROM_HERE
,
554 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
559 if (!parsed
.is_valid()) {
560 status
= STATUS_ERROR_CORRUPTED
;
561 HandleReadResult(FROM_HERE
, status
);
566 HandleReadResult(FROM_HERE
, STATUS_OK
);
570 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteRegistration(
571 const RegistrationData
& registration
,
572 const std::vector
<ResourceRecord
>& resources
,
573 RegistrationData
* old_registration
,
574 std::vector
<int64
>* newly_purgeable_resources
) {
575 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
576 DCHECK(old_registration
);
577 Status status
= LazyOpen(true);
578 old_registration
->version_id
= kInvalidServiceWorkerVersionId
;
579 if (status
!= STATUS_OK
)
582 leveldb::WriteBatch batch
;
583 BumpNextRegistrationIdIfNeeded(registration
.registration_id
, &batch
);
584 BumpNextVersionIdIfNeeded(registration
.version_id
, &batch
);
586 PutUniqueOriginToBatch(registration
.scope
.GetOrigin(), &batch
);
588 int64_t total_size_bytes
= 0;
589 for (const auto& resource
: resources
) {
590 total_size_bytes
+= resource
.size_bytes
;
592 DCHECK_EQ(total_size_bytes
, registration
.resources_total_size_bytes
)
593 << "The total size in the registration must match the cumulative "
594 << "sizes of the resources.";
596 PutRegistrationDataToBatch(registration
, &batch
);
597 batch
.Put(CreateRegistrationIdToOriginKey(registration
.registration_id
),
598 registration
.scope
.GetOrigin().spec());
600 // Used for avoiding multiple writes for the same resource id or url.
601 std::set
<int64
> pushed_resources
;
602 std::set
<GURL
> pushed_urls
;
603 for (std::vector
<ResourceRecord
>::const_iterator itr
= resources
.begin();
604 itr
!= resources
.end(); ++itr
) {
605 if (!itr
->url
.is_valid())
606 return STATUS_ERROR_FAILED
;
608 // Duplicated resource id or url should not exist.
609 DCHECK(pushed_resources
.insert(itr
->resource_id
).second
);
610 DCHECK(pushed_urls
.insert(itr
->url
).second
);
612 PutResourceRecordToBatch(*itr
, registration
.version_id
, &batch
);
614 // Delete a resource from the uncommitted list.
615 batch
.Delete(CreateResourceIdKey(
616 kUncommittedResIdKeyPrefix
, itr
->resource_id
));
617 // Delete from the purgeable list in case this version was once deleted.
619 CreateResourceIdKey(kPurgeableResIdKeyPrefix
, itr
->resource_id
));
622 // Retrieve a previous version to sweep purgeable resources.
623 status
= ReadRegistrationData(registration
.registration_id
,
624 registration
.scope
.GetOrigin(),
626 if (status
!= STATUS_OK
&& status
!= STATUS_ERROR_NOT_FOUND
)
628 if (status
== STATUS_OK
) {
629 DCHECK_LT(old_registration
->version_id
, registration
.version_id
);
630 status
= DeleteResourceRecords(
631 old_registration
->version_id
, newly_purgeable_resources
, &batch
);
632 if (status
!= STATUS_OK
)
635 // Currently resource sharing across versions and registrations is not
636 // supported, so resource ids should not be overlapped between
637 // |registration| and |old_registration|.
638 std::set
<int64
> deleted_resources(newly_purgeable_resources
->begin(),
639 newly_purgeable_resources
->end());
640 DCHECK(base::STLSetIntersection
<std::set
<int64
> >(
641 pushed_resources
, deleted_resources
).empty());
644 return WriteBatch(&batch
);
647 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateVersionToActive(
648 int64 registration_id
,
649 const GURL
& origin
) {
650 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
651 Status status
= LazyOpen(false);
652 if (IsNewOrNonexistentDatabase(status
))
653 return STATUS_ERROR_NOT_FOUND
;
654 if (status
!= STATUS_OK
)
656 if (!origin
.is_valid())
657 return STATUS_ERROR_FAILED
;
659 RegistrationData registration
;
660 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
661 if (status
!= STATUS_OK
)
664 registration
.is_active
= true;
666 leveldb::WriteBatch batch
;
667 PutRegistrationDataToBatch(registration
, &batch
);
668 return WriteBatch(&batch
);
671 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::UpdateLastCheckTime(
672 int64 registration_id
,
674 const base::Time
& time
) {
675 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
676 Status status
= LazyOpen(false);
677 if (IsNewOrNonexistentDatabase(status
))
678 return STATUS_ERROR_NOT_FOUND
;
679 if (status
!= STATUS_OK
)
681 if (!origin
.is_valid())
682 return STATUS_ERROR_FAILED
;
684 RegistrationData registration
;
685 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
686 if (status
!= STATUS_OK
)
689 registration
.last_update_check
= time
;
691 leveldb::WriteBatch batch
;
692 PutRegistrationDataToBatch(registration
, &batch
);
693 return WriteBatch(&batch
);
696 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteRegistration(
697 int64 registration_id
,
699 RegistrationData
* deleted_version
,
700 std::vector
<int64
>* newly_purgeable_resources
) {
701 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
702 DCHECK(deleted_version
);
703 deleted_version
->version_id
= kInvalidServiceWorkerVersionId
;
704 Status status
= LazyOpen(false);
705 if (IsNewOrNonexistentDatabase(status
))
707 if (status
!= STATUS_OK
)
709 if (!origin
.is_valid())
710 return STATUS_ERROR_FAILED
;
712 leveldb::WriteBatch batch
;
714 // Remove |origin| from unique origins if a registration specified by
715 // |registration_id| is the only one for |origin|.
716 // TODO(nhiroki): Check the uniqueness by more efficient way.
717 std::vector
<RegistrationData
> registrations
;
718 status
= GetRegistrationsForOrigin(origin
, ®istrations
);
719 if (status
!= STATUS_OK
)
722 if (registrations
.size() == 1 &&
723 registrations
[0].registration_id
== registration_id
) {
724 batch
.Delete(CreateUniqueOriginKey(origin
));
727 // Delete a registration specified by |registration_id|.
728 batch
.Delete(CreateRegistrationKey(registration_id
, origin
));
729 batch
.Delete(CreateRegistrationIdToOriginKey(registration_id
));
731 // Delete resource records and user data associated with the registration.
732 for (const auto& registration
: registrations
) {
733 if (registration
.registration_id
== registration_id
) {
734 *deleted_version
= registration
;
735 status
= DeleteResourceRecords(
736 registration
.version_id
, newly_purgeable_resources
, &batch
);
737 if (status
!= STATUS_OK
)
740 status
= DeleteUserDataForRegistration(registration_id
, &batch
);
741 if (status
!= STATUS_OK
)
747 return WriteBatch(&batch
);
750 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadUserData(
751 int64 registration_id
,
752 const std::string
& user_data_name
,
753 std::string
* user_data
) {
754 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
755 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
756 DCHECK(!user_data_name
.empty());
759 Status status
= LazyOpen(false);
760 if (IsNewOrNonexistentDatabase(status
))
761 return STATUS_ERROR_NOT_FOUND
;
762 if (status
!= STATUS_OK
)
765 const std::string key
= CreateUserDataKey(registration_id
, user_data_name
);
766 status
= LevelDBStatusToStatus(
767 db_
->Get(leveldb::ReadOptions(), key
, user_data
));
768 HandleReadResult(FROM_HERE
,
769 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
773 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteUserData(
774 int64 registration_id
,
776 const std::string
& user_data_name
,
777 const std::string
& user_data
) {
778 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
779 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
780 DCHECK(!user_data_name
.empty());
782 Status status
= LazyOpen(false);
783 if (IsNewOrNonexistentDatabase(status
))
784 return STATUS_ERROR_NOT_FOUND
;
785 if (status
!= STATUS_OK
)
788 // There should be the registration specified by |registration_id|.
789 RegistrationData registration
;
790 status
= ReadRegistrationData(registration_id
, origin
, ®istration
);
791 if (status
!= STATUS_OK
)
794 leveldb::WriteBatch batch
;
795 batch
.Put(CreateUserDataKey(registration_id
, user_data_name
), user_data
);
796 batch
.Put(CreateHasUserDataKey(registration_id
, user_data_name
), "");
797 return WriteBatch(&batch
);
800 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteUserData(
801 int64 registration_id
,
802 const std::string
& user_data_name
) {
803 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
804 DCHECK_NE(kInvalidServiceWorkerRegistrationId
, registration_id
);
805 DCHECK(!user_data_name
.empty());
807 Status status
= LazyOpen(false);
808 if (IsNewOrNonexistentDatabase(status
))
810 if (status
!= STATUS_OK
)
813 leveldb::WriteBatch batch
;
814 batch
.Delete(CreateUserDataKey(registration_id
, user_data_name
));
815 batch
.Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
816 return WriteBatch(&batch
);
819 ServiceWorkerDatabase::Status
820 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
821 const std::string
& user_data_name
,
822 std::vector
<std::pair
<int64
, std::string
>>* user_data
) {
823 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
824 DCHECK(user_data
->empty());
826 Status status
= LazyOpen(false);
827 if (IsNewOrNonexistentDatabase(status
))
829 if (status
!= STATUS_OK
)
832 std::string key_prefix
= CreateHasUserDataKeyPrefix(user_data_name
);
833 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
834 for (itr
->Seek(key_prefix
); itr
->Valid(); itr
->Next()) {
835 status
= LevelDBStatusToStatus(itr
->status());
836 if (status
!= STATUS_OK
) {
837 HandleReadResult(FROM_HERE
, status
);
842 std::string registration_id_string
;
843 if (!RemovePrefix(itr
->key().ToString(), key_prefix
,
844 ®istration_id_string
)) {
848 int64 registration_id
;
849 status
= ParseId(registration_id_string
, ®istration_id
);
850 if (status
!= STATUS_OK
) {
851 HandleReadResult(FROM_HERE
, status
);
857 status
= LevelDBStatusToStatus(
858 db_
->Get(leveldb::ReadOptions(),
859 CreateUserDataKey(registration_id
, user_data_name
), &value
));
860 if (status
!= STATUS_OK
) {
861 HandleReadResult(FROM_HERE
, status
);
865 user_data
->push_back(std::make_pair(registration_id
, value
));
868 HandleReadResult(FROM_HERE
, status
);
872 ServiceWorkerDatabase::Status
873 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set
<int64
>* ids
) {
874 return ReadResourceIds(kUncommittedResIdKeyPrefix
, ids
);
877 ServiceWorkerDatabase::Status
878 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set
<int64
>& ids
) {
879 return WriteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
882 ServiceWorkerDatabase::Status
883 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set
<int64
>& ids
) {
884 return DeleteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
887 ServiceWorkerDatabase::Status
888 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set
<int64
>* ids
) {
889 return ReadResourceIds(kPurgeableResIdKeyPrefix
, ids
);
892 ServiceWorkerDatabase::Status
893 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set
<int64
>& ids
) {
894 return WriteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
897 ServiceWorkerDatabase::Status
898 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set
<int64
>& ids
) {
899 return DeleteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
902 ServiceWorkerDatabase::Status
903 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
904 const std::set
<int64
>& ids
) {
905 leveldb::WriteBatch batch
;
906 Status status
= DeleteResourceIdsInBatch(
907 kUncommittedResIdKeyPrefix
, ids
, &batch
);
908 if (status
!= STATUS_OK
)
910 status
= WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix
, ids
, &batch
);
911 if (status
!= STATUS_OK
)
913 return WriteBatch(&batch
);
916 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteAllDataForOrigins(
917 const std::set
<GURL
>& origins
,
918 std::vector
<int64
>* newly_purgeable_resources
) {
919 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
920 Status status
= LazyOpen(false);
921 if (IsNewOrNonexistentDatabase(status
))
923 if (status
!= STATUS_OK
)
925 leveldb::WriteBatch batch
;
927 for (const GURL
& origin
: origins
) {
928 if (!origin
.is_valid())
929 return STATUS_ERROR_FAILED
;
931 // Delete from the unique origin list.
932 batch
.Delete(CreateUniqueOriginKey(origin
));
934 std::vector
<RegistrationData
> registrations
;
935 status
= GetRegistrationsForOrigin(origin
, ®istrations
);
936 if (status
!= STATUS_OK
)
939 // Delete registrations, resource records and user data.
940 for (const RegistrationData
& data
: registrations
) {
941 batch
.Delete(CreateRegistrationKey(data
.registration_id
, origin
));
942 batch
.Delete(CreateRegistrationIdToOriginKey(data
.registration_id
));
944 status
= DeleteResourceRecords(
945 data
.version_id
, newly_purgeable_resources
, &batch
);
946 if (status
!= STATUS_OK
)
949 status
= DeleteUserDataForRegistration(data
.registration_id
, &batch
);
950 if (status
!= STATUS_OK
)
955 return WriteBatch(&batch
);
958 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DestroyDatabase() {
959 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
960 Disable(FROM_HERE
, STATUS_OK
);
962 leveldb::Options options
;
965 options
.env
= env_
.get();
967 // In-memory database not initialized.
972 return LevelDBStatusToStatus(
973 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), options
));
976 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::LazyOpen(
977 bool create_if_missing
) {
978 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
980 // Do not try to open a database if we tried and failed once.
981 if (state_
== DISABLED
)
982 return STATUS_ERROR_FAILED
;
986 // When |path_| is empty, open a database in-memory.
987 bool use_in_memory_db
= path_
.empty();
989 if (!create_if_missing
) {
990 // Avoid opening a database if it does not exist at the |path_|.
991 if (use_in_memory_db
||
992 !base::PathExists(path_
) ||
993 base::IsDirectoryEmpty(path_
)) {
994 return STATUS_ERROR_NOT_FOUND
;
998 leveldb::Options options
;
999 options
.create_if_missing
= create_if_missing
;
1000 if (use_in_memory_db
) {
1001 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1002 options
.env
= env_
.get();
1005 leveldb::DB
* db
= NULL
;
1006 Status status
= LevelDBStatusToStatus(
1007 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
1008 HandleOpenResult(FROM_HERE
, status
);
1009 if (status
!= STATUS_OK
) {
1011 // TODO(nhiroki): Should we retry to open the database?
1017 status
= ReadDatabaseVersion(&db_version
);
1018 if (status
!= STATUS_OK
)
1020 DCHECK_LE(0, db_version
);
1022 if (db_version
> 0 && db_version
< kCurrentSchemaVersion
) {
1023 switch (db_version
) {
1025 status
= UpgradeDatabaseSchemaFromV1ToV2();
1026 if (status
!= STATUS_OK
)
1029 // Intentionally fall-through to other version upgrade cases.
1031 // Either the database got upgraded to the current schema version, or some
1032 // upgrade step failed which would have caused this method to abort.
1033 DCHECK_EQ(db_version
, kCurrentSchemaVersion
);
1037 state_
= INITIALIZED
;
1041 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1042 ServiceWorkerDatabase::Status status
) {
1043 if (status
== STATUS_ERROR_NOT_FOUND
)
1045 if (status
== STATUS_OK
&& state_
== UNINITIALIZED
)
1050 ServiceWorkerDatabase::Status
1051 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1052 Status status
= STATUS_OK
;
1053 leveldb::WriteBatch batch
;
1055 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1056 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1057 for (itr
->Seek(kRegKeyPrefix
); itr
->Valid(); itr
->Next()) {
1058 status
= LevelDBStatusToStatus(itr
->status());
1059 if (status
!= STATUS_OK
) {
1060 HandleReadResult(FROM_HERE
, status
);
1065 if (!RemovePrefix(itr
->key().ToString(), kRegKeyPrefix
, &key
))
1068 std::vector
<std::string
> parts
;
1069 base::SplitStringDontTrim(key
, kKeySeparator
, &parts
);
1070 if (parts
.size() != 2) {
1071 status
= STATUS_ERROR_CORRUPTED
;
1072 HandleReadResult(FROM_HERE
, status
);
1076 int64 registration_id
;
1077 status
= ParseId(parts
[1], ®istration_id
);
1078 if (status
!= STATUS_OK
) {
1079 HandleReadResult(FROM_HERE
, status
);
1083 batch
.Put(CreateRegistrationIdToOriginKey(registration_id
), parts
[0]);
1086 // Update schema version manually instead of relying on WriteBatch to make
1087 // sure each upgrade step only updates it to the actually correct version.
1088 batch
.Put(kDatabaseVersionKey
, base::Int64ToString(2));
1089 status
= LevelDBStatusToStatus(
1090 db_
->Write(leveldb::WriteOptions(), &batch
));
1091 HandleWriteResult(FROM_HERE
, status
);
1095 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadNextAvailableId(
1097 int64
* next_avail_id
) {
1099 DCHECK(next_avail_id
);
1102 Status status
= LevelDBStatusToStatus(
1103 db_
->Get(leveldb::ReadOptions(), id_key
, &value
));
1104 if (status
== STATUS_ERROR_NOT_FOUND
) {
1105 // Nobody has gotten the next resource id for |id_key|.
1107 HandleReadResult(FROM_HERE
, STATUS_OK
);
1109 } else if (status
!= STATUS_OK
) {
1110 HandleReadResult(FROM_HERE
, status
);
1114 status
= ParseId(value
, next_avail_id
);
1115 HandleReadResult(FROM_HERE
, status
);
1119 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadRegistrationData(
1120 int64 registration_id
,
1122 RegistrationData
* registration
) {
1123 DCHECK(registration
);
1125 const std::string key
= CreateRegistrationKey(registration_id
, origin
);
1127 Status status
= LevelDBStatusToStatus(
1128 db_
->Get(leveldb::ReadOptions(), key
, &value
));
1129 if (status
!= STATUS_OK
) {
1132 status
== STATUS_ERROR_NOT_FOUND
? STATUS_OK
: status
);
1136 status
= ParseRegistrationData(value
, registration
);
1137 HandleReadResult(FROM_HERE
, status
);
1141 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceRecords(
1143 std::vector
<ResourceRecord
>* resources
) {
1144 DCHECK(resources
->empty());
1146 Status status
= STATUS_OK
;
1147 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1149 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1150 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1151 Status status
= LevelDBStatusToStatus(itr
->status());
1152 if (status
!= STATUS_OK
) {
1153 HandleReadResult(FROM_HERE
, status
);
1158 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
1161 ResourceRecord resource
;
1162 status
= ParseResourceRecord(itr
->value().ToString(), &resource
);
1163 if (status
!= STATUS_OK
) {
1164 HandleReadResult(FROM_HERE
, status
);
1168 resources
->push_back(resource
);
1171 HandleReadResult(FROM_HERE
, status
);
1175 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceRecords(
1177 std::vector
<int64
>* newly_purgeable_resources
,
1178 leveldb::WriteBatch
* batch
) {
1181 Status status
= STATUS_OK
;
1182 const std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
1184 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1185 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1186 status
= LevelDBStatusToStatus(itr
->status());
1187 if (status
!= STATUS_OK
) {
1188 HandleReadResult(FROM_HERE
, status
);
1192 const std::string key
= itr
->key().ToString();
1193 std::string unprefixed
;
1194 if (!RemovePrefix(key
, prefix
, &unprefixed
))
1198 status
= ParseId(unprefixed
, &resource_id
);
1199 if (status
!= STATUS_OK
) {
1200 HandleReadResult(FROM_HERE
, status
);
1204 // Remove a resource record.
1207 // Currently resource sharing across versions and registrations is not
1208 // supported, so we can purge this without caring about it.
1209 PutPurgeableResourceIdToBatch(resource_id
, batch
);
1210 newly_purgeable_resources
->push_back(resource_id
);
1213 HandleReadResult(FROM_HERE
, status
);
1217 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadResourceIds(
1218 const char* id_key_prefix
,
1219 std::set
<int64
>* ids
) {
1220 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1221 DCHECK(id_key_prefix
);
1222 DCHECK(ids
->empty());
1224 Status status
= LazyOpen(false);
1225 if (IsNewOrNonexistentDatabase(status
))
1227 if (status
!= STATUS_OK
)
1230 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1231 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
1232 status
= LevelDBStatusToStatus(itr
->status());
1233 if (status
!= STATUS_OK
) {
1234 HandleReadResult(FROM_HERE
, status
);
1239 std::string unprefixed
;
1240 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
1244 status
= ParseId(unprefixed
, &resource_id
);
1245 if (status
!= STATUS_OK
) {
1246 HandleReadResult(FROM_HERE
, status
);
1250 ids
->insert(resource_id
);
1253 HandleReadResult(FROM_HERE
, status
);
1257 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIds(
1258 const char* id_key_prefix
,
1259 const std::set
<int64
>& ids
) {
1260 leveldb::WriteBatch batch
;
1261 Status status
= WriteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1262 if (status
!= STATUS_OK
)
1264 return WriteBatch(&batch
);
1267 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteResourceIdsInBatch(
1268 const char* id_key_prefix
,
1269 const std::set
<int64
>& ids
,
1270 leveldb::WriteBatch
* batch
) {
1271 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1272 DCHECK(id_key_prefix
);
1274 Status status
= LazyOpen(true);
1275 if (status
!= STATUS_OK
)
1280 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1281 itr
!= ids
.end(); ++itr
) {
1282 // Value should be empty.
1283 batch
->Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
1285 // std::set is sorted, so the last element is the largest.
1286 BumpNextResourceIdIfNeeded(*ids
.rbegin(), batch
);
1290 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIds(
1291 const char* id_key_prefix
,
1292 const std::set
<int64
>& ids
) {
1293 leveldb::WriteBatch batch
;
1294 Status status
= DeleteResourceIdsInBatch(id_key_prefix
, ids
, &batch
);
1295 if (status
!= STATUS_OK
)
1297 return WriteBatch(&batch
);
1300 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1301 const char* id_key_prefix
,
1302 const std::set
<int64
>& ids
,
1303 leveldb::WriteBatch
* batch
) {
1304 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
1305 DCHECK(id_key_prefix
);
1307 Status status
= LazyOpen(false);
1308 if (IsNewOrNonexistentDatabase(status
))
1310 if (status
!= STATUS_OK
)
1313 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
1314 itr
!= ids
.end(); ++itr
) {
1315 batch
->Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
1320 ServiceWorkerDatabase::Status
1321 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1322 int64 registration_id
,
1323 leveldb::WriteBatch
* batch
) {
1325 Status status
= STATUS_OK
;
1326 const std::string prefix
= CreateUserDataKeyPrefix(registration_id
);
1328 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
1329 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
1330 status
= LevelDBStatusToStatus(itr
->status());
1331 if (status
!= STATUS_OK
) {
1332 HandleReadResult(FROM_HERE
, status
);
1336 const std::string key
= itr
->key().ToString();
1337 std::string user_data_name
;
1338 if (!RemovePrefix(key
, prefix
, &user_data_name
))
1341 batch
->Delete(CreateHasUserDataKey(registration_id
, user_data_name
));
1346 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::ReadDatabaseVersion(
1347 int64
* db_version
) {
1349 Status status
= LevelDBStatusToStatus(
1350 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
));
1351 if (status
== STATUS_ERROR_NOT_FOUND
) {
1352 // The database hasn't been initialized yet.
1354 HandleReadResult(FROM_HERE
, STATUS_OK
);
1358 if (status
!= STATUS_OK
) {
1359 HandleReadResult(FROM_HERE
, status
);
1363 status
= ParseDatabaseVersion(value
, db_version
);
1364 HandleReadResult(FROM_HERE
, status
);
1368 ServiceWorkerDatabase::Status
ServiceWorkerDatabase::WriteBatch(
1369 leveldb::WriteBatch
* batch
) {
1371 DCHECK_NE(DISABLED
, state_
);
1373 if (state_
== UNINITIALIZED
) {
1374 // Write the database schema version.
1375 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
1376 state_
= INITIALIZED
;
1379 Status status
= LevelDBStatusToStatus(
1380 db_
->Write(leveldb::WriteOptions(), batch
));
1381 HandleWriteResult(FROM_HERE
, status
);
1385 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1386 int64 used_id
, leveldb::WriteBatch
* batch
) {
1388 if (next_avail_registration_id_
<= used_id
) {
1389 next_avail_registration_id_
= used_id
+ 1;
1390 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
1394 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1395 int64 used_id
, leveldb::WriteBatch
* batch
) {
1397 if (next_avail_resource_id_
<= used_id
) {
1398 next_avail_resource_id_
= used_id
+ 1;
1399 batch
->Put(kNextResIdKey
, base::Int64ToString(next_avail_resource_id_
));
1403 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1404 int64 used_id
, leveldb::WriteBatch
* batch
) {
1406 if (next_avail_version_id_
<= used_id
) {
1407 next_avail_version_id_
= used_id
+ 1;
1408 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
1412 bool ServiceWorkerDatabase::IsOpen() {
1416 void ServiceWorkerDatabase::Disable(
1417 const tracked_objects::Location
& from_here
,
1419 if (status
!= STATUS_OK
) {
1420 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
1421 << " with error: " << StatusToString(status
);
1422 DLOG(ERROR
) << "ServiceWorkerDatabase is disabled.";
1428 void ServiceWorkerDatabase::HandleOpenResult(
1429 const tracked_objects::Location
& from_here
,
1431 if (status
!= STATUS_OK
)
1432 Disable(from_here
, status
);
1433 ServiceWorkerMetrics::CountOpenDatabaseResult(status
);
1436 void ServiceWorkerDatabase::HandleReadResult(
1437 const tracked_objects::Location
& from_here
,
1439 if (status
!= STATUS_OK
)
1440 Disable(from_here
, status
);
1441 ServiceWorkerMetrics::CountReadDatabaseResult(status
);
1444 void ServiceWorkerDatabase::HandleWriteResult(
1445 const tracked_objects::Location
& from_here
,
1447 if (status
!= STATUS_OK
)
1448 Disable(from_here
, status
);
1449 ServiceWorkerMetrics::CountWriteDatabaseResult(status
);
1452 } // namespace content