Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blobb79894ea803f780814b0ea13c17f2e17424ff021
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_database.h"
7 #include "base/files/file_util.h"
8 #include "base/lazy_instance.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "content/browser/service_worker/service_worker_database.pb.h"
18 #include "content/browser/service_worker/service_worker_metrics.h"
19 #include "content/common/service_worker/service_worker_types.h"
20 #include "third_party/leveldatabase/env_chromium.h"
21 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
22 #include "third_party/leveldatabase/src/include/leveldb/db.h"
23 #include "third_party/leveldatabase/src/include/leveldb/env.h"
24 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
26 // LevelDB database schema
27 // =======================
29 // NOTE
30 // - int64 value is serialized as a string by base::Int64ToString().
31 // - GURL value is serialized as a string by GURL::spec().
33 // Version 1 (in sorted order)
34 // key: "INITDATA_DB_VERSION"
35 // value: "1"
37 // key: "INITDATA_NEXT_REGISTRATION_ID"
38 // value: <int64 'next_available_registration_id'>
40 // key: "INITDATA_NEXT_RESOURCE_ID"
41 // value: <int64 'next_available_resource_id'>
43 // key: "INITDATA_NEXT_VERSION_ID"
44 // value: <int64 'next_available_version_id'>
46 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
47 // value: <empty>
49 // key: "PRES:" + <int64 'purgeable_resource_id'>
50 // value: <empty>
52 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
53 // (ex. "REG:http://example.com\x00123456")
54 // value: <ServiceWorkerRegistrationData serialized as a string>
56 // key: "REG_HAS_USER_DATA:" + <std::string 'user_data_name'> + '\x00'
57 // + <int64 'registration_id'>
58 // value: <empty>
60 // key: "REG_USER_DATA:" + <int64 'registration_id'> + '\x00'
61 // + <std::string user_data_name>
62 // (ex. "REG_USER_DATA:123456\x00foo_bar")
63 // value: <std::string user_data>
65 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
66 // (ex. "RES:123456\x00654321")
67 // value: <ServiceWorkerResourceRecord serialized as a string>
69 // key: "URES:" + <int64 'uncommitted_resource_id'>
70 // value: <empty>
72 // Version 2
74 // key: "REGID_TO_ORIGIN:" + <int64 'registration_id'>
75 // value: <GURL 'origin'>
77 // key: "INITDATA_DISKCACHE_MIGRATION_NOT_NEEDED"
78 // value: <empty>
79 // - This entry represents that the diskcache uses the Simple backend and
80 // does not have to do diskcache migration (http://crbug.com/487482).
82 // key: "INITDATA_OLD_DISKCACHE_DELETION_NOT_NEEDED"
83 // value: <empty>
84 // - This entry represents that the old BlockFile diskcache was deleted
85 // after diskcache migration (http://crbug.com/487482).
86 namespace content {
88 namespace {
90 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
91 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
92 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
93 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
94 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
95 const char kDiskCacheMigrationNotNeededKey[] =
96 "INITDATA_DISKCACHE_MIGRATION_NOT_NEEDED";
97 const char kOldDiskCacheDeletionNotNeededKey[] =
98 "INITDATA_OLD_DISKCACHE_DELETION_NOT_NEEDED";
100 const char kRegKeyPrefix[] = "REG:";
101 const char kRegUserDataKeyPrefix[] = "REG_USER_DATA:";
102 const char kRegHasUserDataKeyPrefix[] = "REG_HAS_USER_DATA:";
103 const char kRegIdToOriginKeyPrefix[] = "REGID_TO_ORIGIN:";
104 const char kResKeyPrefix[] = "RES:";
105 const char kKeySeparator = '\x00';
106 const char kEmptyValue[] = "";
108 const char kUncommittedResIdKeyPrefix[] = "URES:";
109 const char kPurgeableResIdKeyPrefix[] = "PRES:";
111 const int64 kCurrentSchemaVersion = 2;
113 class ServiceWorkerEnv : public leveldb_env::ChromiumEnv {
114 public:
115 ServiceWorkerEnv()
116 : ChromiumEnv("LevelDBEnv.ServiceWorker", false /* make_backup */) {}
119 base::LazyInstance<ServiceWorkerEnv>::Leaky g_service_worker_env =
120 LAZY_INSTANCE_INITIALIZER;
122 bool RemovePrefix(const std::string& str,
123 const std::string& prefix,
124 std::string* out) {
125 if (!base::StartsWith(str, prefix, base::CompareCase::SENSITIVE))
126 return false;
127 if (out)
128 *out = str.substr(prefix.size());
129 return true;
132 std::string CreateRegistrationKeyPrefix(const GURL& origin) {
133 return base::StringPrintf("%s%s%c", kRegKeyPrefix,
134 origin.GetOrigin().spec().c_str(), kKeySeparator);
137 std::string CreateRegistrationKey(int64 registration_id,
138 const GURL& origin) {
139 return CreateRegistrationKeyPrefix(origin)
140 .append(base::Int64ToString(registration_id));
143 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
144 return base::StringPrintf("%s%s%c",
145 kResKeyPrefix,
146 base::Int64ToString(version_id).c_str(),
147 kKeySeparator);
150 std::string CreateResourceRecordKey(int64 version_id,
151 int64 resource_id) {
152 return CreateResourceRecordKeyPrefix(version_id).append(
153 base::Int64ToString(resource_id));
156 std::string CreateUniqueOriginKey(const GURL& origin) {
157 return base::StringPrintf("%s%s", kUniqueOriginKey,
158 origin.GetOrigin().spec().c_str());
161 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
162 return base::StringPrintf(
163 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
166 std::string CreateUserDataKeyPrefix(int64 registration_id) {
167 return base::StringPrintf("%s%s%c",
168 kRegUserDataKeyPrefix,
169 base::Int64ToString(registration_id).c_str(),
170 kKeySeparator);
173 std::string CreateUserDataKey(int64 registration_id,
174 const std::string& user_data_name) {
175 return CreateUserDataKeyPrefix(registration_id).append(user_data_name);
178 std::string CreateHasUserDataKeyPrefix(const std::string& user_data_name) {
179 return base::StringPrintf("%s%s%c", kRegHasUserDataKeyPrefix,
180 user_data_name.c_str(), kKeySeparator);
183 std::string CreateHasUserDataKey(int64 registration_id,
184 const std::string& user_data_name) {
185 return CreateHasUserDataKeyPrefix(user_data_name)
186 .append(base::Int64ToString(registration_id));
189 std::string CreateRegistrationIdToOriginKey(int64 registration_id) {
190 return base::StringPrintf("%s%s", kRegIdToOriginKeyPrefix,
191 base::Int64ToString(registration_id).c_str());
194 void PutRegistrationDataToBatch(
195 const ServiceWorkerDatabase::RegistrationData& input,
196 leveldb::WriteBatch* batch) {
197 DCHECK(batch);
199 // Convert RegistrationData to ServiceWorkerRegistrationData.
200 ServiceWorkerRegistrationData data;
201 data.set_registration_id(input.registration_id);
202 data.set_scope_url(input.scope.spec());
203 data.set_script_url(input.script.spec());
204 data.set_version_id(input.version_id);
205 data.set_is_active(input.is_active);
206 data.set_has_fetch_handler(input.has_fetch_handler);
207 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
208 data.set_resources_total_size_bytes(input.resources_total_size_bytes);
210 std::string value;
211 bool success = data.SerializeToString(&value);
212 DCHECK(success);
213 GURL origin = input.scope.GetOrigin();
214 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
217 void PutResourceRecordToBatch(
218 const ServiceWorkerDatabase::ResourceRecord& input,
219 int64 version_id,
220 leveldb::WriteBatch* batch) {
221 DCHECK(batch);
222 DCHECK_GE(input.size_bytes, 0);
224 // Convert ResourceRecord to ServiceWorkerResourceRecord.
225 ServiceWorkerResourceRecord record;
226 record.set_resource_id(input.resource_id);
227 record.set_url(input.url.spec());
228 record.set_size_bytes(input.size_bytes);
230 std::string value;
231 bool success = record.SerializeToString(&value);
232 DCHECK(success);
233 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
236 void PutUniqueOriginToBatch(const GURL& origin,
237 leveldb::WriteBatch* batch) {
238 // Value should be empty.
239 batch->Put(CreateUniqueOriginKey(origin), "");
242 void PutPurgeableResourceIdToBatch(int64 resource_id,
243 leveldb::WriteBatch* batch) {
244 // Value should be empty.
245 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
248 ServiceWorkerDatabase::Status ParseId(
249 const std::string& serialized,
250 int64* out) {
251 DCHECK(out);
252 int64 id;
253 if (!base::StringToInt64(serialized, &id) || id < 0)
254 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
255 *out = id;
256 return ServiceWorkerDatabase::STATUS_OK;
259 ServiceWorkerDatabase::Status ParseDatabaseVersion(
260 const std::string& serialized,
261 int64* out) {
262 DCHECK(out);
263 const int kFirstValidVersion = 1;
264 int64 version;
265 if (!base::StringToInt64(serialized, &version) ||
266 version < kFirstValidVersion) {
267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
269 if (kCurrentSchemaVersion < version) {
270 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
271 << " than the current latest version: "
272 << version << " vs " << kCurrentSchemaVersion;
273 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
275 *out = version;
276 return ServiceWorkerDatabase::STATUS_OK;
279 ServiceWorkerDatabase::Status ParseRegistrationData(
280 const std::string& serialized,
281 ServiceWorkerDatabase::RegistrationData* out) {
282 DCHECK(out);
283 ServiceWorkerRegistrationData data;
284 if (!data.ParseFromString(serialized))
285 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
287 GURL scope_url(data.scope_url());
288 GURL script_url(data.script_url());
289 if (!scope_url.is_valid() ||
290 !script_url.is_valid() ||
291 scope_url.GetOrigin() != script_url.GetOrigin()) {
292 DLOG(ERROR) << "Scope URL '" << data.scope_url() << "' and/or script url '"
293 << data.script_url()
294 << "' are invalid or have mismatching origins.";
295 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
298 // Convert ServiceWorkerRegistrationData to RegistrationData.
299 out->registration_id = data.registration_id();
300 out->scope = scope_url;
301 out->script = script_url;
302 out->version_id = data.version_id();
303 out->is_active = data.is_active();
304 out->has_fetch_handler = data.has_fetch_handler();
305 out->last_update_check =
306 base::Time::FromInternalValue(data.last_update_check_time());
307 out->resources_total_size_bytes = data.resources_total_size_bytes();
309 return ServiceWorkerDatabase::STATUS_OK;
312 ServiceWorkerDatabase::Status ParseResourceRecord(
313 const std::string& serialized,
314 ServiceWorkerDatabase::ResourceRecord* out) {
315 DCHECK(out);
316 ServiceWorkerResourceRecord record;
317 if (!record.ParseFromString(serialized))
318 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
320 GURL url(record.url());
321 if (!url.is_valid())
322 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
324 // Convert ServiceWorkerResourceRecord to ResourceRecord.
325 out->resource_id = record.resource_id();
326 out->url = url;
327 out->size_bytes = record.size_bytes();
328 return ServiceWorkerDatabase::STATUS_OK;
331 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
332 const leveldb::Status& status) {
333 if (status.ok())
334 return ServiceWorkerDatabase::STATUS_OK;
335 else if (status.IsNotFound())
336 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
337 else if (status.IsIOError())
338 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
339 else if (status.IsCorruption())
340 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
341 else
342 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
345 int64_t AccumulateResourceSizeInBytes(
346 const std::vector<ServiceWorkerDatabase::ResourceRecord>& resources) {
347 int64_t total_size_bytes = 0;
348 for (const auto& resource : resources)
349 total_size_bytes += resource.size_bytes;
350 return total_size_bytes;
353 } // namespace
355 const char* ServiceWorkerDatabase::StatusToString(
356 ServiceWorkerDatabase::Status status) {
357 switch (status) {
358 case ServiceWorkerDatabase::STATUS_OK:
359 return "Database OK";
360 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
361 return "Database not found";
362 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
363 return "Database IO error";
364 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
365 return "Database corrupted";
366 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
367 return "Database operation failed";
368 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
369 NOTREACHED();
370 return "Database unknown error";
372 NOTREACHED();
373 return "Database unknown error";
376 ServiceWorkerDatabase::RegistrationData::RegistrationData()
377 : registration_id(kInvalidServiceWorkerRegistrationId),
378 version_id(kInvalidServiceWorkerVersionId),
379 is_active(false),
380 has_fetch_handler(false),
381 resources_total_size_bytes(0) {
384 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
387 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
388 : path_(path),
389 next_avail_registration_id_(0),
390 next_avail_resource_id_(0),
391 next_avail_version_id_(0),
392 state_(UNINITIALIZED),
393 skip_writing_diskcache_migration_state_on_init_for_testing_(false) {
394 sequence_checker_.DetachFromSequence();
397 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
398 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
399 db_.reset();
402 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
403 int64* next_avail_registration_id,
404 int64* next_avail_version_id,
405 int64* next_avail_resource_id) {
406 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
407 DCHECK(next_avail_registration_id);
408 DCHECK(next_avail_version_id);
409 DCHECK(next_avail_resource_id);
411 Status status = LazyOpen(false);
412 if (IsNewOrNonexistentDatabase(status)) {
413 *next_avail_registration_id = 0;
414 *next_avail_version_id = 0;
415 *next_avail_resource_id = 0;
416 return STATUS_OK;
418 if (status != STATUS_OK)
419 return status;
421 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
422 if (status != STATUS_OK)
423 return status;
424 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
425 if (status != STATUS_OK)
426 return status;
427 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
428 if (status != STATUS_OK)
429 return status;
431 *next_avail_registration_id = next_avail_registration_id_;
432 *next_avail_version_id = next_avail_version_id_;
433 *next_avail_resource_id = next_avail_resource_id_;
434 return STATUS_OK;
437 ServiceWorkerDatabase::Status ServiceWorkerDatabase::IsDiskCacheMigrationNeeded(
438 bool* migration_needed) {
439 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
441 Status status = LazyOpen(false);
442 if (IsNewOrNonexistentDatabase(status)) {
443 *migration_needed = false;
444 return STATUS_OK;
446 if (status != STATUS_OK)
447 return status;
449 std::string value;
450 status = LevelDBStatusToStatus(db_->Get(
451 leveldb::ReadOptions(), kDiskCacheMigrationNotNeededKey, &value));
452 if (status == STATUS_ERROR_NOT_FOUND) {
453 *migration_needed = true;
454 HandleReadResult(FROM_HERE, STATUS_OK);
455 return STATUS_OK;
457 if (status != STATUS_OK) {
458 HandleReadResult(FROM_HERE, status);
459 return status;
462 *migration_needed = false;
463 HandleReadResult(FROM_HERE, status);
464 return status;
467 ServiceWorkerDatabase::Status
468 ServiceWorkerDatabase::SetDiskCacheMigrationNotNeeded() {
469 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
471 Status status = LazyOpen(true);
472 if (status != STATUS_OK)
473 return status;
475 leveldb::WriteBatch batch;
476 batch.Put(kDiskCacheMigrationNotNeededKey, kEmptyValue);
477 return WriteBatch(&batch);
480 ServiceWorkerDatabase::Status
481 ServiceWorkerDatabase::IsOldDiskCacheDeletionNeeded(bool* deletion_needed) {
482 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
484 Status status = LazyOpen(false);
485 if (IsNewOrNonexistentDatabase(status)) {
486 *deletion_needed = false;
487 return STATUS_OK;
489 if (status != STATUS_OK)
490 return status;
492 std::string value;
493 status = LevelDBStatusToStatus(db_->Get(
494 leveldb::ReadOptions(), kOldDiskCacheDeletionNotNeededKey, &value));
495 if (status == STATUS_ERROR_NOT_FOUND) {
496 *deletion_needed = true;
497 HandleReadResult(FROM_HERE, STATUS_OK);
498 return STATUS_OK;
500 if (status != STATUS_OK) {
501 HandleReadResult(FROM_HERE, status);
502 return status;
505 *deletion_needed = false;
506 HandleReadResult(FROM_HERE, status);
507 return status;
510 ServiceWorkerDatabase::Status
511 ServiceWorkerDatabase::SetOldDiskCacheDeletionNotNeeded() {
512 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
514 Status status = LazyOpen(true);
515 if (status != STATUS_OK)
516 return status;
518 leveldb::WriteBatch batch;
519 batch.Put(kOldDiskCacheDeletionNotNeededKey, kEmptyValue);
520 return WriteBatch(&batch);
523 ServiceWorkerDatabase::Status
524 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
525 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
526 DCHECK(origins->empty());
528 Status status = LazyOpen(false);
529 if (IsNewOrNonexistentDatabase(status))
530 return STATUS_OK;
531 if (status != STATUS_OK)
532 return status;
534 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
535 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
536 status = LevelDBStatusToStatus(itr->status());
537 if (status != STATUS_OK) {
538 HandleReadResult(FROM_HERE, status);
539 origins->clear();
540 return status;
543 std::string origin_str;
544 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin_str))
545 break;
547 GURL origin(origin_str);
548 if (!origin.is_valid()) {
549 status = STATUS_ERROR_CORRUPTED;
550 HandleReadResult(FROM_HERE, status);
551 origins->clear();
552 return status;
555 origins->insert(origin);
558 HandleReadResult(FROM_HERE, status);
559 return status;
562 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
563 const GURL& origin,
564 std::vector<RegistrationData>* registrations,
565 std::vector<std::vector<ResourceRecord>>* opt_resources_list) {
566 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
567 DCHECK(registrations->empty());
569 Status status = LazyOpen(false);
570 if (IsNewOrNonexistentDatabase(status))
571 return STATUS_OK;
572 if (status != STATUS_OK)
573 return status;
575 std::string prefix = CreateRegistrationKeyPrefix(origin);
576 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
577 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
578 status = LevelDBStatusToStatus(itr->status());
579 if (status != STATUS_OK) {
580 HandleReadResult(FROM_HERE, status);
581 registrations->clear();
582 if (opt_resources_list)
583 opt_resources_list->clear();
584 return status;
587 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
588 break;
590 RegistrationData registration;
591 status = ParseRegistrationData(itr->value().ToString(), &registration);
592 if (status != STATUS_OK) {
593 HandleReadResult(FROM_HERE, status);
594 registrations->clear();
595 if (opt_resources_list)
596 opt_resources_list->clear();
597 return status;
599 registrations->push_back(registration);
601 if (opt_resources_list) {
602 std::vector<ResourceRecord> resources;
603 status = ReadResourceRecords(registration.version_id, &resources);
604 if (status != STATUS_OK) {
605 HandleReadResult(FROM_HERE, status);
606 registrations->clear();
607 opt_resources_list->clear();
608 return status;
610 opt_resources_list->push_back(resources);
614 HandleReadResult(FROM_HERE, status);
615 return status;
618 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
619 std::vector<RegistrationData>* registrations) {
620 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
621 DCHECK(registrations->empty());
623 Status status = LazyOpen(false);
624 if (IsNewOrNonexistentDatabase(status))
625 return STATUS_OK;
626 if (status != STATUS_OK)
627 return status;
629 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
630 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
631 status = LevelDBStatusToStatus(itr->status());
632 if (status != STATUS_OK) {
633 HandleReadResult(FROM_HERE, status);
634 registrations->clear();
635 return status;
638 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
639 break;
641 RegistrationData registration;
642 status = ParseRegistrationData(itr->value().ToString(), &registration);
643 if (status != STATUS_OK) {
644 HandleReadResult(FROM_HERE, status);
645 registrations->clear();
646 return status;
648 registrations->push_back(registration);
651 HandleReadResult(FROM_HERE, status);
652 return status;
655 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
656 int64 registration_id,
657 const GURL& origin,
658 RegistrationData* registration,
659 std::vector<ResourceRecord>* resources) {
660 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
661 DCHECK(registration);
662 DCHECK(resources);
664 Status status = LazyOpen(false);
665 if (IsNewOrNonexistentDatabase(status))
666 return STATUS_ERROR_NOT_FOUND;
667 if (status != STATUS_OK)
668 return status;
670 RegistrationData value;
671 status = ReadRegistrationData(registration_id, origin, &value);
672 if (status != STATUS_OK)
673 return status;
675 status = ReadResourceRecords(value.version_id, resources);
676 if (status != STATUS_OK)
677 return status;
679 // ResourceRecord must contain the ServiceWorker's main script.
680 if (resources->empty())
681 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
683 *registration = value;
684 return STATUS_OK;
687 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationOrigin(
688 int64 registration_id,
689 GURL* origin) {
690 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
691 DCHECK(origin);
693 Status status = LazyOpen(true);
694 if (IsNewOrNonexistentDatabase(status))
695 return STATUS_ERROR_NOT_FOUND;
696 if (status != STATUS_OK)
697 return status;
699 std::string value;
700 status = LevelDBStatusToStatus(
701 db_->Get(leveldb::ReadOptions(),
702 CreateRegistrationIdToOriginKey(registration_id), &value));
703 if (status != STATUS_OK) {
704 HandleReadResult(FROM_HERE,
705 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
706 return status;
709 GURL parsed(value);
710 if (!parsed.is_valid()) {
711 status = STATUS_ERROR_CORRUPTED;
712 HandleReadResult(FROM_HERE, status);
713 return status;
716 *origin = parsed;
717 HandleReadResult(FROM_HERE, STATUS_OK);
718 return STATUS_OK;
721 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
722 const RegistrationData& registration,
723 const std::vector<ResourceRecord>& resources,
724 RegistrationData* old_registration,
725 std::vector<int64>* newly_purgeable_resources) {
726 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
727 DCHECK(old_registration);
728 DCHECK(!resources.empty());
729 Status status = LazyOpen(true);
730 old_registration->version_id = kInvalidServiceWorkerVersionId;
731 if (status != STATUS_OK)
732 return status;
734 leveldb::WriteBatch batch;
735 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
736 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
738 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
740 DCHECK_EQ(AccumulateResourceSizeInBytes(resources),
741 registration.resources_total_size_bytes)
742 << "The total size in the registration must match the cumulative "
743 << "sizes of the resources.";
745 PutRegistrationDataToBatch(registration, &batch);
746 batch.Put(CreateRegistrationIdToOriginKey(registration.registration_id),
747 registration.scope.GetOrigin().spec());
749 // Used for avoiding multiple writes for the same resource id or url.
750 std::set<int64> pushed_resources;
751 std::set<GURL> pushed_urls;
752 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
753 itr != resources.end(); ++itr) {
754 if (!itr->url.is_valid())
755 return STATUS_ERROR_FAILED;
757 // Duplicated resource id or url should not exist.
758 DCHECK(pushed_resources.insert(itr->resource_id).second);
759 DCHECK(pushed_urls.insert(itr->url).second);
761 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
763 // Delete a resource from the uncommitted list.
764 batch.Delete(CreateResourceIdKey(
765 kUncommittedResIdKeyPrefix, itr->resource_id));
766 // Delete from the purgeable list in case this version was once deleted.
767 batch.Delete(
768 CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id));
771 // Retrieve a previous version to sweep purgeable resources.
772 status = ReadRegistrationData(registration.registration_id,
773 registration.scope.GetOrigin(),
774 old_registration);
775 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
776 return status;
777 if (status == STATUS_OK) {
778 DCHECK_LT(old_registration->version_id, registration.version_id);
779 status = DeleteResourceRecords(
780 old_registration->version_id, newly_purgeable_resources, &batch);
781 if (status != STATUS_OK)
782 return status;
784 // Currently resource sharing across versions and registrations is not
785 // supported, so resource ids should not be overlapped between
786 // |registration| and |old_registration|.
787 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
788 newly_purgeable_resources->end());
789 DCHECK(base::STLSetIntersection<std::set<int64> >(
790 pushed_resources, deleted_resources).empty());
793 return WriteBatch(&batch);
796 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
797 int64 registration_id,
798 const GURL& origin) {
799 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
800 Status status = LazyOpen(false);
801 if (IsNewOrNonexistentDatabase(status))
802 return STATUS_ERROR_NOT_FOUND;
803 if (status != STATUS_OK)
804 return status;
805 if (!origin.is_valid())
806 return STATUS_ERROR_FAILED;
808 RegistrationData registration;
809 status = ReadRegistrationData(registration_id, origin, &registration);
810 if (status != STATUS_OK)
811 return status;
813 registration.is_active = true;
815 leveldb::WriteBatch batch;
816 PutRegistrationDataToBatch(registration, &batch);
817 return WriteBatch(&batch);
820 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
821 int64 registration_id,
822 const GURL& origin,
823 const base::Time& time) {
824 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
825 Status status = LazyOpen(false);
826 if (IsNewOrNonexistentDatabase(status))
827 return STATUS_ERROR_NOT_FOUND;
828 if (status != STATUS_OK)
829 return status;
830 if (!origin.is_valid())
831 return STATUS_ERROR_FAILED;
833 RegistrationData registration;
834 status = ReadRegistrationData(registration_id, origin, &registration);
835 if (status != STATUS_OK)
836 return status;
838 registration.last_update_check = time;
840 leveldb::WriteBatch batch;
841 PutRegistrationDataToBatch(registration, &batch);
842 return WriteBatch(&batch);
845 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
846 int64 registration_id,
847 const GURL& origin,
848 RegistrationData* deleted_version,
849 std::vector<int64>* newly_purgeable_resources) {
850 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
851 DCHECK(deleted_version);
852 deleted_version->version_id = kInvalidServiceWorkerVersionId;
853 Status status = LazyOpen(false);
854 if (IsNewOrNonexistentDatabase(status))
855 return STATUS_OK;
856 if (status != STATUS_OK)
857 return status;
858 if (!origin.is_valid())
859 return STATUS_ERROR_FAILED;
861 leveldb::WriteBatch batch;
863 // Remove |origin| from unique origins if a registration specified by
864 // |registration_id| is the only one for |origin|.
865 // TODO(nhiroki): Check the uniqueness by more efficient way.
866 std::vector<RegistrationData> registrations;
867 status = GetRegistrationsForOrigin(origin, &registrations, nullptr);
868 if (status != STATUS_OK)
869 return status;
871 if (registrations.size() == 1 &&
872 registrations[0].registration_id == registration_id) {
873 batch.Delete(CreateUniqueOriginKey(origin));
876 // Delete a registration specified by |registration_id|.
877 batch.Delete(CreateRegistrationKey(registration_id, origin));
878 batch.Delete(CreateRegistrationIdToOriginKey(registration_id));
880 // Delete resource records and user data associated with the registration.
881 for (const auto& registration : registrations) {
882 if (registration.registration_id == registration_id) {
883 *deleted_version = registration;
884 status = DeleteResourceRecords(
885 registration.version_id, newly_purgeable_resources, &batch);
886 if (status != STATUS_OK)
887 return status;
889 status = DeleteUserDataForRegistration(registration_id, &batch);
890 if (status != STATUS_OK)
891 return status;
892 break;
896 return WriteBatch(&batch);
899 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadUserData(
900 int64 registration_id,
901 const std::string& user_data_name,
902 std::string* user_data) {
903 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
904 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
905 DCHECK(!user_data_name.empty());
906 DCHECK(user_data);
908 Status status = LazyOpen(false);
909 if (IsNewOrNonexistentDatabase(status))
910 return STATUS_ERROR_NOT_FOUND;
911 if (status != STATUS_OK)
912 return status;
914 const std::string key = CreateUserDataKey(registration_id, user_data_name);
915 status = LevelDBStatusToStatus(
916 db_->Get(leveldb::ReadOptions(), key, user_data));
917 HandleReadResult(FROM_HERE,
918 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
919 return status;
922 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteUserData(
923 int64 registration_id,
924 const GURL& origin,
925 const std::string& user_data_name,
926 const std::string& user_data) {
927 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
928 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
929 DCHECK(!user_data_name.empty());
931 Status status = LazyOpen(false);
932 if (IsNewOrNonexistentDatabase(status))
933 return STATUS_ERROR_NOT_FOUND;
934 if (status != STATUS_OK)
935 return status;
937 // There should be the registration specified by |registration_id|.
938 RegistrationData registration;
939 status = ReadRegistrationData(registration_id, origin, &registration);
940 if (status != STATUS_OK)
941 return status;
943 leveldb::WriteBatch batch;
944 batch.Put(CreateUserDataKey(registration_id, user_data_name), user_data);
945 batch.Put(CreateHasUserDataKey(registration_id, user_data_name), "");
946 return WriteBatch(&batch);
949 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteUserData(
950 int64 registration_id,
951 const std::string& user_data_name) {
952 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
953 DCHECK_NE(kInvalidServiceWorkerRegistrationId, registration_id);
954 DCHECK(!user_data_name.empty());
956 Status status = LazyOpen(false);
957 if (IsNewOrNonexistentDatabase(status))
958 return STATUS_OK;
959 if (status != STATUS_OK)
960 return status;
962 leveldb::WriteBatch batch;
963 batch.Delete(CreateUserDataKey(registration_id, user_data_name));
964 batch.Delete(CreateHasUserDataKey(registration_id, user_data_name));
965 return WriteBatch(&batch);
968 ServiceWorkerDatabase::Status
969 ServiceWorkerDatabase::ReadUserDataForAllRegistrations(
970 const std::string& user_data_name,
971 std::vector<std::pair<int64, std::string>>* user_data) {
972 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
973 DCHECK(user_data->empty());
975 Status status = LazyOpen(false);
976 if (IsNewOrNonexistentDatabase(status))
977 return STATUS_OK;
978 if (status != STATUS_OK)
979 return status;
981 std::string key_prefix = CreateHasUserDataKeyPrefix(user_data_name);
982 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
983 for (itr->Seek(key_prefix); itr->Valid(); itr->Next()) {
984 status = LevelDBStatusToStatus(itr->status());
985 if (status != STATUS_OK) {
986 HandleReadResult(FROM_HERE, status);
987 user_data->clear();
988 return status;
991 std::string registration_id_string;
992 if (!RemovePrefix(itr->key().ToString(), key_prefix,
993 &registration_id_string)) {
994 break;
997 int64 registration_id;
998 status = ParseId(registration_id_string, &registration_id);
999 if (status != STATUS_OK) {
1000 HandleReadResult(FROM_HERE, status);
1001 user_data->clear();
1002 return status;
1005 std::string value;
1006 status = LevelDBStatusToStatus(
1007 db_->Get(leveldb::ReadOptions(),
1008 CreateUserDataKey(registration_id, user_data_name), &value));
1009 if (status != STATUS_OK) {
1010 HandleReadResult(FROM_HERE, status);
1011 user_data->clear();
1012 return status;
1014 user_data->push_back(std::make_pair(registration_id, value));
1017 HandleReadResult(FROM_HERE, status);
1018 return status;
1021 ServiceWorkerDatabase::Status
1022 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
1023 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
1026 ServiceWorkerDatabase::Status
1027 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
1028 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
1031 ServiceWorkerDatabase::Status
1032 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
1033 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
1036 ServiceWorkerDatabase::Status
1037 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
1038 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
1041 ServiceWorkerDatabase::Status
1042 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
1043 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
1046 ServiceWorkerDatabase::Status
1047 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
1048 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
1051 ServiceWorkerDatabase::Status
1052 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
1053 const std::set<int64>& ids) {
1054 leveldb::WriteBatch batch;
1055 Status status = DeleteResourceIdsInBatch(
1056 kUncommittedResIdKeyPrefix, ids, &batch);
1057 if (status != STATUS_OK)
1058 return status;
1059 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
1060 if (status != STATUS_OK)
1061 return status;
1062 return WriteBatch(&batch);
1065 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigins(
1066 const std::set<GURL>& origins,
1067 std::vector<int64>* newly_purgeable_resources) {
1068 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1069 Status status = LazyOpen(false);
1070 if (IsNewOrNonexistentDatabase(status))
1071 return STATUS_OK;
1072 if (status != STATUS_OK)
1073 return status;
1074 leveldb::WriteBatch batch;
1076 for (const GURL& origin : origins) {
1077 if (!origin.is_valid())
1078 return STATUS_ERROR_FAILED;
1080 // Delete from the unique origin list.
1081 batch.Delete(CreateUniqueOriginKey(origin));
1083 std::vector<RegistrationData> registrations;
1084 status = GetRegistrationsForOrigin(origin, &registrations, nullptr);
1085 if (status != STATUS_OK)
1086 return status;
1088 // Delete registrations, resource records and user data.
1089 for (const RegistrationData& data : registrations) {
1090 batch.Delete(CreateRegistrationKey(data.registration_id, origin));
1091 batch.Delete(CreateRegistrationIdToOriginKey(data.registration_id));
1093 status = DeleteResourceRecords(
1094 data.version_id, newly_purgeable_resources, &batch);
1095 if (status != STATUS_OK)
1096 return status;
1098 status = DeleteUserDataForRegistration(data.registration_id, &batch);
1099 if (status != STATUS_OK)
1100 return status;
1104 return WriteBatch(&batch);
1107 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
1108 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1109 Disable(FROM_HERE, STATUS_OK);
1111 if (IsDatabaseInMemory()) {
1112 env_.reset();
1113 return STATUS_OK;
1116 // Directly delete the database directory instead of leveldb::DestroyDB()
1117 // because the API does not delete the directory if there are unrelated files.
1118 // (https://code.google.com/p/chromium/issues/detail?id=468926#c24)
1119 Status status = base::DeleteFile(path_, true /* recursive */)
1120 ? STATUS_OK
1121 : STATUS_ERROR_FAILED;
1122 ServiceWorkerMetrics::RecordDestroyDatabaseResult(status);
1123 return status;
1126 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
1127 bool create_if_missing) {
1128 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1130 // Do not try to open a database if we tried and failed once.
1131 if (state_ == DISABLED)
1132 return STATUS_ERROR_FAILED;
1133 if (IsOpen())
1134 return STATUS_OK;
1136 if (!create_if_missing) {
1137 // Avoid opening a database if it does not exist at the |path_|.
1138 if (IsDatabaseInMemory() || !base::PathExists(path_) ||
1139 base::IsDirectoryEmpty(path_)) {
1140 return STATUS_ERROR_NOT_FOUND;
1144 leveldb::Options options;
1145 options.create_if_missing = create_if_missing;
1146 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
1147 if (IsDatabaseInMemory()) {
1148 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
1149 options.env = env_.get();
1150 } else {
1151 options.env = g_service_worker_env.Pointer();
1154 leveldb::DB* db = NULL;
1155 Status status = LevelDBStatusToStatus(
1156 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
1157 HandleOpenResult(FROM_HERE, status);
1158 if (status != STATUS_OK) {
1159 DCHECK(!db);
1160 // TODO(nhiroki): Should we retry to open the database?
1161 return status;
1163 db_.reset(db);
1165 int64 db_version;
1166 status = ReadDatabaseVersion(&db_version);
1167 if (status != STATUS_OK)
1168 return status;
1169 DCHECK_LE(0, db_version);
1171 if (db_version > 0 && db_version < kCurrentSchemaVersion) {
1172 switch (db_version) {
1173 case 1:
1174 status = UpgradeDatabaseSchemaFromV1ToV2();
1175 if (status != STATUS_OK)
1176 return status;
1177 db_version = 2;
1178 // Intentionally fall-through to other version upgrade cases.
1180 // Either the database got upgraded to the current schema version, or some
1181 // upgrade step failed which would have caused this method to abort.
1182 DCHECK_EQ(db_version, kCurrentSchemaVersion);
1185 if (db_version > 0)
1186 state_ = INITIALIZED;
1187 return STATUS_OK;
1190 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
1191 ServiceWorkerDatabase::Status status) {
1192 if (status == STATUS_ERROR_NOT_FOUND)
1193 return true;
1194 if (status == STATUS_OK && state_ == UNINITIALIZED)
1195 return true;
1196 return false;
1199 ServiceWorkerDatabase::Status
1200 ServiceWorkerDatabase::UpgradeDatabaseSchemaFromV1ToV2() {
1201 Status status = STATUS_OK;
1202 leveldb::WriteBatch batch;
1204 // Version 2 introduced REGID_TO_ORIGIN, add for all existing registrations.
1205 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1206 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
1207 status = LevelDBStatusToStatus(itr->status());
1208 if (status != STATUS_OK) {
1209 HandleReadResult(FROM_HERE, status);
1210 return status;
1213 std::string key;
1214 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, &key))
1215 break;
1217 std::vector<std::string> parts =
1218 base::SplitString(key, std::string(1, kKeySeparator),
1219 base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
1220 if (parts.size() != 2) {
1221 status = STATUS_ERROR_CORRUPTED;
1222 HandleReadResult(FROM_HERE, status);
1223 return status;
1226 int64 registration_id;
1227 status = ParseId(parts[1], &registration_id);
1228 if (status != STATUS_OK) {
1229 HandleReadResult(FROM_HERE, status);
1230 return status;
1233 batch.Put(CreateRegistrationIdToOriginKey(registration_id), parts[0]);
1236 // Update schema version manually instead of relying on WriteBatch to make
1237 // sure each upgrade step only updates it to the actually correct version.
1238 batch.Put(kDatabaseVersionKey, base::Int64ToString(2));
1239 status = LevelDBStatusToStatus(
1240 db_->Write(leveldb::WriteOptions(), &batch));
1241 HandleWriteResult(FROM_HERE, status);
1242 return status;
1245 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
1246 const char* id_key,
1247 int64* next_avail_id) {
1248 DCHECK(id_key);
1249 DCHECK(next_avail_id);
1251 std::string value;
1252 Status status = LevelDBStatusToStatus(
1253 db_->Get(leveldb::ReadOptions(), id_key, &value));
1254 if (status == STATUS_ERROR_NOT_FOUND) {
1255 // Nobody has gotten the next resource id for |id_key|.
1256 *next_avail_id = 0;
1257 HandleReadResult(FROM_HERE, STATUS_OK);
1258 return STATUS_OK;
1259 } else if (status != STATUS_OK) {
1260 HandleReadResult(FROM_HERE, status);
1261 return status;
1264 status = ParseId(value, next_avail_id);
1265 HandleReadResult(FROM_HERE, status);
1266 return status;
1269 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
1270 int64 registration_id,
1271 const GURL& origin,
1272 RegistrationData* registration) {
1273 DCHECK(registration);
1275 const std::string key = CreateRegistrationKey(registration_id, origin);
1276 std::string value;
1277 Status status = LevelDBStatusToStatus(
1278 db_->Get(leveldb::ReadOptions(), key, &value));
1279 if (status != STATUS_OK) {
1280 HandleReadResult(
1281 FROM_HERE,
1282 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
1283 return status;
1286 status = ParseRegistrationData(value, registration);
1287 HandleReadResult(FROM_HERE, status);
1288 return status;
1291 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
1292 int64 version_id,
1293 std::vector<ResourceRecord>* resources) {
1294 DCHECK(resources->empty());
1296 Status status = STATUS_OK;
1297 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1299 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1300 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1301 Status status = LevelDBStatusToStatus(itr->status());
1302 if (status != STATUS_OK) {
1303 HandleReadResult(FROM_HERE, status);
1304 resources->clear();
1305 return status;
1308 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
1309 break;
1311 ResourceRecord resource;
1312 status = ParseResourceRecord(itr->value().ToString(), &resource);
1313 if (status != STATUS_OK) {
1314 HandleReadResult(FROM_HERE, status);
1315 resources->clear();
1316 return status;
1318 resources->push_back(resource);
1321 HandleReadResult(FROM_HERE, status);
1322 return status;
1325 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
1326 int64 version_id,
1327 std::vector<int64>* newly_purgeable_resources,
1328 leveldb::WriteBatch* batch) {
1329 DCHECK(batch);
1331 Status status = STATUS_OK;
1332 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
1334 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1335 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1336 status = LevelDBStatusToStatus(itr->status());
1337 if (status != STATUS_OK) {
1338 HandleReadResult(FROM_HERE, status);
1339 return status;
1342 const std::string key = itr->key().ToString();
1343 std::string unprefixed;
1344 if (!RemovePrefix(key, prefix, &unprefixed))
1345 break;
1347 int64 resource_id;
1348 status = ParseId(unprefixed, &resource_id);
1349 if (status != STATUS_OK) {
1350 HandleReadResult(FROM_HERE, status);
1351 return status;
1354 // Remove a resource record.
1355 batch->Delete(key);
1357 // Currently resource sharing across versions and registrations is not
1358 // supported, so we can purge this without caring about it.
1359 PutPurgeableResourceIdToBatch(resource_id, batch);
1360 newly_purgeable_resources->push_back(resource_id);
1363 HandleReadResult(FROM_HERE, status);
1364 return status;
1367 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
1368 const char* id_key_prefix,
1369 std::set<int64>* ids) {
1370 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1371 DCHECK(id_key_prefix);
1372 DCHECK(ids->empty());
1374 Status status = LazyOpen(false);
1375 if (IsNewOrNonexistentDatabase(status))
1376 return STATUS_OK;
1377 if (status != STATUS_OK)
1378 return status;
1380 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1381 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
1382 status = LevelDBStatusToStatus(itr->status());
1383 if (status != STATUS_OK) {
1384 HandleReadResult(FROM_HERE, status);
1385 ids->clear();
1386 return status;
1389 std::string unprefixed;
1390 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
1391 break;
1393 int64 resource_id;
1394 status = ParseId(unprefixed, &resource_id);
1395 if (status != STATUS_OK) {
1396 HandleReadResult(FROM_HERE, status);
1397 ids->clear();
1398 return status;
1400 ids->insert(resource_id);
1403 HandleReadResult(FROM_HERE, status);
1404 return status;
1407 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
1408 const char* id_key_prefix,
1409 const std::set<int64>& ids) {
1410 leveldb::WriteBatch batch;
1411 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
1412 if (status != STATUS_OK)
1413 return status;
1414 return WriteBatch(&batch);
1417 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
1418 const char* id_key_prefix,
1419 const std::set<int64>& ids,
1420 leveldb::WriteBatch* batch) {
1421 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1422 DCHECK(id_key_prefix);
1424 Status status = LazyOpen(true);
1425 if (status != STATUS_OK)
1426 return status;
1428 if (ids.empty())
1429 return STATUS_OK;
1430 for (std::set<int64>::const_iterator itr = ids.begin();
1431 itr != ids.end(); ++itr) {
1432 // Value should be empty.
1433 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
1435 // std::set is sorted, so the last element is the largest.
1436 BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
1437 return STATUS_OK;
1440 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
1441 const char* id_key_prefix,
1442 const std::set<int64>& ids) {
1443 leveldb::WriteBatch batch;
1444 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
1445 if (status != STATUS_OK)
1446 return status;
1447 return WriteBatch(&batch);
1450 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
1451 const char* id_key_prefix,
1452 const std::set<int64>& ids,
1453 leveldb::WriteBatch* batch) {
1454 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1455 DCHECK(id_key_prefix);
1457 Status status = LazyOpen(false);
1458 if (IsNewOrNonexistentDatabase(status))
1459 return STATUS_OK;
1460 if (status != STATUS_OK)
1461 return status;
1463 for (std::set<int64>::const_iterator itr = ids.begin();
1464 itr != ids.end(); ++itr) {
1465 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1467 return STATUS_OK;
1470 ServiceWorkerDatabase::Status
1471 ServiceWorkerDatabase::DeleteUserDataForRegistration(
1472 int64 registration_id,
1473 leveldb::WriteBatch* batch) {
1474 DCHECK(batch);
1475 Status status = STATUS_OK;
1476 const std::string prefix = CreateUserDataKeyPrefix(registration_id);
1478 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
1479 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
1480 status = LevelDBStatusToStatus(itr->status());
1481 if (status != STATUS_OK) {
1482 HandleReadResult(FROM_HERE, status);
1483 return status;
1486 const std::string key = itr->key().ToString();
1487 std::string user_data_name;
1488 if (!RemovePrefix(key, prefix, &user_data_name))
1489 break;
1490 batch->Delete(key);
1491 batch->Delete(CreateHasUserDataKey(registration_id, user_data_name));
1493 return status;
1496 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1497 int64* db_version) {
1498 std::string value;
1499 Status status = LevelDBStatusToStatus(
1500 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1501 if (status == STATUS_ERROR_NOT_FOUND) {
1502 // The database hasn't been initialized yet.
1503 *db_version = 0;
1504 HandleReadResult(FROM_HERE, STATUS_OK);
1505 return STATUS_OK;
1508 if (status != STATUS_OK) {
1509 HandleReadResult(FROM_HERE, status);
1510 return status;
1513 status = ParseDatabaseVersion(value, db_version);
1514 HandleReadResult(FROM_HERE, status);
1515 return status;
1518 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1519 leveldb::WriteBatch* batch) {
1520 DCHECK(batch);
1521 DCHECK_NE(DISABLED, state_);
1523 if (state_ == UNINITIALIZED) {
1524 // Write database default values.
1525 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1526 if (!skip_writing_diskcache_migration_state_on_init_for_testing_)
1527 batch->Put(kDiskCacheMigrationNotNeededKey, kEmptyValue);
1528 state_ = INITIALIZED;
1531 Status status = LevelDBStatusToStatus(
1532 db_->Write(leveldb::WriteOptions(), batch));
1533 HandleWriteResult(FROM_HERE, status);
1534 return status;
1537 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1538 int64 used_id, leveldb::WriteBatch* batch) {
1539 DCHECK(batch);
1540 if (next_avail_registration_id_ <= used_id) {
1541 next_avail_registration_id_ = used_id + 1;
1542 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1546 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1547 int64 used_id, leveldb::WriteBatch* batch) {
1548 DCHECK(batch);
1549 if (next_avail_resource_id_ <= used_id) {
1550 next_avail_resource_id_ = used_id + 1;
1551 batch->Put(kNextResIdKey, base::Int64ToString(next_avail_resource_id_));
1555 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1556 int64 used_id, leveldb::WriteBatch* batch) {
1557 DCHECK(batch);
1558 if (next_avail_version_id_ <= used_id) {
1559 next_avail_version_id_ = used_id + 1;
1560 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1564 bool ServiceWorkerDatabase::IsOpen() {
1565 return db_ != NULL;
1568 void ServiceWorkerDatabase::Disable(
1569 const tracked_objects::Location& from_here,
1570 Status status) {
1571 if (status != STATUS_OK) {
1572 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1573 << " with error: " << StatusToString(status);
1574 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1576 state_ = DISABLED;
1577 db_.reset();
1580 void ServiceWorkerDatabase::HandleOpenResult(
1581 const tracked_objects::Location& from_here,
1582 Status status) {
1583 if (status != STATUS_OK)
1584 Disable(from_here, status);
1585 ServiceWorkerMetrics::CountOpenDatabaseResult(status);
1588 void ServiceWorkerDatabase::HandleReadResult(
1589 const tracked_objects::Location& from_here,
1590 Status status) {
1591 if (status != STATUS_OK)
1592 Disable(from_here, status);
1593 ServiceWorkerMetrics::CountReadDatabaseResult(status);
1596 void ServiceWorkerDatabase::HandleWriteResult(
1597 const tracked_objects::Location& from_here,
1598 Status status) {
1599 if (status != STATUS_OK)
1600 Disable(from_here, status);
1601 ServiceWorkerMetrics::CountWriteDatabaseResult(status);
1604 bool ServiceWorkerDatabase::IsDatabaseInMemory() const {
1605 return path_.empty();
1608 } // namespace content