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