Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blob61b8659e77ca16d4fea8b57fa6d8a900c2d56520
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 <string>
9 #include "base/files/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "content/browser/service_worker/service_worker_database.pb.h"
19 #include "content/browser/service_worker/service_worker_metrics.h"
20 #include "content/common/service_worker/service_worker_types.h"
21 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
22 #include "third_party/leveldatabase/src/include/leveldb/db.h"
23 #include "third_party/leveldatabase/src/include/leveldb/env.h"
24 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
26 // LevelDB database schema
27 // =======================
29 // NOTE
30 // - int64 value is serialized as a string by base::Int64ToString().
31 // - GURL value is serialized as a string by GURL::spec().
33 // Version 1 (in sorted order)
34 // key: "INITDATA_DB_VERSION"
35 // value: "1"
37 // key: "INITDATA_NEXT_REGISTRATION_ID"
38 // value: <int64 'next_available_registration_id'>
40 // key: "INITDATA_NEXT_RESOURCE_ID"
41 // value: <int64 'next_available_resource_id'>
43 // key: "INITDATA_NEXT_VERSION_ID"
44 // value: <int64 'next_available_version_id'>
46 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
47 // value: <empty>
49 // key: "PRES:" + <int64 'purgeable_resource_id'>
50 // value: <empty>
52 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
53 // (ex. "REG:http://example.com\x00123456")
54 // value: <ServiceWorkerRegistrationData serialized as a string>
56 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
57 // (ex. "RES:123456\x00654321")
58 // value: <ServiceWorkerResourceRecord serialized as a string>
60 // key: "URES:" + <int64 'uncommitted_resource_id'>
61 // value: <empty>
63 namespace content {
65 namespace {
67 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
68 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
69 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
70 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
71 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
73 const char kRegKeyPrefix[] = "REG:";
74 const char kResKeyPrefix[] = "RES:";
75 const char kKeySeparator = '\x00';
77 const char kUncommittedResIdKeyPrefix[] = "URES:";
78 const char kPurgeableResIdKeyPrefix[] = "PRES:";
80 const int64 kCurrentSchemaVersion = 1;
82 bool RemovePrefix(const std::string& str,
83 const std::string& prefix,
84 std::string* out) {
85 if (!StartsWithASCII(str, prefix, true))
86 return false;
87 if (out)
88 *out = str.substr(prefix.size());
89 return true;
92 std::string CreateRegistrationKey(int64 registration_id,
93 const GURL& origin) {
94 return base::StringPrintf("%s%s%c%s",
95 kRegKeyPrefix,
96 origin.spec().c_str(),
97 kKeySeparator,
98 base::Int64ToString(registration_id).c_str());
101 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
102 return base::StringPrintf("%s%s%c",
103 kResKeyPrefix,
104 base::Int64ToString(version_id).c_str(),
105 kKeySeparator);
108 std::string CreateResourceRecordKey(int64 version_id,
109 int64 resource_id) {
110 return CreateResourceRecordKeyPrefix(version_id).append(
111 base::Int64ToString(resource_id));
114 std::string CreateUniqueOriginKey(const GURL& origin) {
115 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
118 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
119 return base::StringPrintf(
120 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
123 void PutRegistrationDataToBatch(
124 const ServiceWorkerDatabase::RegistrationData& input,
125 leveldb::WriteBatch* batch) {
126 DCHECK(batch);
128 // Convert RegistrationData to ServiceWorkerRegistrationData.
129 ServiceWorkerRegistrationData data;
130 data.set_registration_id(input.registration_id);
131 data.set_scope_url(input.scope.spec());
132 data.set_script_url(input.script.spec());
133 data.set_version_id(input.version_id);
134 data.set_is_active(input.is_active);
135 data.set_has_fetch_handler(input.has_fetch_handler);
136 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
137 data.set_resources_total_size_bytes(input.resources_total_size_bytes);
139 std::string value;
140 bool success = data.SerializeToString(&value);
141 DCHECK(success);
142 GURL origin = input.scope.GetOrigin();
143 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
146 void PutResourceRecordToBatch(
147 const ServiceWorkerDatabase::ResourceRecord& input,
148 int64 version_id,
149 leveldb::WriteBatch* batch) {
150 DCHECK(batch);
151 DCHECK_GE(input.size_bytes, 0);
153 // Convert ResourceRecord to ServiceWorkerResourceRecord.
154 ServiceWorkerResourceRecord record;
155 record.set_resource_id(input.resource_id);
156 record.set_url(input.url.spec());
157 record.set_size_bytes(input.size_bytes);
159 std::string value;
160 bool success = record.SerializeToString(&value);
161 DCHECK(success);
162 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
165 void PutUniqueOriginToBatch(const GURL& origin,
166 leveldb::WriteBatch* batch) {
167 // Value should be empty.
168 batch->Put(CreateUniqueOriginKey(origin), "");
171 void PutPurgeableResourceIdToBatch(int64 resource_id,
172 leveldb::WriteBatch* batch) {
173 // Value should be empty.
174 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
177 ServiceWorkerDatabase::Status ParseId(
178 const std::string& serialized,
179 int64* out) {
180 DCHECK(out);
181 int64 id;
182 if (!base::StringToInt64(serialized, &id) || id < 0)
183 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
184 *out = id;
185 return ServiceWorkerDatabase::STATUS_OK;
188 ServiceWorkerDatabase::Status ParseDatabaseVersion(
189 const std::string& serialized,
190 int64* out) {
191 DCHECK(out);
192 const int kFirstValidVersion = 1;
193 int64 version;
194 if (!base::StringToInt64(serialized, &version) ||
195 version < kFirstValidVersion) {
196 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
198 if (kCurrentSchemaVersion < version) {
199 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
200 << " than the current latest version: "
201 << version << " vs " << kCurrentSchemaVersion;
202 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
204 *out = version;
205 return ServiceWorkerDatabase::STATUS_OK;
208 ServiceWorkerDatabase::Status ParseRegistrationData(
209 const std::string& serialized,
210 ServiceWorkerDatabase::RegistrationData* out) {
211 DCHECK(out);
212 ServiceWorkerRegistrationData data;
213 if (!data.ParseFromString(serialized))
214 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
216 GURL scope_url(data.scope_url());
217 GURL script_url(data.script_url());
218 if (!scope_url.is_valid() ||
219 !script_url.is_valid() ||
220 scope_url.GetOrigin() != script_url.GetOrigin()) {
221 DLOG(ERROR) << "Scope URL '" << data.scope_url() << "' and/or script url '"
222 << data.script_url()
223 << "' are invalid or have mismatching origins.";
224 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
227 // Convert ServiceWorkerRegistrationData to RegistrationData.
228 out->registration_id = data.registration_id();
229 out->scope = scope_url;
230 out->script = script_url;
231 out->version_id = data.version_id();
232 out->is_active = data.is_active();
233 out->has_fetch_handler = data.has_fetch_handler();
234 out->last_update_check =
235 base::Time::FromInternalValue(data.last_update_check_time());
236 out->resources_total_size_bytes = data.resources_total_size_bytes();
238 return ServiceWorkerDatabase::STATUS_OK;
241 ServiceWorkerDatabase::Status ParseResourceRecord(
242 const std::string& serialized,
243 ServiceWorkerDatabase::ResourceRecord* out) {
244 DCHECK(out);
245 ServiceWorkerResourceRecord record;
246 if (!record.ParseFromString(serialized))
247 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
249 GURL url(record.url());
250 if (!url.is_valid())
251 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
253 // Convert ServiceWorkerResourceRecord to ResourceRecord.
254 out->resource_id = record.resource_id();
255 out->url = url;
256 out->size_bytes = record.size_bytes();
257 return ServiceWorkerDatabase::STATUS_OK;
260 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
261 const leveldb::Status& status) {
262 if (status.ok())
263 return ServiceWorkerDatabase::STATUS_OK;
264 else if (status.IsNotFound())
265 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
266 else if (status.IsIOError())
267 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
268 else if (status.IsCorruption())
269 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
270 else
271 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
274 } // namespace
276 const char* ServiceWorkerDatabase::StatusToString(
277 ServiceWorkerDatabase::Status status) {
278 switch (status) {
279 case ServiceWorkerDatabase::STATUS_OK:
280 return "Database OK";
281 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
282 return "Database not found";
283 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
284 return "Database IO error";
285 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
286 return "Database corrupted";
287 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
288 return "Database operation failed";
289 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
290 NOTREACHED();
291 return "Database unknown error";
293 NOTREACHED();
294 return "Database unknown error";
297 ServiceWorkerDatabase::RegistrationData::RegistrationData()
298 : registration_id(kInvalidServiceWorkerRegistrationId),
299 version_id(kInvalidServiceWorkerVersionId),
300 is_active(false),
301 has_fetch_handler(false),
302 resources_total_size_bytes(0) {
305 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
308 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
309 : path_(path),
310 next_avail_registration_id_(0),
311 next_avail_resource_id_(0),
312 next_avail_version_id_(0),
313 state_(UNINITIALIZED) {
314 sequence_checker_.DetachFromSequence();
317 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
318 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
319 db_.reset();
322 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
323 int64* next_avail_registration_id,
324 int64* next_avail_version_id,
325 int64* next_avail_resource_id) {
326 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
327 DCHECK(next_avail_registration_id);
328 DCHECK(next_avail_version_id);
329 DCHECK(next_avail_resource_id);
331 Status status = LazyOpen(false);
332 if (IsNewOrNonexistentDatabase(status)) {
333 *next_avail_registration_id = 0;
334 *next_avail_version_id = 0;
335 *next_avail_resource_id = 0;
336 return STATUS_OK;
338 if (status != STATUS_OK)
339 return status;
341 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
342 if (status != STATUS_OK)
343 return status;
344 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
345 if (status != STATUS_OK)
346 return status;
347 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
348 if (status != STATUS_OK)
349 return status;
351 *next_avail_registration_id = next_avail_registration_id_;
352 *next_avail_version_id = next_avail_version_id_;
353 *next_avail_resource_id = next_avail_resource_id_;
354 return STATUS_OK;
357 ServiceWorkerDatabase::Status
358 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
359 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
360 DCHECK(origins->empty());
362 Status status = LazyOpen(false);
363 if (IsNewOrNonexistentDatabase(status))
364 return STATUS_OK;
365 if (status != STATUS_OK)
366 return status;
368 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
369 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
370 status = LevelDBStatusToStatus(itr->status());
371 if (status != STATUS_OK) {
372 HandleReadResult(FROM_HERE, status);
373 origins->clear();
374 return status;
377 std::string origin;
378 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
379 break;
380 origins->insert(GURL(origin));
383 HandleReadResult(FROM_HERE, status);
384 return status;
387 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
388 const GURL& origin,
389 std::vector<RegistrationData>* registrations) {
390 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
391 DCHECK(registrations->empty());
393 Status status = LazyOpen(false);
394 if (IsNewOrNonexistentDatabase(status))
395 return STATUS_OK;
396 if (status != STATUS_OK)
397 return status;
399 // Create a key prefix for registrations.
400 std::string prefix = base::StringPrintf(
401 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
403 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
404 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
405 status = LevelDBStatusToStatus(itr->status());
406 if (status != STATUS_OK) {
407 HandleReadResult(FROM_HERE, status);
408 registrations->clear();
409 return status;
412 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
413 break;
415 RegistrationData registration;
416 status = ParseRegistrationData(itr->value().ToString(), &registration);
417 if (status != STATUS_OK) {
418 HandleReadResult(FROM_HERE, status);
419 registrations->clear();
420 return status;
422 registrations->push_back(registration);
425 HandleReadResult(FROM_HERE, status);
426 return status;
429 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
430 std::vector<RegistrationData>* registrations) {
431 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
432 DCHECK(registrations->empty());
434 Status status = LazyOpen(false);
435 if (IsNewOrNonexistentDatabase(status))
436 return STATUS_OK;
437 if (status != STATUS_OK)
438 return status;
440 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
441 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
442 status = LevelDBStatusToStatus(itr->status());
443 if (status != STATUS_OK) {
444 HandleReadResult(FROM_HERE, status);
445 registrations->clear();
446 return status;
449 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
450 break;
452 RegistrationData registration;
453 status = ParseRegistrationData(itr->value().ToString(), &registration);
454 if (status != STATUS_OK) {
455 HandleReadResult(FROM_HERE, status);
456 registrations->clear();
457 return status;
459 registrations->push_back(registration);
462 HandleReadResult(FROM_HERE, status);
463 return status;
466 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
467 int64 registration_id,
468 const GURL& origin,
469 RegistrationData* registration,
470 std::vector<ResourceRecord>* resources) {
471 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
472 DCHECK(registration);
473 DCHECK(resources);
475 Status status = LazyOpen(false);
476 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
477 return status;
479 RegistrationData value;
480 status = ReadRegistrationData(registration_id, origin, &value);
481 if (status != STATUS_OK)
482 return status;
484 status = ReadResourceRecords(value.version_id, resources);
485 if (status != STATUS_OK)
486 return status;
488 *registration = value;
489 return STATUS_OK;
492 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
493 const RegistrationData& registration,
494 const std::vector<ResourceRecord>& resources,
495 RegistrationData* old_registration,
496 std::vector<int64>* newly_purgeable_resources) {
497 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
498 DCHECK(old_registration);
499 Status status = LazyOpen(true);
500 old_registration->version_id = kInvalidServiceWorkerVersionId;
501 if (status != STATUS_OK)
502 return status;
504 leveldb::WriteBatch batch;
505 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
506 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
508 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
509 #if DCHECK_IS_ON
510 int64_t total_size_bytes = 0;
511 for (const auto& resource : resources) {
512 total_size_bytes += resource.size_bytes;
514 DCHECK_EQ(total_size_bytes, registration.resources_total_size_bytes)
515 << "The total size in the registration must match the cumulative "
516 << "sizes of the resources.";
517 #endif
518 PutRegistrationDataToBatch(registration, &batch);
520 // Used for avoiding multiple writes for the same resource id or url.
521 std::set<int64> pushed_resources;
522 std::set<GURL> pushed_urls;
523 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
524 itr != resources.end(); ++itr) {
525 if (!itr->url.is_valid())
526 return STATUS_ERROR_FAILED;
528 // Duplicated resource id or url should not exist.
529 DCHECK(pushed_resources.insert(itr->resource_id).second);
530 DCHECK(pushed_urls.insert(itr->url).second);
532 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
534 // Delete a resource from the uncommitted list.
535 batch.Delete(CreateResourceIdKey(
536 kUncommittedResIdKeyPrefix, itr->resource_id));
537 // Delete from the purgeable list in case this version was once deleted.
538 batch.Delete(
539 CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id));
542 // Retrieve a previous version to sweep purgeable resources.
543 status = ReadRegistrationData(registration.registration_id,
544 registration.scope.GetOrigin(),
545 old_registration);
546 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
547 return status;
548 if (status == STATUS_OK) {
549 DCHECK_LT(old_registration->version_id, registration.version_id);
550 status = DeleteResourceRecords(
551 old_registration->version_id, newly_purgeable_resources, &batch);
552 if (status != STATUS_OK)
553 return status;
555 // Currently resource sharing across versions and registrations is not
556 // supported, so resource ids should not be overlapped between
557 // |registration| and |old_registration|.
558 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
559 newly_purgeable_resources->end());
560 DCHECK(base::STLSetIntersection<std::set<int64> >(
561 pushed_resources, deleted_resources).empty());
564 return WriteBatch(&batch);
567 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
568 int64 registration_id,
569 const GURL& origin) {
570 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
571 Status status = LazyOpen(false);
572 if (IsNewOrNonexistentDatabase(status))
573 return STATUS_ERROR_NOT_FOUND;
574 if (status != STATUS_OK)
575 return status;
576 if (!origin.is_valid())
577 return STATUS_ERROR_FAILED;
579 RegistrationData registration;
580 status = ReadRegistrationData(registration_id, origin, &registration);
581 if (status != STATUS_OK)
582 return status;
584 registration.is_active = true;
586 leveldb::WriteBatch batch;
587 PutRegistrationDataToBatch(registration, &batch);
588 return WriteBatch(&batch);
591 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
592 int64 registration_id,
593 const GURL& origin,
594 const base::Time& time) {
595 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
596 Status status = LazyOpen(false);
597 if (IsNewOrNonexistentDatabase(status))
598 return STATUS_ERROR_NOT_FOUND;
599 if (status != STATUS_OK)
600 return status;
601 if (!origin.is_valid())
602 return STATUS_ERROR_FAILED;
604 RegistrationData registration;
605 status = ReadRegistrationData(registration_id, origin, &registration);
606 if (status != STATUS_OK)
607 return status;
609 registration.last_update_check = time;
611 leveldb::WriteBatch batch;
612 PutRegistrationDataToBatch(registration, &batch);
613 return WriteBatch(&batch);
616 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
617 int64 registration_id,
618 const GURL& origin,
619 RegistrationData* deleted_version,
620 std::vector<int64>* newly_purgeable_resources) {
621 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
622 DCHECK(deleted_version);
623 deleted_version->version_id = kInvalidServiceWorkerVersionId;
624 Status status = LazyOpen(false);
625 if (IsNewOrNonexistentDatabase(status))
626 return STATUS_OK;
627 if (status != STATUS_OK)
628 return status;
629 if (!origin.is_valid())
630 return STATUS_ERROR_FAILED;
632 leveldb::WriteBatch batch;
634 // Remove |origin| from unique origins if a registration specified by
635 // |registration_id| is the only one for |origin|.
636 // TODO(nhiroki): Check the uniqueness by more efficient way.
637 std::vector<RegistrationData> registrations;
638 status = GetRegistrationsForOrigin(origin, &registrations);
639 if (status != STATUS_OK)
640 return status;
642 if (registrations.size() == 1 &&
643 registrations[0].registration_id == registration_id) {
644 batch.Delete(CreateUniqueOriginKey(origin));
647 // Delete a registration specified by |registration_id|.
648 batch.Delete(CreateRegistrationKey(registration_id, origin));
650 // Delete resource records associated with the registration.
651 for (const auto& registration : registrations) {
652 if (registration.registration_id == registration_id) {
653 *deleted_version = registration;
654 status = DeleteResourceRecords(
655 registration.version_id, newly_purgeable_resources, &batch);
656 if (status != STATUS_OK)
657 return status;
658 break;
662 return WriteBatch(&batch);
665 ServiceWorkerDatabase::Status
666 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
667 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
670 ServiceWorkerDatabase::Status
671 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
672 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
675 ServiceWorkerDatabase::Status
676 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
677 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
680 ServiceWorkerDatabase::Status
681 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
682 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
685 ServiceWorkerDatabase::Status
686 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
687 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
690 ServiceWorkerDatabase::Status
691 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
692 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
695 ServiceWorkerDatabase::Status
696 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
697 const std::set<int64>& ids) {
698 leveldb::WriteBatch batch;
699 Status status = DeleteResourceIdsInBatch(
700 kUncommittedResIdKeyPrefix, ids, &batch);
701 if (status != STATUS_OK)
702 return status;
703 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
704 if (status != STATUS_OK)
705 return status;
706 return WriteBatch(&batch);
709 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigins(
710 const std::set<GURL>& origins,
711 std::vector<int64>* newly_purgeable_resources) {
712 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
713 Status status = LazyOpen(false);
714 if (IsNewOrNonexistentDatabase(status))
715 return STATUS_OK;
716 if (status != STATUS_OK)
717 return status;
718 leveldb::WriteBatch batch;
720 for (const GURL& origin : origins) {
721 if (!origin.is_valid())
722 return STATUS_ERROR_FAILED;
724 // Delete from the unique origin list.
725 batch.Delete(CreateUniqueOriginKey(origin));
727 std::vector<RegistrationData> registrations;
728 status = GetRegistrationsForOrigin(origin, &registrations);
729 if (status != STATUS_OK)
730 return status;
732 // Delete registrations and resource records.
733 for (const RegistrationData& data : registrations) {
734 batch.Delete(CreateRegistrationKey(data.registration_id, origin));
735 status = DeleteResourceRecords(
736 data.version_id, newly_purgeable_resources, &batch);
737 if (status != STATUS_OK)
738 return status;
742 return WriteBatch(&batch);
745 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
746 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
747 Disable(FROM_HERE, STATUS_OK);
749 leveldb::Options options;
750 if (path_.empty()) {
751 if (env_) {
752 options.env = env_.get();
753 } else {
754 // In-memory database not initialized.
755 return STATUS_OK;
759 return LevelDBStatusToStatus(
760 leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
763 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
764 bool create_if_missing) {
765 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
767 // Do not try to open a database if we tried and failed once.
768 if (state_ == DISABLED)
769 return STATUS_ERROR_FAILED;
770 if (IsOpen())
771 return STATUS_OK;
773 // When |path_| is empty, open a database in-memory.
774 bool use_in_memory_db = path_.empty();
776 if (!create_if_missing) {
777 // Avoid opening a database if it does not exist at the |path_|.
778 if (use_in_memory_db ||
779 !base::PathExists(path_) ||
780 base::IsDirectoryEmpty(path_)) {
781 return STATUS_ERROR_NOT_FOUND;
785 leveldb::Options options;
786 options.create_if_missing = create_if_missing;
787 if (use_in_memory_db) {
788 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
789 options.env = env_.get();
792 leveldb::DB* db = NULL;
793 Status status = LevelDBStatusToStatus(
794 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
795 HandleOpenResult(FROM_HERE, status);
796 if (status != STATUS_OK) {
797 DCHECK(!db);
798 // TODO(nhiroki): Should we retry to open the database?
799 return status;
801 db_.reset(db);
803 int64 db_version;
804 status = ReadDatabaseVersion(&db_version);
805 if (status != STATUS_OK)
806 return status;
807 DCHECK_LE(0, db_version);
808 if (db_version > 0)
809 state_ = INITIALIZED;
810 return STATUS_OK;
813 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
814 ServiceWorkerDatabase::Status status) {
815 if (status == STATUS_ERROR_NOT_FOUND)
816 return true;
817 if (status == STATUS_OK && state_ == UNINITIALIZED)
818 return true;
819 return false;
822 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
823 const char* id_key,
824 int64* next_avail_id) {
825 DCHECK(id_key);
826 DCHECK(next_avail_id);
828 std::string value;
829 Status status = LevelDBStatusToStatus(
830 db_->Get(leveldb::ReadOptions(), id_key, &value));
831 if (status == STATUS_ERROR_NOT_FOUND) {
832 // Nobody has gotten the next resource id for |id_key|.
833 *next_avail_id = 0;
834 HandleReadResult(FROM_HERE, STATUS_OK);
835 return STATUS_OK;
836 } else if (status != STATUS_OK) {
837 HandleReadResult(FROM_HERE, status);
838 return status;
841 status = ParseId(value, next_avail_id);
842 HandleReadResult(FROM_HERE, status);
843 return status;
846 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
847 int64 registration_id,
848 const GURL& origin,
849 RegistrationData* registration) {
850 DCHECK(registration);
852 const std::string key = CreateRegistrationKey(registration_id, origin);
853 std::string value;
854 Status status = LevelDBStatusToStatus(
855 db_->Get(leveldb::ReadOptions(), key, &value));
856 if (status != STATUS_OK) {
857 HandleReadResult(
858 FROM_HERE,
859 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
860 return status;
863 status = ParseRegistrationData(value, registration);
864 HandleReadResult(FROM_HERE, status);
865 return status;
868 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
869 int64 version_id,
870 std::vector<ResourceRecord>* resources) {
871 DCHECK(resources->empty());
873 Status status = STATUS_OK;
874 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
876 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
877 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
878 Status status = LevelDBStatusToStatus(itr->status());
879 if (status != STATUS_OK) {
880 HandleReadResult(FROM_HERE, status);
881 resources->clear();
882 return status;
885 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
886 break;
888 ResourceRecord resource;
889 status = ParseResourceRecord(itr->value().ToString(), &resource);
890 if (status != STATUS_OK) {
891 HandleReadResult(FROM_HERE, status);
892 resources->clear();
893 return status;
895 resources->push_back(resource);
898 HandleReadResult(FROM_HERE, status);
899 return status;
902 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
903 int64 version_id,
904 std::vector<int64>* newly_purgeable_resources,
905 leveldb::WriteBatch* batch) {
906 DCHECK(batch);
908 Status status = STATUS_OK;
909 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
911 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
912 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
913 status = LevelDBStatusToStatus(itr->status());
914 if (status != STATUS_OK) {
915 HandleReadResult(FROM_HERE, status);
916 return status;
919 const std::string key = itr->key().ToString();
920 std::string unprefixed;
921 if (!RemovePrefix(key, prefix, &unprefixed))
922 break;
924 int64 resource_id;
925 status = ParseId(unprefixed, &resource_id);
926 if (status != STATUS_OK) {
927 HandleReadResult(FROM_HERE, status);
928 return status;
931 // Remove a resource record.
932 batch->Delete(key);
934 // Currently resource sharing across versions and registrations is not
935 // supported, so we can purge this without caring about it.
936 PutPurgeableResourceIdToBatch(resource_id, batch);
937 newly_purgeable_resources->push_back(resource_id);
940 HandleReadResult(FROM_HERE, status);
941 return status;
944 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
945 const char* id_key_prefix,
946 std::set<int64>* ids) {
947 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
948 DCHECK(id_key_prefix);
949 DCHECK(ids->empty());
951 Status status = LazyOpen(false);
952 if (IsNewOrNonexistentDatabase(status))
953 return STATUS_OK;
954 if (status != STATUS_OK)
955 return status;
957 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
958 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
959 status = LevelDBStatusToStatus(itr->status());
960 if (status != STATUS_OK) {
961 HandleReadResult(FROM_HERE, status);
962 ids->clear();
963 return status;
966 std::string unprefixed;
967 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
968 break;
970 int64 resource_id;
971 status = ParseId(unprefixed, &resource_id);
972 if (status != STATUS_OK) {
973 HandleReadResult(FROM_HERE, status);
974 ids->clear();
975 return status;
977 ids->insert(resource_id);
980 HandleReadResult(FROM_HERE, status);
981 return status;
984 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
985 const char* id_key_prefix,
986 const std::set<int64>& ids) {
987 leveldb::WriteBatch batch;
988 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
989 if (status != STATUS_OK)
990 return status;
991 return WriteBatch(&batch);
994 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
995 const char* id_key_prefix,
996 const std::set<int64>& ids,
997 leveldb::WriteBatch* batch) {
998 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
999 DCHECK(id_key_prefix);
1001 Status status = LazyOpen(true);
1002 if (status != STATUS_OK)
1003 return status;
1005 if (ids.empty())
1006 return STATUS_OK;
1007 for (std::set<int64>::const_iterator itr = ids.begin();
1008 itr != ids.end(); ++itr) {
1009 // Value should be empty.
1010 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
1012 // std::set is sorted, so the last element is the largest.
1013 BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
1014 return STATUS_OK;
1017 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
1018 const char* id_key_prefix,
1019 const std::set<int64>& ids) {
1020 leveldb::WriteBatch batch;
1021 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
1022 if (status != STATUS_OK)
1023 return status;
1024 return WriteBatch(&batch);
1027 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1028 const char* id_key_prefix,
1029 const std::set<int64>& ids,
1030 leveldb::WriteBatch* batch) {
1031 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1032 DCHECK(id_key_prefix);
1034 Status status = LazyOpen(false);
1035 if (IsNewOrNonexistentDatabase(status))
1036 return STATUS_OK;
1037 if (status != STATUS_OK)
1038 return status;
1040 for (std::set<int64>::const_iterator itr = ids.begin();
1041 itr != ids.end(); ++itr) {
1042 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1044 return STATUS_OK;
1047 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1048 int64* db_version) {
1049 std::string value;
1050 Status status = LevelDBStatusToStatus(
1051 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1052 if (status == STATUS_ERROR_NOT_FOUND) {
1053 // The database hasn't been initialized yet.
1054 *db_version = 0;
1055 HandleReadResult(FROM_HERE, STATUS_OK);
1056 return STATUS_OK;
1059 if (status != STATUS_OK) {
1060 HandleReadResult(FROM_HERE, status);
1061 return status;
1064 status = ParseDatabaseVersion(value, db_version);
1065 HandleReadResult(FROM_HERE, status);
1066 return status;
1069 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1070 leveldb::WriteBatch* batch) {
1071 DCHECK(batch);
1072 DCHECK_NE(DISABLED, state_);
1074 if (state_ == UNINITIALIZED) {
1075 // Write the database schema version.
1076 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1077 state_ = INITIALIZED;
1080 Status status = LevelDBStatusToStatus(
1081 db_->Write(leveldb::WriteOptions(), batch));
1082 HandleWriteResult(FROM_HERE, status);
1083 return status;
1086 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1087 int64 used_id, leveldb::WriteBatch* batch) {
1088 DCHECK(batch);
1089 if (next_avail_registration_id_ <= used_id) {
1090 next_avail_registration_id_ = used_id + 1;
1091 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1095 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1096 int64 used_id, leveldb::WriteBatch* batch) {
1097 DCHECK(batch);
1098 if (next_avail_resource_id_ <= used_id) {
1099 next_avail_resource_id_ = used_id + 1;
1100 batch->Put(kNextResIdKey, base::Int64ToString(next_avail_resource_id_));
1104 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1105 int64 used_id, leveldb::WriteBatch* batch) {
1106 DCHECK(batch);
1107 if (next_avail_version_id_ <= used_id) {
1108 next_avail_version_id_ = used_id + 1;
1109 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1113 bool ServiceWorkerDatabase::IsOpen() {
1114 return db_ != NULL;
1117 void ServiceWorkerDatabase::Disable(
1118 const tracked_objects::Location& from_here,
1119 Status status) {
1120 if (status != STATUS_OK) {
1121 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1122 << " with error: " << StatusToString(status);
1123 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1125 state_ = DISABLED;
1126 db_.reset();
1129 void ServiceWorkerDatabase::HandleOpenResult(
1130 const tracked_objects::Location& from_here,
1131 Status status) {
1132 if (status != STATUS_OK)
1133 Disable(from_here, status);
1134 ServiceWorkerMetrics::CountOpenDatabaseResult(status);
1137 void ServiceWorkerDatabase::HandleReadResult(
1138 const tracked_objects::Location& from_here,
1139 Status status) {
1140 if (status != STATUS_OK)
1141 Disable(from_here, status);
1142 ServiceWorkerMetrics::CountReadDatabaseResult(status);
1145 void ServiceWorkerDatabase::HandleWriteResult(
1146 const tracked_objects::Location& from_here,
1147 Status status) {
1148 if (status != STATUS_OK)
1149 Disable(from_here, status);
1150 ServiceWorkerMetrics::CountWriteDatabaseResult(status);
1153 } // namespace content