base: Change DCHECK_IS_ON to a macro DCHECK_IS_ON().
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blobcf89aa8b8ca64e08637bab5f7bc4ad74a1f92f48
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 // =======================
27 // NOTE
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"
33 // value: "1"
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'>
45 // value: <empty>
47 // key: "PRES:" + <int64 'purgeable_resource_id'>
48 // value: <empty>
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'>
56 // value: <empty>
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'>
68 // value: <empty>
70 namespace content {
72 namespace {
74 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
75 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
76 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
77 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
78 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
80 const char kRegKeyPrefix[] = "REG:";
81 const char kRegUserDataKeyPrefix[] = "REG_USER_DATA:";
82 const char kRegHasUserDataKeyPrefix[] = "REG_HAS_USER_DATA:";
83 const char kResKeyPrefix[] = "RES:";
84 const char kKeySeparator = '\x00';
86 const char kUncommittedResIdKeyPrefix[] = "URES:";
87 const char kPurgeableResIdKeyPrefix[] = "PRES:";
89 const int64 kCurrentSchemaVersion = 1;
91 bool RemovePrefix(const std::string& str,
92 const std::string& prefix,
93 std::string* out) {
94 if (!StartsWithASCII(str, prefix, true))
95 return false;
96 if (out)
97 *out = str.substr(prefix.size());
98 return true;
101 std::string CreateRegistrationKey(int64 registration_id,
102 const GURL& origin) {
103 return base::StringPrintf("%s%s%c%s",
104 kRegKeyPrefix,
105 origin.spec().c_str(),
106 kKeySeparator,
107 base::Int64ToString(registration_id).c_str());
110 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
111 return base::StringPrintf("%s%s%c",
112 kResKeyPrefix,
113 base::Int64ToString(version_id).c_str(),
114 kKeySeparator);
117 std::string CreateResourceRecordKey(int64 version_id,
118 int64 resource_id) {
119 return CreateResourceRecordKeyPrefix(version_id).append(
120 base::Int64ToString(resource_id));
123 std::string CreateUniqueOriginKey(const GURL& origin) {
124 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
127 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
128 return base::StringPrintf(
129 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
132 std::string CreateUserDataKeyPrefix(int64 registration_id) {
133 return base::StringPrintf("%s%s%c",
134 kRegUserDataKeyPrefix,
135 base::Int64ToString(registration_id).c_str(),
136 kKeySeparator);
139 std::string CreateUserDataKey(int64 registration_id,
140 const std::string& user_data_name) {
141 return CreateUserDataKeyPrefix(registration_id).append(user_data_name);
144 std::string CreateHasUserDataKeyPrefix(const std::string& user_data_name) {
145 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix,
146 user_data_name.c_str(), kKeySeparator);
149 std::string CreateHasUserDataKey(int64 registration_id,
150 const std::string& user_data_name) {
151 return CreateHasUserDataKeyPrefix(user_data_name)
152 .append(base::Int64ToString(registration_id));
155 void PutRegistrationDataToBatch(
156 const ServiceWorkerDatabase::RegistrationData& input,
157 leveldb::WriteBatch* batch) {
158 DCHECK(batch);
160 // Convert RegistrationData to ServiceWorkerRegistrationData.
161 ServiceWorkerRegistrationData data;
162 data.set_registration_id(input.registration_id);
163 data.set_scope_url(input.scope.spec());
164 data.set_script_url(input.script.spec());
165 data.set_version_id(input.version_id);
166 data.set_is_active(input.is_active);
167 data.set_has_fetch_handler(input.has_fetch_handler);
168 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
169 data.set_resources_total_size_bytes(input.resources_total_size_bytes);
171 std::string value;
172 bool success = data.SerializeToString(&value);
173 DCHECK(success);
174 GURL origin = input.scope.GetOrigin();
175 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
178 void PutResourceRecordToBatch(
179 const ServiceWorkerDatabase::ResourceRecord& input,
180 int64 version_id,
181 leveldb::WriteBatch* batch) {
182 DCHECK(batch);
183 DCHECK_GE(input.size_bytes, 0);
185 // Convert ResourceRecord to ServiceWorkerResourceRecord.
186 ServiceWorkerResourceRecord record;
187 record.set_resource_id(input.resource_id);
188 record.set_url(input.url.spec());
189 record.set_size_bytes(input.size_bytes);
191 std::string value;
192 bool success = record.SerializeToString(&value);
193 DCHECK(success);
194 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
197 void PutUniqueOriginToBatch(const GURL& origin,
198 leveldb::WriteBatch* batch) {
199 // Value should be empty.
200 batch->Put(CreateUniqueOriginKey(origin), "");
203 void PutPurgeableResourceIdToBatch(int64 resource_id,
204 leveldb::WriteBatch* batch) {
205 // Value should be empty.
206 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
209 ServiceWorkerDatabase::Status ParseId(
210 const std::string& serialized,
211 int64* out) {
212 DCHECK(out);
213 int64 id;
214 if (!base::StringToInt64(serialized, &id) || id < 0)
215 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
216 *out = id;
217 return ServiceWorkerDatabase::STATUS_OK;
220 ServiceWorkerDatabase::Status ParseDatabaseVersion(
221 const std::string& serialized,
222 int64* out) {
223 DCHECK(out);
224 const int kFirstValidVersion = 1;
225 int64 version;
226 if (!base::StringToInt64(serialized, &version) ||
227 version < kFirstValidVersion) {
228 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
230 if (kCurrentSchemaVersion < version) {
231 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
232 << " than the current latest version: "
233 << version << " vs " << kCurrentSchemaVersion;
234 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
236 *out = version;
237 return ServiceWorkerDatabase::STATUS_OK;
240 ServiceWorkerDatabase::Status ParseRegistrationData(
241 const std::string& serialized,
242 ServiceWorkerDatabase::RegistrationData* out) {
243 DCHECK(out);
244 ServiceWorkerRegistrationData data;
245 if (!data.ParseFromString(serialized))
246 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
248 GURL scope_url(data.scope_url());
249 GURL script_url(data.script_url());
250 if (!scope_url.is_valid() ||
251 !script_url.is_valid() ||
252 scope_url.GetOrigin() != script_url.GetOrigin()) {
253 DLOG(ERROR) << "Scope URL '" << data.scope_url() << "' and/or script url '"
254 << data.script_url()
255 << "' are invalid or have mismatching origins.";
256 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
259 // Convert ServiceWorkerRegistrationData to RegistrationData.
260 out->registration_id = data.registration_id();
261 out->scope = scope_url;
262 out->script = script_url;
263 out->version_id = data.version_id();
264 out->is_active = data.is_active();
265 out->has_fetch_handler = data.has_fetch_handler();
266 out->last_update_check =
267 base::Time::FromInternalValue(data.last_update_check_time());
268 out->resources_total_size_bytes = data.resources_total_size_bytes();
270 return ServiceWorkerDatabase::STATUS_OK;
273 ServiceWorkerDatabase::Status ParseResourceRecord(
274 const std::string& serialized,
275 ServiceWorkerDatabase::ResourceRecord* out) {
276 DCHECK(out);
277 ServiceWorkerResourceRecord record;
278 if (!record.ParseFromString(serialized))
279 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
281 GURL url(record.url());
282 if (!url.is_valid())
283 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
285 // Convert ServiceWorkerResourceRecord to ResourceRecord.
286 out->resource_id = record.resource_id();
287 out->url = url;
288 out->size_bytes = record.size_bytes();
289 return ServiceWorkerDatabase::STATUS_OK;
292 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
293 const leveldb::Status& status) {
294 if (status.ok())
295 return ServiceWorkerDatabase::STATUS_OK;
296 else if (status.IsNotFound())
297 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
298 else if (status.IsIOError())
299 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
300 else if (status.IsCorruption())
301 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
302 else
303 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
306 } // namespace
308 const char* ServiceWorkerDatabase::StatusToString(
309 ServiceWorkerDatabase::Status status) {
310 switch (status) {
311 case ServiceWorkerDatabase::STATUS_OK:
312 return "Database OK";
313 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
314 return "Database not found";
315 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
316 return "Database IO error";
317 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
318 return "Database corrupted";
319 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
320 return "Database operation failed";
321 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
322 NOTREACHED();
323 return "Database unknown error";
325 NOTREACHED();
326 return "Database unknown error";
329 ServiceWorkerDatabase::RegistrationData::RegistrationData()
330 : registration_id(kInvalidServiceWorkerRegistrationId),
331 version_id(kInvalidServiceWorkerVersionId),
332 is_active(false),
333 has_fetch_handler(false),
334 resources_total_size_bytes(0) {
337 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
340 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
341 : path_(path),
342 next_avail_registration_id_(0),
343 next_avail_resource_id_(0),
344 next_avail_version_id_(0),
345 state_(UNINITIALIZED) {
346 sequence_checker_.DetachFromSequence();
349 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
350 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
351 db_.reset();
354 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
355 int64* next_avail_registration_id,
356 int64* next_avail_version_id,
357 int64* next_avail_resource_id) {
358 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
359 DCHECK(next_avail_registration_id);
360 DCHECK(next_avail_version_id);
361 DCHECK(next_avail_resource_id);
363 Status status = LazyOpen(false);
364 if (IsNewOrNonexistentDatabase(status)) {
365 *next_avail_registration_id = 0;
366 *next_avail_version_id = 0;
367 *next_avail_resource_id = 0;
368 return STATUS_OK;
370 if (status != STATUS_OK)
371 return status;
373 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
374 if (status != STATUS_OK)
375 return status;
376 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
377 if (status != STATUS_OK)
378 return status;
379 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
380 if (status != STATUS_OK)
381 return status;
383 *next_avail_registration_id = next_avail_registration_id_;
384 *next_avail_version_id = next_avail_version_id_;
385 *next_avail_resource_id = next_avail_resource_id_;
386 return STATUS_OK;
389 ServiceWorkerDatabase::Status
390 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
391 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
392 DCHECK(origins->empty());
394 Status status = LazyOpen(false);
395 if (IsNewOrNonexistentDatabase(status))
396 return STATUS_OK;
397 if (status != STATUS_OK)
398 return status;
400 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
401 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
402 status = LevelDBStatusToStatus(itr->status());
403 if (status != STATUS_OK) {
404 HandleReadResult(FROM_HERE, status);
405 origins->clear();
406 return status;
409 std::string origin;
410 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
411 break;
412 origins->insert(GURL(origin));
415 HandleReadResult(FROM_HERE, status);
416 return status;
419 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
420 const GURL& origin,
421 std::vector<RegistrationData>* registrations) {
422 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
423 DCHECK(registrations->empty());
425 Status status = LazyOpen(false);
426 if (IsNewOrNonexistentDatabase(status))
427 return STATUS_OK;
428 if (status != STATUS_OK)
429 return status;
431 // Create a key prefix for registrations.
432 std::string prefix = base::StringPrintf(
433 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
435 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
436 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
437 status = LevelDBStatusToStatus(itr->status());
438 if (status != STATUS_OK) {
439 HandleReadResult(FROM_HERE, status);
440 registrations->clear();
441 return status;
444 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
445 break;
447 RegistrationData registration;
448 status = ParseRegistrationData(itr->value().ToString(), &registration);
449 if (status != STATUS_OK) {
450 HandleReadResult(FROM_HERE, status);
451 registrations->clear();
452 return status;
454 registrations->push_back(registration);
457 HandleReadResult(FROM_HERE, status);
458 return status;
461 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
462 std::vector<RegistrationData>* registrations) {
463 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
464 DCHECK(registrations->empty());
466 Status status = LazyOpen(false);
467 if (IsNewOrNonexistentDatabase(status))
468 return STATUS_OK;
469 if (status != STATUS_OK)
470 return status;
472 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
473 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
474 status = LevelDBStatusToStatus(itr->status());
475 if (status != STATUS_OK) {
476 HandleReadResult(FROM_HERE, status);
477 registrations->clear();
478 return status;
481 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
482 break;
484 RegistrationData registration;
485 status = ParseRegistrationData(itr->value().ToString(), &registration);
486 if (status != STATUS_OK) {
487 HandleReadResult(FROM_HERE, status);
488 registrations->clear();
489 return status;
491 registrations->push_back(registration);
494 HandleReadResult(FROM_HERE, status);
495 return status;
498 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
499 int64 registration_id,
500 const GURL& origin,
501 RegistrationData* registration,
502 std::vector<ResourceRecord>* resources) {
503 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
504 DCHECK(registration);
505 DCHECK(resources);
507 Status status = LazyOpen(false);
508 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
509 return status;
511 RegistrationData value;
512 status = ReadRegistrationData(registration_id, origin, &value);
513 if (status != STATUS_OK)
514 return status;
516 status = ReadResourceRecords(value.version_id, resources);
517 if (status != STATUS_OK)
518 return status;
520 *registration = value;
521 return STATUS_OK;
524 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
525 const RegistrationData& registration,
526 const std::vector<ResourceRecord>& resources,
527 RegistrationData* old_registration,
528 std::vector<int64>* newly_purgeable_resources) {
529 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
530 DCHECK(old_registration);
531 Status status = LazyOpen(true);
532 old_registration->version_id = kInvalidServiceWorkerVersionId;
533 if (status != STATUS_OK)
534 return status;
536 leveldb::WriteBatch batch;
537 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
538 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
540 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
541 #if DCHECK_IS_ON()
542 int64_t total_size_bytes = 0;
543 for (const auto& resource : resources) {
544 total_size_bytes += resource.size_bytes;
546 DCHECK_EQ(total_size_bytes, registration.resources_total_size_bytes)
547 << "The total size in the registration must match the cumulative "
548 << "sizes of the resources.";
549 #endif
550 PutRegistrationDataToBatch(registration, &batch);
552 // Used for avoiding multiple writes for the same resource id or url.
553 std::set<int64> pushed_resources;
554 std::set<GURL> pushed_urls;
555 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
556 itr != resources.end(); ++itr) {
557 if (!itr->url.is_valid())
558 return STATUS_ERROR_FAILED;
560 // Duplicated resource id or url should not exist.
561 DCHECK(pushed_resources.insert(itr->resource_id).second);
562 DCHECK(pushed_urls.insert(itr->url).second);
564 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
566 // Delete a resource from the uncommitted list.
567 batch.Delete(CreateResourceIdKey(
568 kUncommittedResIdKeyPrefix, itr->resource_id));
569 // Delete from the purgeable list in case this version was once deleted.
570 batch.Delete(
571 CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id));
574 // Retrieve a previous version to sweep purgeable resources.
575 status = ReadRegistrationData(registration.registration_id,
576 registration.scope.GetOrigin(),
577 old_registration);
578 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
579 return status;
580 if (status == STATUS_OK) {
581 DCHECK_LT(old_registration->version_id, registration.version_id);
582 status = DeleteResourceRecords(
583 old_registration->version_id, newly_purgeable_resources, &batch);
584 if (status != STATUS_OK)
585 return status;
587 // Currently resource sharing across versions and registrations is not
588 // supported, so resource ids should not be overlapped between
589 // |registration| and |old_registration|.
590 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
591 newly_purgeable_resources->end());
592 DCHECK(base::STLSetIntersection<std::set<int64> >(
593 pushed_resources, deleted_resources).empty());
596 return WriteBatch(&batch);
599 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
600 int64 registration_id,
601 const GURL& origin) {
602 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
603 Status status = LazyOpen(false);
604 if (IsNewOrNonexistentDatabase(status))
605 return STATUS_ERROR_NOT_FOUND;
606 if (status != STATUS_OK)
607 return status;
608 if (!origin.is_valid())
609 return STATUS_ERROR_FAILED;
611 RegistrationData registration;
612 status = ReadRegistrationData(registration_id, origin, &registration);
613 if (status != STATUS_OK)
614 return status;
616 registration.is_active = true;
618 leveldb::WriteBatch batch;
619 PutRegistrationDataToBatch(registration, &batch);
620 return WriteBatch(&batch);
623 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
624 int64 registration_id,
625 const GURL& origin,
626 const base::Time& time) {
627 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
628 Status status = LazyOpen(false);
629 if (IsNewOrNonexistentDatabase(status))
630 return STATUS_ERROR_NOT_FOUND;
631 if (status != STATUS_OK)
632 return status;
633 if (!origin.is_valid())
634 return STATUS_ERROR_FAILED;
636 RegistrationData registration;
637 status = ReadRegistrationData(registration_id, origin, &registration);
638 if (status != STATUS_OK)
639 return status;
641 registration.last_update_check = time;
643 leveldb::WriteBatch batch;
644 PutRegistrationDataToBatch(registration, &batch);
645 return WriteBatch(&batch);
648 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
649 int64 registration_id,
650 const GURL& origin,
651 RegistrationData* deleted_version,
652 std::vector<int64>* newly_purgeable_resources) {
653 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
654 DCHECK(deleted_version);
655 deleted_version->version_id = kInvalidServiceWorkerVersionId;
656 Status status = LazyOpen(false);
657 if (IsNewOrNonexistentDatabase(status))
658 return STATUS_OK;
659 if (status != STATUS_OK)
660 return status;
661 if (!origin.is_valid())
662 return STATUS_ERROR_FAILED;
664 leveldb::WriteBatch batch;
666 // Remove |origin| from unique origins if a registration specified by
667 // |registration_id| is the only one for |origin|.
668 // TODO(nhiroki): Check the uniqueness by more efficient way.
669 std::vector<RegistrationData> registrations;
670 status = GetRegistrationsForOrigin(origin, &registrations);
671 if (status != STATUS_OK)
672 return status;
674 if (registrations.size() == 1 &&
675 registrations[0].registration_id == registration_id) {
676 batch.Delete(CreateUniqueOriginKey(origin));
679 // Delete a registration specified by |registration_id|.
680 batch.Delete(CreateRegistrationKey(registration_id, origin));
682 // Delete resource records and user data associated with the registration.
683 for (const auto& registration : registrations) {
684 if (registration.registration_id == registration_id) {
685 *deleted_version = registration;
686 status = DeleteResourceRecords(
687 registration.version_id, newly_purgeable_resources, &batch);
688 if (status != STATUS_OK)
689 return status;
691 status = DeleteUserDataForRegistration(registration_id, &batch);
692 if (status != STATUS_OK)
693 return status;
694 break;
698 return WriteBatch(&batch);
701 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadUserData(
702 int64 registration_id,
703 const std::string& user_data_name,
704 std::string* user_data) {
705 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
706 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
707 DCHECK(!user_data_name.empty());
708 DCHECK(user_data);
710 Status status = LazyOpen(false);
711 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
712 return status;
714 const std::string key = CreateUserDataKey(registration_id, user_data_name);
715 status = LevelDBStatusToStatus(
716 db_->Get(leveldb::ReadOptions(), key, user_data));
717 HandleReadResult(FROM_HERE,
718 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
719 return status;
722 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteUserData(
723 int64 registration_id,
724 const GURL& origin,
725 const std::string& user_data_name,
726 const std::string& user_data) {
727 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
728 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
729 DCHECK(!user_data_name.empty());
731 Status status = LazyOpen(false);
732 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
733 return status;
735 // There should be the registration specified by |registration_id|.
736 RegistrationData registration;
737 status = ReadRegistrationData(registration_id, origin, &registration);
738 if (status != STATUS_OK)
739 return status;
741 leveldb::WriteBatch batch;
742 batch.Put(CreateUserDataKey(registration_id, user_data_name), user_data);
743 batch.Put(CreateHasUserDataKey(registration_id, user_data_name), "");
744 return WriteBatch(&batch);
747 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteUserData(
748 int64 registration_id,
749 const std::string& user_data_name) {
750 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
751 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
752 DCHECK(!user_data_name.empty());
754 Status status = LazyOpen(false);
755 if (IsNewOrNonexistentDatabase(status))
756 return STATUS_OK;
757 if (status != STATUS_OK)
758 return status;
760 leveldb::WriteBatch batch;
761 batch.Delete(CreateUserDataKey(registration_id, user_data_name));
762 batch.Delete(CreateHasUserDataKey(registration_id, user_data_name));
763 return WriteBatch(&batch);
766 ServiceWorkerDatabase::Status
767 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
768 const std::string& user_data_name,
769 std::vector<std::pair<int64, std::string>>* user_data) {
770 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
771 DCHECK(user_data->empty());
773 Status status = LazyOpen(false);
774 if (IsNewOrNonexistentDatabase(status))
775 return STATUS_OK;
776 if (status != STATUS_OK)
777 return status;
779 std::string key_prefix = CreateHasUserDataKeyPrefix(user_data_name);
780 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
781 for (itr->Seek(key_prefix); itr->Valid(); itr->Next()) {
782 status = LevelDBStatusToStatus(itr->status());
783 if (status != STATUS_OK) {
784 HandleReadResult(FROM_HERE, status);
785 user_data->clear();
786 return status;
789 std::string registration_id_string;
790 if (!RemovePrefix(itr->key().ToString(), key_prefix,
791 &registration_id_string)) {
792 break;
795 int64 registration_id;
796 status = ParseId(registration_id_string, &registration_id);
797 if (status != STATUS_OK) {
798 HandleReadResult(FROM_HERE, status);
799 user_data->clear();
800 return status;
803 std::string value;
804 status = LevelDBStatusToStatus(
805 db_->Get(leveldb::ReadOptions(),
806 CreateUserDataKey(registration_id, user_data_name), &value));
807 if (status != STATUS_OK) {
808 HandleReadResult(FROM_HERE, status);
809 user_data->clear();
810 return status;
812 user_data->push_back(std::make_pair(registration_id, value));
815 HandleReadResult(FROM_HERE, status);
816 return status;
819 ServiceWorkerDatabase::Status
820 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
821 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
824 ServiceWorkerDatabase::Status
825 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
826 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
829 ServiceWorkerDatabase::Status
830 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
831 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
834 ServiceWorkerDatabase::Status
835 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
836 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
839 ServiceWorkerDatabase::Status
840 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
841 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
844 ServiceWorkerDatabase::Status
845 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
846 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
849 ServiceWorkerDatabase::Status
850 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
851 const std::set<int64>& ids) {
852 leveldb::WriteBatch batch;
853 Status status = DeleteResourceIdsInBatch(
854 kUncommittedResIdKeyPrefix, ids, &batch);
855 if (status != STATUS_OK)
856 return status;
857 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
858 if (status != STATUS_OK)
859 return status;
860 return WriteBatch(&batch);
863 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigins(
864 const std::set<GURL>& origins,
865 std::vector<int64>* newly_purgeable_resources) {
866 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
867 Status status = LazyOpen(false);
868 if (IsNewOrNonexistentDatabase(status))
869 return STATUS_OK;
870 if (status != STATUS_OK)
871 return status;
872 leveldb::WriteBatch batch;
874 for (const GURL& origin : origins) {
875 if (!origin.is_valid())
876 return STATUS_ERROR_FAILED;
878 // Delete from the unique origin list.
879 batch.Delete(CreateUniqueOriginKey(origin));
881 std::vector<RegistrationData> registrations;
882 status = GetRegistrationsForOrigin(origin, &registrations);
883 if (status != STATUS_OK)
884 return status;
886 // Delete registrations, resource records and user data.
887 for (const RegistrationData& data : registrations) {
888 batch.Delete(CreateRegistrationKey(data.registration_id, origin));
890 status = DeleteResourceRecords(
891 data.version_id, newly_purgeable_resources, &batch);
892 if (status != STATUS_OK)
893 return status;
895 status = DeleteUserDataForRegistration(data.registration_id, &batch);
896 if (status != STATUS_OK)
897 return status;
901 return WriteBatch(&batch);
904 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
905 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
906 Disable(FROM_HERE, STATUS_OK);
908 leveldb::Options options;
909 if (path_.empty()) {
910 if (env_) {
911 options.env = env_.get();
912 } else {
913 // In-memory database not initialized.
914 return STATUS_OK;
918 return LevelDBStatusToStatus(
919 leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
922 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
923 bool create_if_missing) {
924 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
926 // Do not try to open a database if we tried and failed once.
927 if (state_ == DISABLED)
928 return STATUS_ERROR_FAILED;
929 if (IsOpen())
930 return STATUS_OK;
932 // When |path_| is empty, open a database in-memory.
933 bool use_in_memory_db = path_.empty();
935 if (!create_if_missing) {
936 // Avoid opening a database if it does not exist at the |path_|.
937 if (use_in_memory_db ||
938 !base::PathExists(path_) ||
939 base::IsDirectoryEmpty(path_)) {
940 return STATUS_ERROR_NOT_FOUND;
944 leveldb::Options options;
945 options.create_if_missing = create_if_missing;
946 if (use_in_memory_db) {
947 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
948 options.env = env_.get();
951 leveldb::DB* db = NULL;
952 Status status = LevelDBStatusToStatus(
953 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
954 HandleOpenResult(FROM_HERE, status);
955 if (status != STATUS_OK) {
956 DCHECK(!db);
957 // TODO(nhiroki): Should we retry to open the database?
958 return status;
960 db_.reset(db);
962 int64 db_version;
963 status = ReadDatabaseVersion(&db_version);
964 if (status != STATUS_OK)
965 return status;
966 DCHECK_LE(0, db_version);
967 if (db_version > 0)
968 state_ = INITIALIZED;
969 return STATUS_OK;
972 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
973 ServiceWorkerDatabase::Status status) {
974 if (status == STATUS_ERROR_NOT_FOUND)
975 return true;
976 if (status == STATUS_OK && state_ == UNINITIALIZED)
977 return true;
978 return false;
981 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
982 const char* id_key,
983 int64* next_avail_id) {
984 DCHECK(id_key);
985 DCHECK(next_avail_id);
987 std::string value;
988 Status status = LevelDBStatusToStatus(
989 db_->Get(leveldb::ReadOptions(), id_key, &value));
990 if (status == STATUS_ERROR_NOT_FOUND) {
991 // Nobody has gotten the next resource id for |id_key|.
992 *next_avail_id = 0;
993 HandleReadResult(FROM_HERE, STATUS_OK);
994 return STATUS_OK;
995 } else if (status != STATUS_OK) {
996 HandleReadResult(FROM_HERE, status);
997 return status;
1000 status = ParseId(value, next_avail_id);
1001 HandleReadResult(FROM_HERE, status);
1002 return status;
1005 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
1006 int64 registration_id,
1007 const GURL& origin,
1008 RegistrationData* registration) {
1009 DCHECK(registration);
1011 const std::string key = CreateRegistrationKey(registration_id, origin);
1012 std::string value;
1013 Status status = LevelDBStatusToStatus(
1014 db_->Get(leveldb::ReadOptions(), key, &value));
1015 if (status != STATUS_OK) {
1016 HandleReadResult(
1017 FROM_HERE,
1018 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
1019 return status;
1022 status = ParseRegistrationData(value, registration);
1023 HandleReadResult(FROM_HERE, status);
1024 return status;
1027 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
1028 int64 version_id,
1029 std::vector<ResourceRecord>* resources) {
1030 DCHECK(resources->empty());
1032 Status status = STATUS_OK;
1033 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1035 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1036 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1037 Status status = LevelDBStatusToStatus(itr->status());
1038 if (status != STATUS_OK) {
1039 HandleReadResult(FROM_HERE, status);
1040 resources->clear();
1041 return status;
1044 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
1045 break;
1047 ResourceRecord resource;
1048 status = ParseResourceRecord(itr->value().ToString(), &resource);
1049 if (status != STATUS_OK) {
1050 HandleReadResult(FROM_HERE, status);
1051 resources->clear();
1052 return status;
1054 resources->push_back(resource);
1057 HandleReadResult(FROM_HERE, status);
1058 return status;
1061 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
1062 int64 version_id,
1063 std::vector<int64>* newly_purgeable_resources,
1064 leveldb::WriteBatch* batch) {
1065 DCHECK(batch);
1067 Status status = STATUS_OK;
1068 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1070 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1071 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1072 status = LevelDBStatusToStatus(itr->status());
1073 if (status != STATUS_OK) {
1074 HandleReadResult(FROM_HERE, status);
1075 return status;
1078 const std::string key = itr->key().ToString();
1079 std::string unprefixed;
1080 if (!RemovePrefix(key, prefix, &unprefixed))
1081 break;
1083 int64 resource_id;
1084 status = ParseId(unprefixed, &resource_id);
1085 if (status != STATUS_OK) {
1086 HandleReadResult(FROM_HERE, status);
1087 return status;
1090 // Remove a resource record.
1091 batch->Delete(key);
1093 // Currently resource sharing across versions and registrations is not
1094 // supported, so we can purge this without caring about it.
1095 PutPurgeableResourceIdToBatch(resource_id, batch);
1096 newly_purgeable_resources->push_back(resource_id);
1099 HandleReadResult(FROM_HERE, status);
1100 return status;
1103 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
1104 const char* id_key_prefix,
1105 std::set<int64>* ids) {
1106 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1107 DCHECK(id_key_prefix);
1108 DCHECK(ids->empty());
1110 Status status = LazyOpen(false);
1111 if (IsNewOrNonexistentDatabase(status))
1112 return STATUS_OK;
1113 if (status != STATUS_OK)
1114 return status;
1116 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1117 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
1118 status = LevelDBStatusToStatus(itr->status());
1119 if (status != STATUS_OK) {
1120 HandleReadResult(FROM_HERE, status);
1121 ids->clear();
1122 return status;
1125 std::string unprefixed;
1126 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
1127 break;
1129 int64 resource_id;
1130 status = ParseId(unprefixed, &resource_id);
1131 if (status != STATUS_OK) {
1132 HandleReadResult(FROM_HERE, status);
1133 ids->clear();
1134 return status;
1136 ids->insert(resource_id);
1139 HandleReadResult(FROM_HERE, status);
1140 return status;
1143 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
1144 const char* id_key_prefix,
1145 const std::set<int64>& ids) {
1146 leveldb::WriteBatch batch;
1147 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
1148 if (status != STATUS_OK)
1149 return status;
1150 return WriteBatch(&batch);
1153 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
1154 const char* id_key_prefix,
1155 const std::set<int64>& ids,
1156 leveldb::WriteBatch* batch) {
1157 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1158 DCHECK(id_key_prefix);
1160 Status status = LazyOpen(true);
1161 if (status != STATUS_OK)
1162 return status;
1164 if (ids.empty())
1165 return STATUS_OK;
1166 for (std::set<int64>::const_iterator itr = ids.begin();
1167 itr != ids.end(); ++itr) {
1168 // Value should be empty.
1169 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
1171 // std::set is sorted, so the last element is the largest.
1172 BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
1173 return STATUS_OK;
1176 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
1177 const char* id_key_prefix,
1178 const std::set<int64>& ids) {
1179 leveldb::WriteBatch batch;
1180 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
1181 if (status != STATUS_OK)
1182 return status;
1183 return WriteBatch(&batch);
1186 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1187 const char* id_key_prefix,
1188 const std::set<int64>& ids,
1189 leveldb::WriteBatch* batch) {
1190 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1191 DCHECK(id_key_prefix);
1193 Status status = LazyOpen(false);
1194 if (IsNewOrNonexistentDatabase(status))
1195 return STATUS_OK;
1196 if (status != STATUS_OK)
1197 return status;
1199 for (std::set<int64>::const_iterator itr = ids.begin();
1200 itr != ids.end(); ++itr) {
1201 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1203 return STATUS_OK;
1206 ServiceWorkerDatabase::Status
1207 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1208 int64 registration_id,
1209 leveldb::WriteBatch* batch) {
1210 DCHECK(batch);
1211 Status status = STATUS_OK;
1212 const std::string prefix = CreateUserDataKeyPrefix(registration_id);
1214 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1215 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1216 status = LevelDBStatusToStatus(itr->status());
1217 if (status != STATUS_OK) {
1218 HandleReadResult(FROM_HERE, status);
1219 return status;
1222 const std::string key = itr->key().ToString();
1223 std::string user_data_name;
1224 if (!RemovePrefix(key, prefix, &user_data_name))
1225 break;
1226 batch->Delete(key);
1227 batch->Delete(CreateHasUserDataKey(registration_id, user_data_name));
1229 return status;
1232 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1233 int64* db_version) {
1234 std::string value;
1235 Status status = LevelDBStatusToStatus(
1236 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1237 if (status == STATUS_ERROR_NOT_FOUND) {
1238 // The database hasn't been initialized yet.
1239 *db_version = 0;
1240 HandleReadResult(FROM_HERE, STATUS_OK);
1241 return STATUS_OK;
1244 if (status != STATUS_OK) {
1245 HandleReadResult(FROM_HERE, status);
1246 return status;
1249 status = ParseDatabaseVersion(value, db_version);
1250 HandleReadResult(FROM_HERE, status);
1251 return status;
1254 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1255 leveldb::WriteBatch* batch) {
1256 DCHECK(batch);
1257 DCHECK_NE(DISABLED, state_);
1259 if (state_ == UNINITIALIZED) {
1260 // Write the database schema version.
1261 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1262 state_ = INITIALIZED;
1265 Status status = LevelDBStatusToStatus(
1266 db_->Write(leveldb::WriteOptions(), batch));
1267 HandleWriteResult(FROM_HERE, status);
1268 return status;
1271 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1272 int64 used_id, leveldb::WriteBatch* batch) {
1273 DCHECK(batch);
1274 if (next_avail_registration_id_ <= used_id) {
1275 next_avail_registration_id_ = used_id + 1;
1276 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1280 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1281 int64 used_id, leveldb::WriteBatch* batch) {
1282 DCHECK(batch);
1283 if (next_avail_resource_id_ <= used_id) {
1284 next_avail_resource_id_ = used_id + 1;
1285 batch->Put(kNextResIdKey, base::Int64ToString(next_avail_resource_id_));
1289 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1290 int64 used_id, leveldb::WriteBatch* batch) {
1291 DCHECK(batch);
1292 if (next_avail_version_id_ <= used_id) {
1293 next_avail_version_id_ = used_id + 1;
1294 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1298 bool ServiceWorkerDatabase::IsOpen() {
1299 return db_ != NULL;
1302 void ServiceWorkerDatabase::Disable(
1303 const tracked_objects::Location& from_here,
1304 Status status) {
1305 if (status != STATUS_OK) {
1306 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1307 << " with error: " << StatusToString(status);
1308 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1310 state_ = DISABLED;
1311 db_.reset();
1314 void ServiceWorkerDatabase::HandleOpenResult(
1315 const tracked_objects::Location& from_here,
1316 Status status) {
1317 if (status != STATUS_OK)
1318 Disable(from_here, status);
1319 ServiceWorkerMetrics::CountOpenDatabaseResult(status);
1322 void ServiceWorkerDatabase::HandleReadResult(
1323 const tracked_objects::Location& from_here,
1324 Status status) {
1325 if (status != STATUS_OK)
1326 Disable(from_here, status);
1327 ServiceWorkerMetrics::CountReadDatabaseResult(status);
1330 void ServiceWorkerDatabase::HandleWriteResult(
1331 const tracked_objects::Location& from_here,
1332 Status status) {
1333 if (status != STATUS_OK)
1334 Disable(from_here, status);
1335 ServiceWorkerMetrics::CountWriteDatabaseResult(status);
1338 } // namespace content