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