Simplify ChildProcessLauncher
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blob5cd3473ebfd88d3f2d7c0b77df7b60cfc80fbf12
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/env_chromium.h"
20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
21 #include "third_party/leveldatabase/src/include/leveldb/db.h"
22 #include "third_party/leveldatabase/src/include/leveldb/env.h"
23 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
25 // LevelDB database schema
26 // =======================
28 // NOTE
29 // - int64 value is serialized as a string by base::Int64ToString().
30 // - GURL value is serialized as a string by GURL::spec().
32 // Version 1 (in sorted order)
33 // key: "INITDATA_DB_VERSION"
34 // value: "1"
36 // key: "INITDATA_NEXT_REGISTRATION_ID"
37 // value: <int64 'next_available_registration_id'>
39 // key: "INITDATA_NEXT_RESOURCE_ID"
40 // value: <int64 'next_available_resource_id'>
42 // key: "INITDATA_NEXT_VERSION_ID"
43 // value: <int64 'next_available_version_id'>
45 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
46 // value: <empty>
48 // key: "PRES:" + <int64 'purgeable_resource_id'>
49 // value: <empty>
51 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
52 // (ex. "REG:http://example.com\x00123456")
53 // value: <ServiceWorkerRegistrationData serialized as a string>
55 // key: "REG_HAS_USER_DATA:" + <std::string 'user_data_name'> + '\x00'
56 // + <int64 'registration_id'>
57 // value: <empty>
59 // key: "REG_USER_DATA:" + <int64 'registration_id'> + '\x00'
60 // + <std::string user_data_name>
61 // (ex. "REG_USER_DATA:123456\x00foo_bar")
62 // value: <std::string user_data>
64 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
65 // (ex. "RES:123456\x00654321")
66 // value: <ServiceWorkerResourceRecord serialized as a string>
68 // key: "URES:" + <int64 'uncommitted_resource_id'>
69 // value: <empty>
71 // Version 2
73 // key: "REGID_TO_ORIGIN:" + <int64 'registration_id'>
74 // value: <GURL 'origin'>
75 namespace content {
77 namespace {
79 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
80 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
81 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
82 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
83 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
85 const char kRegKeyPrefix[] = "REG:";
86 const char kRegUserDataKeyPrefix[] = "REG_USER_DATA:";
87 const char kRegHasUserDataKeyPrefix[] = "REG_HAS_USER_DATA:";
88 const char kRegIdToOriginKeyPrefix[] = "REGID_TO_ORIGIN:";
89 const char kResKeyPrefix[] = "RES:";
90 const char kKeySeparator = '\x00';
92 const char kUncommittedResIdKeyPrefix[] = "URES:";
93 const char kPurgeableResIdKeyPrefix[] = "PRES:";
95 const int64 kCurrentSchemaVersion = 2;
97 bool RemovePrefix(const std::string& str,
98 const std::string& prefix,
99 std::string* out) {
100 if (!StartsWithASCII(str, prefix, true))
101 return false;
102 if (out)
103 *out = str.substr(prefix.size());
104 return true;
107 std::string CreateRegistrationKey(int64 registration_id,
108 const GURL& origin) {
109 return base::StringPrintf("%s%s%c%s",
110 kRegKeyPrefix,
111 origin.spec().c_str(),
112 kKeySeparator,
113 base::Int64ToString(registration_id).c_str());
116 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
117 return base::StringPrintf("%s%s%c",
118 kResKeyPrefix,
119 base::Int64ToString(version_id).c_str(),
120 kKeySeparator);
123 std::string CreateResourceRecordKey(int64 version_id,
124 int64 resource_id) {
125 return CreateResourceRecordKeyPrefix(version_id).append(
126 base::Int64ToString(resource_id));
129 std::string CreateUniqueOriginKey(const GURL& origin) {
130 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
133 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
134 return base::StringPrintf(
135 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
138 std::string CreateUserDataKeyPrefix(int64 registration_id) {
139 return base::StringPrintf("%s%s%c",
140 kRegUserDataKeyPrefix,
141 base::Int64ToString(registration_id).c_str(),
142 kKeySeparator);
145 std::string CreateUserDataKey(int64 registration_id,
146 const std::string& user_data_name) {
147 return CreateUserDataKeyPrefix(registration_id).append(user_data_name);
150 std::string CreateHasUserDataKeyPrefix(const std::string& user_data_name) {
151 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix,
152 user_data_name.c_str(), kKeySeparator);
155 std::string CreateHasUserDataKey(int64 registration_id,
156 const std::string& user_data_name) {
157 return CreateHasUserDataKeyPrefix(user_data_name)
158 .append(base::Int64ToString(registration_id));
161 std::string CreateRegistrationIdToOriginKey(int64 registration_id) {
162 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix,
163 base::Int64ToString(registration_id).c_str());
166 void PutRegistrationDataToBatch(
167 const ServiceWorkerDatabase::RegistrationData& input,
168 leveldb::WriteBatch* batch) {
169 DCHECK(batch);
171 // Convert RegistrationData to ServiceWorkerRegistrationData.
172 ServiceWorkerRegistrationData data;
173 data.set_registration_id(input.registration_id);
174 data.set_scope_url(input.scope.spec());
175 data.set_script_url(input.script.spec());
176 data.set_version_id(input.version_id);
177 data.set_is_active(input.is_active);
178 data.set_has_fetch_handler(input.has_fetch_handler);
179 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
180 data.set_resources_total_size_bytes(input.resources_total_size_bytes);
182 std::string value;
183 bool success = data.SerializeToString(&value);
184 DCHECK(success);
185 GURL origin = input.scope.GetOrigin();
186 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
189 void PutResourceRecordToBatch(
190 const ServiceWorkerDatabase::ResourceRecord& input,
191 int64 version_id,
192 leveldb::WriteBatch* batch) {
193 DCHECK(batch);
194 DCHECK_GE(input.size_bytes, 0);
196 // Convert ResourceRecord to ServiceWorkerResourceRecord.
197 ServiceWorkerResourceRecord record;
198 record.set_resource_id(input.resource_id);
199 record.set_url(input.url.spec());
200 record.set_size_bytes(input.size_bytes);
202 std::string value;
203 bool success = record.SerializeToString(&value);
204 DCHECK(success);
205 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
208 void PutUniqueOriginToBatch(const GURL& origin,
209 leveldb::WriteBatch* batch) {
210 // Value should be empty.
211 batch->Put(CreateUniqueOriginKey(origin), "");
214 void PutPurgeableResourceIdToBatch(int64 resource_id,
215 leveldb::WriteBatch* batch) {
216 // Value should be empty.
217 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
220 ServiceWorkerDatabase::Status ParseId(
221 const std::string& serialized,
222 int64* out) {
223 DCHECK(out);
224 int64 id;
225 if (!base::StringToInt64(serialized, &id) || id < 0)
226 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
227 *out = id;
228 return ServiceWorkerDatabase::STATUS_OK;
231 ServiceWorkerDatabase::Status ParseDatabaseVersion(
232 const std::string& serialized,
233 int64* out) {
234 DCHECK(out);
235 const int kFirstValidVersion = 1;
236 int64 version;
237 if (!base::StringToInt64(serialized, &version) ||
238 version < kFirstValidVersion) {
239 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
241 if (kCurrentSchemaVersion < version) {
242 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
243 << " than the current latest version: "
244 << version << " vs " << kCurrentSchemaVersion;
245 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
247 *out = version;
248 return ServiceWorkerDatabase::STATUS_OK;
251 ServiceWorkerDatabase::Status ParseRegistrationData(
252 const std::string& serialized,
253 ServiceWorkerDatabase::RegistrationData* out) {
254 DCHECK(out);
255 ServiceWorkerRegistrationData data;
256 if (!data.ParseFromString(serialized))
257 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
259 GURL scope_url(data.scope_url());
260 GURL script_url(data.script_url());
261 if (!scope_url.is_valid() ||
262 !script_url.is_valid() ||
263 scope_url.GetOrigin() != script_url.GetOrigin()) {
264 DLOG(ERROR) << "Scope URL '" << data.scope_url() << "' and/or script url '"
265 << data.script_url()
266 << "' are invalid or have mismatching origins.";
267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
270 // Convert ServiceWorkerRegistrationData to RegistrationData.
271 out->registration_id = data.registration_id();
272 out->scope = scope_url;
273 out->script = script_url;
274 out->version_id = data.version_id();
275 out->is_active = data.is_active();
276 out->has_fetch_handler = data.has_fetch_handler();
277 out->last_update_check =
278 base::Time::FromInternalValue(data.last_update_check_time());
279 out->resources_total_size_bytes = data.resources_total_size_bytes();
281 return ServiceWorkerDatabase::STATUS_OK;
284 ServiceWorkerDatabase::Status ParseResourceRecord(
285 const std::string& serialized,
286 ServiceWorkerDatabase::ResourceRecord* out) {
287 DCHECK(out);
288 ServiceWorkerResourceRecord record;
289 if (!record.ParseFromString(serialized))
290 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
292 GURL url(record.url());
293 if (!url.is_valid())
294 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
296 // Convert ServiceWorkerResourceRecord to ResourceRecord.
297 out->resource_id = record.resource_id();
298 out->url = url;
299 out->size_bytes = record.size_bytes();
300 return ServiceWorkerDatabase::STATUS_OK;
303 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
304 const leveldb::Status& status) {
305 if (status.ok())
306 return ServiceWorkerDatabase::STATUS_OK;
307 else if (status.IsNotFound())
308 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
309 else if (status.IsIOError())
310 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
311 else if (status.IsCorruption())
312 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
313 else
314 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
317 } // namespace
319 const char* ServiceWorkerDatabase::StatusToString(
320 ServiceWorkerDatabase::Status status) {
321 switch (status) {
322 case ServiceWorkerDatabase::STATUS_OK:
323 return "Database OK";
324 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
325 return "Database not found";
326 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
327 return "Database IO error";
328 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
329 return "Database corrupted";
330 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
331 return "Database operation failed";
332 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
333 NOTREACHED();
334 return "Database unknown error";
336 NOTREACHED();
337 return "Database unknown error";
340 ServiceWorkerDatabase::RegistrationData::RegistrationData()
341 : registration_id(kInvalidServiceWorkerRegistrationId),
342 version_id(kInvalidServiceWorkerVersionId),
343 is_active(false),
344 has_fetch_handler(false),
345 resources_total_size_bytes(0) {
348 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
351 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
352 : path_(path),
353 next_avail_registration_id_(0),
354 next_avail_resource_id_(0),
355 next_avail_version_id_(0),
356 state_(UNINITIALIZED) {
357 sequence_checker_.DetachFromSequence();
360 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
361 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
362 db_.reset();
365 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
366 int64* next_avail_registration_id,
367 int64* next_avail_version_id,
368 int64* next_avail_resource_id) {
369 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
370 DCHECK(next_avail_registration_id);
371 DCHECK(next_avail_version_id);
372 DCHECK(next_avail_resource_id);
374 Status status = LazyOpen(false);
375 if (IsNewOrNonexistentDatabase(status)) {
376 *next_avail_registration_id = 0;
377 *next_avail_version_id = 0;
378 *next_avail_resource_id = 0;
379 return STATUS_OK;
381 if (status != STATUS_OK)
382 return status;
384 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
385 if (status != STATUS_OK)
386 return status;
387 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
388 if (status != STATUS_OK)
389 return status;
390 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
391 if (status != STATUS_OK)
392 return status;
394 *next_avail_registration_id = next_avail_registration_id_;
395 *next_avail_version_id = next_avail_version_id_;
396 *next_avail_resource_id = next_avail_resource_id_;
397 return STATUS_OK;
400 ServiceWorkerDatabase::Status
401 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
402 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
403 DCHECK(origins->empty());
405 Status status = LazyOpen(false);
406 if (IsNewOrNonexistentDatabase(status))
407 return STATUS_OK;
408 if (status != STATUS_OK)
409 return status;
411 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
412 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
413 status = LevelDBStatusToStatus(itr->status());
414 if (status != STATUS_OK) {
415 HandleReadResult(FROM_HERE, status);
416 origins->clear();
417 return status;
420 std::string origin;
421 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
422 break;
423 origins->insert(GURL(origin));
426 HandleReadResult(FROM_HERE, status);
427 return status;
430 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
431 const GURL& origin,
432 std::vector<RegistrationData>* registrations) {
433 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
434 DCHECK(registrations->empty());
436 Status status = LazyOpen(false);
437 if (IsNewOrNonexistentDatabase(status))
438 return STATUS_OK;
439 if (status != STATUS_OK)
440 return status;
442 // Create a key prefix for registrations.
443 std::string prefix = base::StringPrintf(
444 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
446 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
447 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
448 status = LevelDBStatusToStatus(itr->status());
449 if (status != STATUS_OK) {
450 HandleReadResult(FROM_HERE, status);
451 registrations->clear();
452 return status;
455 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
456 break;
458 RegistrationData registration;
459 status = ParseRegistrationData(itr->value().ToString(), &registration);
460 if (status != STATUS_OK) {
461 HandleReadResult(FROM_HERE, status);
462 registrations->clear();
463 return status;
465 registrations->push_back(registration);
468 HandleReadResult(FROM_HERE, status);
469 return status;
472 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
473 std::vector<RegistrationData>* registrations) {
474 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
475 DCHECK(registrations->empty());
477 Status status = LazyOpen(false);
478 if (IsNewOrNonexistentDatabase(status))
479 return STATUS_OK;
480 if (status != STATUS_OK)
481 return status;
483 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
484 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
485 status = LevelDBStatusToStatus(itr->status());
486 if (status != STATUS_OK) {
487 HandleReadResult(FROM_HERE, status);
488 registrations->clear();
489 return status;
492 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
493 break;
495 RegistrationData registration;
496 status = ParseRegistrationData(itr->value().ToString(), &registration);
497 if (status != STATUS_OK) {
498 HandleReadResult(FROM_HERE, status);
499 registrations->clear();
500 return status;
502 registrations->push_back(registration);
505 HandleReadResult(FROM_HERE, status);
506 return status;
509 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
510 int64 registration_id,
511 const GURL& origin,
512 RegistrationData* registration,
513 std::vector<ResourceRecord>* resources) {
514 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
515 DCHECK(registration);
516 DCHECK(resources);
518 Status status = LazyOpen(false);
519 if (IsNewOrNonexistentDatabase(status))
520 return STATUS_ERROR_NOT_FOUND;
521 if (status != STATUS_OK)
522 return status;
524 RegistrationData value;
525 status = ReadRegistrationData(registration_id, origin, &value);
526 if (status != STATUS_OK)
527 return status;
529 status = ReadResourceRecords(value.version_id, resources);
530 if (status != STATUS_OK)
531 return status;
533 *registration = value;
534 return STATUS_OK;
537 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationOrigin(
538 int64 registration_id,
539 GURL* origin) {
540 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
541 DCHECK(origin);
543 Status status = LazyOpen(true);
544 if (IsNewOrNonexistentDatabase(status))
545 return STATUS_ERROR_NOT_FOUND;
546 if (status != STATUS_OK)
547 return status;
549 std::string value;
550 status = LevelDBStatusToStatus(
551 db_->Get(leveldb::ReadOptions(),
552 CreateRegistrationIdToOriginKey(registration_id), &value));
553 if (status != STATUS_OK) {
554 HandleReadResult(FROM_HERE,
555 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
556 return status;
559 GURL parsed(value);
560 if (!parsed.is_valid()) {
561 status = STATUS_ERROR_CORRUPTED;
562 HandleReadResult(FROM_HERE, status);
563 return status;
566 *origin = parsed;
567 HandleReadResult(FROM_HERE, STATUS_OK);
568 return STATUS_OK;
571 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
572 const RegistrationData& registration,
573 const std::vector<ResourceRecord>& resources,
574 RegistrationData* old_registration,
575 std::vector<int64>* newly_purgeable_resources) {
576 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
577 DCHECK(old_registration);
578 Status status = LazyOpen(true);
579 old_registration->version_id = kInvalidServiceWorkerVersionId;
580 if (status != STATUS_OK)
581 return status;
583 leveldb::WriteBatch batch;
584 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
585 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
587 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
588 #if DCHECK_IS_ON()
589 int64_t total_size_bytes = 0;
590 for (const auto& resource : resources) {
591 total_size_bytes += resource.size_bytes;
593 DCHECK_EQ(total_size_bytes, registration.resources_total_size_bytes)
594 << "The total size in the registration must match the cumulative "
595 << "sizes of the resources.";
596 #endif
597 PutRegistrationDataToBatch(registration, &batch);
598 batch.Put(CreateRegistrationIdToOriginKey(registration.registration_id),
599 registration.scope.GetOrigin().spec());
601 // Used for avoiding multiple writes for the same resource id or url.
602 std::set<int64> pushed_resources;
603 std::set<GURL> pushed_urls;
604 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
605 itr != resources.end(); ++itr) {
606 if (!itr->url.is_valid())
607 return STATUS_ERROR_FAILED;
609 // Duplicated resource id or url should not exist.
610 DCHECK(pushed_resources.insert(itr->resource_id).second);
611 DCHECK(pushed_urls.insert(itr->url).second);
613 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
615 // Delete a resource from the uncommitted list.
616 batch.Delete(CreateResourceIdKey(
617 kUncommittedResIdKeyPrefix, itr->resource_id));
618 // Delete from the purgeable list in case this version was once deleted.
619 batch.Delete(
620 CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id));
623 // Retrieve a previous version to sweep purgeable resources.
624 status = ReadRegistrationData(registration.registration_id,
625 registration.scope.GetOrigin(),
626 old_registration);
627 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
628 return status;
629 if (status == STATUS_OK) {
630 DCHECK_LT(old_registration->version_id, registration.version_id);
631 status = DeleteResourceRecords(
632 old_registration->version_id, newly_purgeable_resources, &batch);
633 if (status != STATUS_OK)
634 return status;
636 // Currently resource sharing across versions and registrations is not
637 // supported, so resource ids should not be overlapped between
638 // |registration| and |old_registration|.
639 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
640 newly_purgeable_resources->end());
641 DCHECK(base::STLSetIntersection<std::set<int64> >(
642 pushed_resources, deleted_resources).empty());
645 return WriteBatch(&batch);
648 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
649 int64 registration_id,
650 const GURL& origin) {
651 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
652 Status status = LazyOpen(false);
653 if (IsNewOrNonexistentDatabase(status))
654 return STATUS_ERROR_NOT_FOUND;
655 if (status != STATUS_OK)
656 return status;
657 if (!origin.is_valid())
658 return STATUS_ERROR_FAILED;
660 RegistrationData registration;
661 status = ReadRegistrationData(registration_id, origin, &registration);
662 if (status != STATUS_OK)
663 return status;
665 registration.is_active = true;
667 leveldb::WriteBatch batch;
668 PutRegistrationDataToBatch(registration, &batch);
669 return WriteBatch(&batch);
672 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
673 int64 registration_id,
674 const GURL& origin,
675 const base::Time& time) {
676 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
677 Status status = LazyOpen(false);
678 if (IsNewOrNonexistentDatabase(status))
679 return STATUS_ERROR_NOT_FOUND;
680 if (status != STATUS_OK)
681 return status;
682 if (!origin.is_valid())
683 return STATUS_ERROR_FAILED;
685 RegistrationData registration;
686 status = ReadRegistrationData(registration_id, origin, &registration);
687 if (status != STATUS_OK)
688 return status;
690 registration.last_update_check = time;
692 leveldb::WriteBatch batch;
693 PutRegistrationDataToBatch(registration, &batch);
694 return WriteBatch(&batch);
697 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
698 int64 registration_id,
699 const GURL& origin,
700 RegistrationData* deleted_version,
701 std::vector<int64>* newly_purgeable_resources) {
702 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
703 DCHECK(deleted_version);
704 deleted_version->version_id = kInvalidServiceWorkerVersionId;
705 Status status = LazyOpen(false);
706 if (IsNewOrNonexistentDatabase(status))
707 return STATUS_OK;
708 if (status != STATUS_OK)
709 return status;
710 if (!origin.is_valid())
711 return STATUS_ERROR_FAILED;
713 leveldb::WriteBatch batch;
715 // Remove |origin| from unique origins if a registration specified by
716 // |registration_id| is the only one for |origin|.
717 // TODO(nhiroki): Check the uniqueness by more efficient way.
718 std::vector<RegistrationData> registrations;
719 status = GetRegistrationsForOrigin(origin, &registrations);
720 if (status != STATUS_OK)
721 return status;
723 if (registrations.size() == 1 &&
724 registrations[0].registration_id == registration_id) {
725 batch.Delete(CreateUniqueOriginKey(origin));
728 // Delete a registration specified by |registration_id|.
729 batch.Delete(CreateRegistrationKey(registration_id, origin));
730 batch.Delete(CreateRegistrationIdToOriginKey(registration_id));
732 // Delete resource records and user data associated with the registration.
733 for (const auto& registration : registrations) {
734 if (registration.registration_id == registration_id) {
735 *deleted_version = registration;
736 status = DeleteResourceRecords(
737 registration.version_id, newly_purgeable_resources, &batch);
738 if (status != STATUS_OK)
739 return status;
741 status = DeleteUserDataForRegistration(registration_id, &batch);
742 if (status != STATUS_OK)
743 return status;
744 break;
748 return WriteBatch(&batch);
751 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadUserData(
752 int64 registration_id,
753 const std::string& user_data_name,
754 std::string* user_data) {
755 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
756 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
757 DCHECK(!user_data_name.empty());
758 DCHECK(user_data);
760 Status status = LazyOpen(false);
761 if (IsNewOrNonexistentDatabase(status))
762 return STATUS_ERROR_NOT_FOUND;
763 if (status != STATUS_OK)
764 return status;
766 const std::string key = CreateUserDataKey(registration_id, user_data_name);
767 status = LevelDBStatusToStatus(
768 db_->Get(leveldb::ReadOptions(), key, user_data));
769 HandleReadResult(FROM_HERE,
770 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
771 return status;
774 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteUserData(
775 int64 registration_id,
776 const GURL& origin,
777 const std::string& user_data_name,
778 const std::string& user_data) {
779 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
780 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
781 DCHECK(!user_data_name.empty());
783 Status status = LazyOpen(false);
784 if (IsNewOrNonexistentDatabase(status))
785 return STATUS_ERROR_NOT_FOUND;
786 if (status != STATUS_OK)
787 return status;
789 // There should be the registration specified by |registration_id|.
790 RegistrationData registration;
791 status = ReadRegistrationData(registration_id, origin, &registration);
792 if (status != STATUS_OK)
793 return status;
795 leveldb::WriteBatch batch;
796 batch.Put(CreateUserDataKey(registration_id, user_data_name), user_data);
797 batch.Put(CreateHasUserDataKey(registration_id, user_data_name), "");
798 return WriteBatch(&batch);
801 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteUserData(
802 int64 registration_id,
803 const std::string& user_data_name) {
804 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
805 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
806 DCHECK(!user_data_name.empty());
808 Status status = LazyOpen(false);
809 if (IsNewOrNonexistentDatabase(status))
810 return STATUS_OK;
811 if (status != STATUS_OK)
812 return status;
814 leveldb::WriteBatch batch;
815 batch.Delete(CreateUserDataKey(registration_id, user_data_name));
816 batch.Delete(CreateHasUserDataKey(registration_id, user_data_name));
817 return WriteBatch(&batch);
820 ServiceWorkerDatabase::Status
821 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
822 const std::string& user_data_name,
823 std::vector<std::pair<int64, std::string>>* user_data) {
824 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
825 DCHECK(user_data->empty());
827 Status status = LazyOpen(false);
828 if (IsNewOrNonexistentDatabase(status))
829 return STATUS_OK;
830 if (status != STATUS_OK)
831 return status;
833 std::string key_prefix = CreateHasUserDataKeyPrefix(user_data_name);
834 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
835 for (itr->Seek(key_prefix); itr->Valid(); itr->Next()) {
836 status = LevelDBStatusToStatus(itr->status());
837 if (status != STATUS_OK) {
838 HandleReadResult(FROM_HERE, status);
839 user_data->clear();
840 return status;
843 std::string registration_id_string;
844 if (!RemovePrefix(itr->key().ToString(), key_prefix,
845 &registration_id_string)) {
846 break;
849 int64 registration_id;
850 status = ParseId(registration_id_string, &registration_id);
851 if (status != STATUS_OK) {
852 HandleReadResult(FROM_HERE, status);
853 user_data->clear();
854 return status;
857 std::string value;
858 status = LevelDBStatusToStatus(
859 db_->Get(leveldb::ReadOptions(),
860 CreateUserDataKey(registration_id, user_data_name), &value));
861 if (status != STATUS_OK) {
862 HandleReadResult(FROM_HERE, status);
863 user_data->clear();
864 return status;
866 user_data->push_back(std::make_pair(registration_id, value));
869 HandleReadResult(FROM_HERE, status);
870 return status;
873 ServiceWorkerDatabase::Status
874 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
875 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
878 ServiceWorkerDatabase::Status
879 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
880 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
883 ServiceWorkerDatabase::Status
884 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
885 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
888 ServiceWorkerDatabase::Status
889 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
890 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
893 ServiceWorkerDatabase::Status
894 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
895 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
898 ServiceWorkerDatabase::Status
899 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
900 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
903 ServiceWorkerDatabase::Status
904 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
905 const std::set<int64>& ids) {
906 leveldb::WriteBatch batch;
907 Status status = DeleteResourceIdsInBatch(
908 kUncommittedResIdKeyPrefix, ids, &batch);
909 if (status != STATUS_OK)
910 return status;
911 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
912 if (status != STATUS_OK)
913 return status;
914 return WriteBatch(&batch);
917 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigins(
918 const std::set<GURL>& origins,
919 std::vector<int64>* newly_purgeable_resources) {
920 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
921 Status status = LazyOpen(false);
922 if (IsNewOrNonexistentDatabase(status))
923 return STATUS_OK;
924 if (status != STATUS_OK)
925 return status;
926 leveldb::WriteBatch batch;
928 for (const GURL& origin : origins) {
929 if (!origin.is_valid())
930 return STATUS_ERROR_FAILED;
932 // Delete from the unique origin list.
933 batch.Delete(CreateUniqueOriginKey(origin));
935 std::vector<RegistrationData> registrations;
936 status = GetRegistrationsForOrigin(origin, &registrations);
937 if (status != STATUS_OK)
938 return status;
940 // Delete registrations, resource records and user data.
941 for (const RegistrationData& data : registrations) {
942 batch.Delete(CreateRegistrationKey(data.registration_id, origin));
943 batch.Delete(CreateRegistrationIdToOriginKey(data.registration_id));
945 status = DeleteResourceRecords(
946 data.version_id, newly_purgeable_resources, &batch);
947 if (status != STATUS_OK)
948 return status;
950 status = DeleteUserDataForRegistration(data.registration_id, &batch);
951 if (status != STATUS_OK)
952 return status;
956 return WriteBatch(&batch);
959 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
960 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
961 Disable(FROM_HERE, STATUS_OK);
963 leveldb::Options options;
964 if (path_.empty()) {
965 if (env_) {
966 options.env = env_.get();
967 } else {
968 // In-memory database not initialized.
969 return STATUS_OK;
973 Status status =
974 LevelDBStatusToStatus(leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
975 ServiceWorkerMetrics::RecordDestroyDatabaseResult(status);
976 return status;
979 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
980 bool create_if_missing) {
981 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
983 // Do not try to open a database if we tried and failed once.
984 if (state_ == DISABLED)
985 return STATUS_ERROR_FAILED;
986 if (IsOpen())
987 return STATUS_OK;
989 // When |path_| is empty, open a database in-memory.
990 bool use_in_memory_db = path_.empty();
992 if (!create_if_missing) {
993 // Avoid opening a database if it does not exist at the |path_|.
994 if (use_in_memory_db ||
995 !base::PathExists(path_) ||
996 base::IsDirectoryEmpty(path_)) {
997 return STATUS_ERROR_NOT_FOUND;
1001 leveldb::Options options;
1002 options.create_if_missing = create_if_missing;
1003 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
1004 if (use_in_memory_db) {
1005 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1006 options.env = env_.get();
1009 leveldb::DB* db = NULL;
1010 Status status = LevelDBStatusToStatus(
1011 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
1012 HandleOpenResult(FROM_HERE, status);
1013 if (status != STATUS_OK) {
1014 DCHECK(!db);
1015 // TODO(nhiroki): Should we retry to open the database?
1016 return status;
1018 db_.reset(db);
1020 int64 db_version;
1021 status = ReadDatabaseVersion(&db_version);
1022 if (status != STATUS_OK)
1023 return status;
1024 DCHECK_LE(0, db_version);
1026 if (db_version > 0 && db_version < kCurrentSchemaVersion) {
1027 switch (db_version) {
1028 case 1:
1029 status = UpgradeDatabaseSchemaFromV1ToV2();
1030 if (status != STATUS_OK)
1031 return status;
1032 db_version = 2;
1033 // Intentionally fall-through to other version upgrade cases.
1035 // Either the database got upgraded to the current schema version, or some
1036 // upgrade step failed which would have caused this method to abort.
1037 DCHECK_EQ(db_version, kCurrentSchemaVersion);
1040 if (db_version > 0)
1041 state_ = INITIALIZED;
1042 return STATUS_OK;
1045 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1046 ServiceWorkerDatabase::Status status) {
1047 if (status == STATUS_ERROR_NOT_FOUND)
1048 return true;
1049 if (status == STATUS_OK && state_ == UNINITIALIZED)
1050 return true;
1051 return false;
1054 ServiceWorkerDatabase::Status
1055 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1056 Status status = STATUS_OK;
1057 leveldb::WriteBatch batch;
1059 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1060 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1061 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
1062 status = LevelDBStatusToStatus(itr->status());
1063 if (status != STATUS_OK) {
1064 HandleReadResult(FROM_HERE, status);
1065 return status;
1068 std::string key;
1069 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, &key))
1070 break;
1072 std::vector<std::string> parts;
1073 base::SplitStringDontTrim(key, kKeySeparator, &parts);
1074 if (parts.size() != 2) {
1075 status = STATUS_ERROR_CORRUPTED;
1076 HandleReadResult(FROM_HERE, status);
1077 return status;
1080 int64 registration_id;
1081 status = ParseId(parts[1], &registration_id);
1082 if (status != STATUS_OK) {
1083 HandleReadResult(FROM_HERE, status);
1084 return status;
1087 batch.Put(CreateRegistrationIdToOriginKey(registration_id), parts[0]);
1090 // Update schema version manually instead of relying on WriteBatch to make
1091 // sure each upgrade step only updates it to the actually correct version.
1092 batch.Put(kDatabaseVersionKey, base::Int64ToString(2));
1093 status = LevelDBStatusToStatus(
1094 db_->Write(leveldb::WriteOptions(), &batch));
1095 HandleWriteResult(FROM_HERE, status);
1096 return status;
1099 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
1100 const char* id_key,
1101 int64* next_avail_id) {
1102 DCHECK(id_key);
1103 DCHECK(next_avail_id);
1105 std::string value;
1106 Status status = LevelDBStatusToStatus(
1107 db_->Get(leveldb::ReadOptions(), id_key, &value));
1108 if (status == STATUS_ERROR_NOT_FOUND) {
1109 // Nobody has gotten the next resource id for |id_key|.
1110 *next_avail_id = 0;
1111 HandleReadResult(FROM_HERE, STATUS_OK);
1112 return STATUS_OK;
1113 } else if (status != STATUS_OK) {
1114 HandleReadResult(FROM_HERE, status);
1115 return status;
1118 status = ParseId(value, next_avail_id);
1119 HandleReadResult(FROM_HERE, status);
1120 return status;
1123 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
1124 int64 registration_id,
1125 const GURL& origin,
1126 RegistrationData* registration) {
1127 DCHECK(registration);
1129 const std::string key = CreateRegistrationKey(registration_id, origin);
1130 std::string value;
1131 Status status = LevelDBStatusToStatus(
1132 db_->Get(leveldb::ReadOptions(), key, &value));
1133 if (status != STATUS_OK) {
1134 HandleReadResult(
1135 FROM_HERE,
1136 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
1137 return status;
1140 status = ParseRegistrationData(value, registration);
1141 HandleReadResult(FROM_HERE, status);
1142 return status;
1145 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
1146 int64 version_id,
1147 std::vector<ResourceRecord>* resources) {
1148 DCHECK(resources->empty());
1150 Status status = STATUS_OK;
1151 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1153 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1154 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1155 Status status = LevelDBStatusToStatus(itr->status());
1156 if (status != STATUS_OK) {
1157 HandleReadResult(FROM_HERE, status);
1158 resources->clear();
1159 return status;
1162 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
1163 break;
1165 ResourceRecord resource;
1166 status = ParseResourceRecord(itr->value().ToString(), &resource);
1167 if (status != STATUS_OK) {
1168 HandleReadResult(FROM_HERE, status);
1169 resources->clear();
1170 return status;
1172 resources->push_back(resource);
1175 HandleReadResult(FROM_HERE, status);
1176 return status;
1179 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
1180 int64 version_id,
1181 std::vector<int64>* newly_purgeable_resources,
1182 leveldb::WriteBatch* batch) {
1183 DCHECK(batch);
1185 Status status = STATUS_OK;
1186 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1188 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1189 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1190 status = LevelDBStatusToStatus(itr->status());
1191 if (status != STATUS_OK) {
1192 HandleReadResult(FROM_HERE, status);
1193 return status;
1196 const std::string key = itr->key().ToString();
1197 std::string unprefixed;
1198 if (!RemovePrefix(key, prefix, &unprefixed))
1199 break;
1201 int64 resource_id;
1202 status = ParseId(unprefixed, &resource_id);
1203 if (status != STATUS_OK) {
1204 HandleReadResult(FROM_HERE, status);
1205 return status;
1208 // Remove a resource record.
1209 batch->Delete(key);
1211 // Currently resource sharing across versions and registrations is not
1212 // supported, so we can purge this without caring about it.
1213 PutPurgeableResourceIdToBatch(resource_id, batch);
1214 newly_purgeable_resources->push_back(resource_id);
1217 HandleReadResult(FROM_HERE, status);
1218 return status;
1221 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
1222 const char* id_key_prefix,
1223 std::set<int64>* ids) {
1224 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1225 DCHECK(id_key_prefix);
1226 DCHECK(ids->empty());
1228 Status status = LazyOpen(false);
1229 if (IsNewOrNonexistentDatabase(status))
1230 return STATUS_OK;
1231 if (status != STATUS_OK)
1232 return status;
1234 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1235 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
1236 status = LevelDBStatusToStatus(itr->status());
1237 if (status != STATUS_OK) {
1238 HandleReadResult(FROM_HERE, status);
1239 ids->clear();
1240 return status;
1243 std::string unprefixed;
1244 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
1245 break;
1247 int64 resource_id;
1248 status = ParseId(unprefixed, &resource_id);
1249 if (status != STATUS_OK) {
1250 HandleReadResult(FROM_HERE, status);
1251 ids->clear();
1252 return status;
1254 ids->insert(resource_id);
1257 HandleReadResult(FROM_HERE, status);
1258 return status;
1261 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
1262 const char* id_key_prefix,
1263 const std::set<int64>& ids) {
1264 leveldb::WriteBatch batch;
1265 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
1266 if (status != STATUS_OK)
1267 return status;
1268 return WriteBatch(&batch);
1271 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
1272 const char* id_key_prefix,
1273 const std::set<int64>& ids,
1274 leveldb::WriteBatch* batch) {
1275 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1276 DCHECK(id_key_prefix);
1278 Status status = LazyOpen(true);
1279 if (status != STATUS_OK)
1280 return status;
1282 if (ids.empty())
1283 return STATUS_OK;
1284 for (std::set<int64>::const_iterator itr = ids.begin();
1285 itr != ids.end(); ++itr) {
1286 // Value should be empty.
1287 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
1289 // std::set is sorted, so the last element is the largest.
1290 BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
1291 return STATUS_OK;
1294 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
1295 const char* id_key_prefix,
1296 const std::set<int64>& ids) {
1297 leveldb::WriteBatch batch;
1298 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
1299 if (status != STATUS_OK)
1300 return status;
1301 return WriteBatch(&batch);
1304 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1305 const char* id_key_prefix,
1306 const std::set<int64>& ids,
1307 leveldb::WriteBatch* batch) {
1308 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1309 DCHECK(id_key_prefix);
1311 Status status = LazyOpen(false);
1312 if (IsNewOrNonexistentDatabase(status))
1313 return STATUS_OK;
1314 if (status != STATUS_OK)
1315 return status;
1317 for (std::set<int64>::const_iterator itr = ids.begin();
1318 itr != ids.end(); ++itr) {
1319 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1321 return STATUS_OK;
1324 ServiceWorkerDatabase::Status
1325 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1326 int64 registration_id,
1327 leveldb::WriteBatch* batch) {
1328 DCHECK(batch);
1329 Status status = STATUS_OK;
1330 const std::string prefix = CreateUserDataKeyPrefix(registration_id);
1332 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1333 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1334 status = LevelDBStatusToStatus(itr->status());
1335 if (status != STATUS_OK) {
1336 HandleReadResult(FROM_HERE, status);
1337 return status;
1340 const std::string key = itr->key().ToString();
1341 std::string user_data_name;
1342 if (!RemovePrefix(key, prefix, &user_data_name))
1343 break;
1344 batch->Delete(key);
1345 batch->Delete(CreateHasUserDataKey(registration_id, user_data_name));
1347 return status;
1350 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1351 int64* db_version) {
1352 std::string value;
1353 Status status = LevelDBStatusToStatus(
1354 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1355 if (status == STATUS_ERROR_NOT_FOUND) {
1356 // The database hasn't been initialized yet.
1357 *db_version = 0;
1358 HandleReadResult(FROM_HERE, STATUS_OK);
1359 return STATUS_OK;
1362 if (status != STATUS_OK) {
1363 HandleReadResult(FROM_HERE, status);
1364 return status;
1367 status = ParseDatabaseVersion(value, db_version);
1368 HandleReadResult(FROM_HERE, status);
1369 return status;
1372 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1373 leveldb::WriteBatch* batch) {
1374 DCHECK(batch);
1375 DCHECK_NE(DISABLED, state_);
1377 if (state_ == UNINITIALIZED) {
1378 // Write the database schema version.
1379 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1380 state_ = INITIALIZED;
1383 Status status = LevelDBStatusToStatus(
1384 db_->Write(leveldb::WriteOptions(), batch));
1385 HandleWriteResult(FROM_HERE, status);
1386 return status;
1389 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1390 int64 used_id, leveldb::WriteBatch* batch) {
1391 DCHECK(batch);
1392 if (next_avail_registration_id_ <= used_id) {
1393 next_avail_registration_id_ = used_id + 1;
1394 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1398 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1399 int64 used_id, leveldb::WriteBatch* batch) {
1400 DCHECK(batch);
1401 if (next_avail_resource_id_ <= used_id) {
1402 next_avail_resource_id_ = used_id + 1;
1403 batch->Put(kNextResIdKey, base::Int64ToString(next_avail_resource_id_));
1407 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1408 int64 used_id, leveldb::WriteBatch* batch) {
1409 DCHECK(batch);
1410 if (next_avail_version_id_ <= used_id) {
1411 next_avail_version_id_ = used_id + 1;
1412 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1416 bool ServiceWorkerDatabase::IsOpen() {
1417 return db_ != NULL;
1420 void ServiceWorkerDatabase::Disable(
1421 const tracked_objects::Location& from_here,
1422 Status status) {
1423 if (status != STATUS_OK) {
1424 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1425 << " with error: " << StatusToString(status);
1426 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1428 state_ = DISABLED;
1429 db_.reset();
1432 void ServiceWorkerDatabase::HandleOpenResult(
1433 const tracked_objects::Location& from_here,
1434 Status status) {
1435 if (status != STATUS_OK)
1436 Disable(from_here, status);
1437 ServiceWorkerMetrics::CountOpenDatabaseResult(status);
1440 void ServiceWorkerDatabase::HandleReadResult(
1441 const tracked_objects::Location& from_here,
1442 Status status) {
1443 if (status != STATUS_OK)
1444 Disable(from_here, status);
1445 ServiceWorkerMetrics::CountReadDatabaseResult(status);
1448 void ServiceWorkerDatabase::HandleWriteResult(
1449 const tracked_objects::Location& from_here,
1450 Status status) {
1451 if (status != STATUS_OK)
1452 Disable(from_here, status);
1453 ServiceWorkerMetrics::CountWriteDatabaseResult(status);
1456 } // namespace content