Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blob4561bf678dfd6428eebafbb43f07e199e5f9182d
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_database.h"
7 #include <string>
9 #include "base/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.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 "third_party/leveldatabase/src/helpers/memenv/memenv.h"
18 #include "third_party/leveldatabase/src/include/leveldb/db.h"
19 #include "third_party/leveldatabase/src/include/leveldb/env.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
22 // LevelDB database schema
23 // =======================
25 // NOTE
26 // - int64 value is serialized as a string by base::Int64ToString().
27 // - GURL value is serialized as a string by GURL::spec().
29 // Version 1 (in sorted order)
30 // key: "INITDATA_DB_VERSION"
31 // value: "1"
33 // key: "INITDATA_NEXT_REGISTRATION_ID"
34 // value: <int64 'next_available_registration_id'>
36 // key: "INITDATA_NEXT_RESOURCE_ID"
37 // value: <int64 'next_available_resource_id'>
39 // key: "INITDATA_NEXT_VERSION_ID"
40 // value: <int64 'next_available_version_id'>
42 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
43 // value: <empty>
45 // key: "PRES:" + <int64 'purgeable_resource_id'>
46 // value: <empty>
48 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
49 // (ex. "REG:http://example.com\x00123456")
50 // value: <ServiceWorkerRegistrationData serialized as a string>
52 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
53 // (ex. "RES:123456\x00654321")
54 // value: <ServiceWorkerResourceRecord serialized as a string>
56 // key: "URES:" + <int64 'uncommitted_resource_id'>
57 // value: <empty>
59 namespace content {
61 namespace {
63 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
64 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
65 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
66 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
67 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
69 const char kRegKeyPrefix[] = "REG:";
70 const char kResKeyPrefix[] = "RES:";
71 const char kKeySeparator = '\x00';
73 const char kUncommittedResIdKeyPrefix[] = "URES:";
74 const char kPurgeableResIdKeyPrefix[] = "PRES:";
76 const int64 kCurrentSchemaVersion = 1;
78 bool RemovePrefix(const std::string& str,
79 const std::string& prefix,
80 std::string* out) {
81 if (!StartsWithASCII(str, prefix, true))
82 return false;
83 if (out)
84 *out = str.substr(prefix.size());
85 return true;
88 std::string CreateRegistrationKey(int64 registration_id,
89 const GURL& origin) {
90 return base::StringPrintf("%s%s%c%s",
91 kRegKeyPrefix,
92 origin.spec().c_str(),
93 kKeySeparator,
94 base::Int64ToString(registration_id).c_str());
97 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
98 return base::StringPrintf("%s%s%c",
99 kResKeyPrefix,
100 base::Int64ToString(version_id).c_str(),
101 kKeySeparator);
104 std::string CreateResourceRecordKey(int64 version_id,
105 int64 resource_id) {
106 return CreateResourceRecordKeyPrefix(version_id).append(
107 base::Int64ToString(resource_id));
110 std::string CreateUniqueOriginKey(const GURL& origin) {
111 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
114 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
115 return base::StringPrintf(
116 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
119 void PutRegistrationDataToBatch(
120 const ServiceWorkerDatabase::RegistrationData& input,
121 leveldb::WriteBatch* batch) {
122 DCHECK(batch);
124 // Convert RegistrationData to ServiceWorkerRegistrationData.
125 ServiceWorkerRegistrationData data;
126 data.set_registration_id(input.registration_id);
127 data.set_scope_url(input.scope.spec());
128 data.set_script_url(input.script.spec());
129 data.set_version_id(input.version_id);
130 data.set_is_active(input.is_active);
131 data.set_has_fetch_handler(input.has_fetch_handler);
132 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
134 std::string value;
135 bool success = data.SerializeToString(&value);
136 DCHECK(success);
137 GURL origin = input.scope.GetOrigin();
138 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
141 void PutResourceRecordToBatch(
142 const ServiceWorkerDatabase::ResourceRecord& input,
143 int64 version_id,
144 leveldb::WriteBatch* batch) {
145 DCHECK(batch);
147 // Convert ResourceRecord to ServiceWorkerResourceRecord.
148 ServiceWorkerResourceRecord record;
149 record.set_resource_id(input.resource_id);
150 record.set_url(input.url.spec());
152 std::string value;
153 bool success = record.SerializeToString(&value);
154 DCHECK(success);
155 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
158 void PutUniqueOriginToBatch(const GURL& origin,
159 leveldb::WriteBatch* batch) {
160 // Value should be empty.
161 batch->Put(CreateUniqueOriginKey(origin), "");
164 void PutPurgeableResourceIdToBatch(int64 resource_id,
165 leveldb::WriteBatch* batch) {
166 // Value should be empty.
167 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
170 bool ParseRegistrationData(const std::string& serialized,
171 ServiceWorkerDatabase::RegistrationData* out) {
172 DCHECK(out);
173 ServiceWorkerRegistrationData data;
174 if (!data.ParseFromString(serialized))
175 return false;
177 GURL scope_url(data.scope_url());
178 GURL script_url(data.script_url());
179 if (!scope_url.is_valid() ||
180 !script_url.is_valid() ||
181 scope_url.GetOrigin() != script_url.GetOrigin()) {
182 return false;
185 // Convert ServiceWorkerRegistrationData to RegistrationData.
186 out->registration_id = data.registration_id();
187 out->scope = scope_url;
188 out->script = script_url;
189 out->version_id = data.version_id();
190 out->is_active = data.is_active();
191 out->has_fetch_handler = data.has_fetch_handler();
192 out->last_update_check =
193 base::Time::FromInternalValue(data.last_update_check_time());
194 return true;
197 bool ParseResourceRecord(const std::string& serialized,
198 ServiceWorkerDatabase::ResourceRecord* out) {
199 DCHECK(out);
200 ServiceWorkerResourceRecord record;
201 if (!record.ParseFromString(serialized))
202 return false;
204 GURL url(record.url());
205 if (!url.is_valid())
206 return false;
208 // Convert ServiceWorkerResourceRecord to ResourceRecord.
209 out->resource_id = record.resource_id();
210 out->url = url;
211 return true;
214 } // namespace
216 ServiceWorkerDatabase::RegistrationData::RegistrationData()
217 : registration_id(-1),
218 version_id(-1),
219 is_active(false),
220 has_fetch_handler(false) {
223 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
226 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
227 : path_(path),
228 next_avail_registration_id_(0),
229 next_avail_resource_id_(0),
230 next_avail_version_id_(0),
231 is_disabled_(false),
232 was_corruption_detected_(false),
233 is_initialized_(false) {
236 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
237 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
238 db_.reset();
241 bool ServiceWorkerDatabase::GetNextAvailableIds(
242 int64* next_avail_registration_id,
243 int64* next_avail_version_id,
244 int64* next_avail_resource_id) {
245 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
246 DCHECK(next_avail_registration_id);
247 DCHECK(next_avail_version_id);
248 DCHECK(next_avail_resource_id);
250 if (!LazyOpen(false) || is_disabled_)
251 return false;
253 if (!ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_) ||
254 !ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_) ||
255 !ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_)) {
256 return false;
259 *next_avail_registration_id = next_avail_registration_id_;
260 *next_avail_version_id = next_avail_version_id_;
261 *next_avail_resource_id = next_avail_resource_id_;
262 return true;
265 bool ServiceWorkerDatabase::GetOriginsWithRegistrations(
266 std::set<GURL>* origins) {
267 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
268 DCHECK(origins);
270 if (!LazyOpen(false) || is_disabled_)
271 return false;
273 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
274 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
275 if (!itr->status().ok()) {
276 HandleError(FROM_HERE, itr->status());
277 origins->clear();
278 return false;
281 std::string origin;
282 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
283 break;
284 origins->insert(GURL(origin));
286 return true;
289 bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
290 const GURL& origin,
291 std::vector<RegistrationData>* registrations) {
292 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
293 DCHECK(registrations);
295 if (!LazyOpen(false) || is_disabled_)
296 return false;
298 // Create a key prefix for registrations.
299 std::string prefix = base::StringPrintf(
300 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
302 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
303 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
304 if (!itr->status().ok()) {
305 HandleError(FROM_HERE, itr->status());
306 registrations->clear();
307 return false;
310 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
311 break;
313 RegistrationData registration;
314 if (!ParseRegistrationData(itr->value().ToString(), &registration)) {
315 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
316 registrations->clear();
317 return false;
319 registrations->push_back(registration);
321 return true;
324 bool ServiceWorkerDatabase::ReadRegistration(
325 int64 registration_id,
326 const GURL& origin,
327 RegistrationData* registration,
328 std::vector<ResourceRecord>* resources) {
329 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
330 DCHECK(registration);
331 DCHECK(resources);
333 if (!LazyOpen(false) || is_disabled_)
334 return false;
336 RegistrationData value;
337 if (!ReadRegistrationData(registration_id, origin, &value))
338 return false;
340 if (!ReadResourceRecords(value.version_id, resources))
341 return false;
343 *registration = value;
344 return true;
347 bool ServiceWorkerDatabase::WriteRegistration(
348 const RegistrationData& registration,
349 const std::vector<ResourceRecord>& resources) {
350 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
351 if (!LazyOpen(true) || is_disabled_)
352 return false;
354 leveldb::WriteBatch batch;
355 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
356 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
358 // TODO(nhiroki): Skip to add the origin into the unique origin list if it
359 // has already been added.
360 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
362 PutRegistrationDataToBatch(registration, &batch);
364 // Retrieve a previous version to sweep purgeable resources.
365 RegistrationData old_registration;
366 if (!ReadRegistrationData(registration.registration_id,
367 registration.scope.GetOrigin(),
368 &old_registration)) {
369 if (is_disabled_)
370 return false;
371 // Just not found.
372 } else {
373 DCHECK_LT(old_registration.version_id, registration.version_id);
374 // Currently resource sharing across versions and registrations is not
375 // suppported, so resource ids should not be overlapped between
376 // |registration| and |old_registration|.
377 // TODO(nhiroki): Add DCHECK to make sure the overlap does not exist.
378 if (!DeleteResourceRecords(old_registration.version_id, &batch))
379 return false;
382 // Used for avoiding multiple writes for the same resource id or url.
383 std::set<int64> pushed_resources;
384 std::set<GURL> pushed_urls;
385 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
386 itr != resources.end(); ++itr) {
387 if (!itr->url.is_valid())
388 return false;
390 // Duplicated resource id or url should not exist.
391 DCHECK(pushed_resources.insert(itr->resource_id).second);
392 DCHECK(pushed_urls.insert(itr->url).second);
394 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
396 // Delete a resource from the uncommitted list.
397 batch.Delete(CreateResourceIdKey(
398 kUncommittedResIdKeyPrefix, itr->resource_id));
401 return WriteBatch(&batch);
404 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id,
405 const GURL& origin) {
406 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
407 if (!LazyOpen(false) || is_disabled_)
408 return false;
410 RegistrationData registration;
411 if (!ReadRegistrationData(registration_id, origin, &registration))
412 return false;
414 registration.is_active = true;
416 leveldb::WriteBatch batch;
417 PutRegistrationDataToBatch(registration, &batch);
418 return WriteBatch(&batch);
421 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id,
422 const GURL& origin,
423 const base::Time& time) {
424 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
425 if (!LazyOpen(false) || is_disabled_)
426 return false;
428 RegistrationData registration;
429 if (!ReadRegistrationData(registration_id, origin, &registration))
430 return false;
432 registration.last_update_check = time;
434 leveldb::WriteBatch batch;
435 PutRegistrationDataToBatch(registration, &batch);
436 return WriteBatch(&batch);
439 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id,
440 const GURL& origin) {
441 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
442 if (!LazyOpen(false) || is_disabled_)
443 return false;
445 leveldb::WriteBatch batch;
447 // Remove |origin| from unique origins if a registration specified by
448 // |registration_id| is the only one for |origin|.
449 // TODO(nhiroki): Check the uniqueness by more efficient way.
450 std::vector<RegistrationData> registrations;
451 if (!GetRegistrationsForOrigin(origin, &registrations))
452 return false;
453 if (registrations.size() == 1 &&
454 registrations[0].registration_id == registration_id) {
455 batch.Delete(CreateUniqueOriginKey(origin));
458 // Delete a registration specified by |registration_id|.
459 batch.Delete(CreateRegistrationKey(registration_id, origin));
461 // Delete resource records associated with the registration.
462 for (std::vector<RegistrationData>::const_iterator itr =
463 registrations.begin(); itr != registrations.end(); ++itr) {
464 if (itr->registration_id == registration_id) {
465 if (!DeleteResourceRecords(itr->version_id, &batch))
466 return false;
467 break;
471 return WriteBatch(&batch);
474 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
475 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
478 bool ServiceWorkerDatabase::WriteUncommittedResourceIds(
479 const std::set<int64>& ids) {
480 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
483 bool ServiceWorkerDatabase::ClearUncommittedResourceIds(
484 const std::set<int64>& ids) {
485 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
488 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
489 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
492 bool ServiceWorkerDatabase::WritePurgeableResourceIds(
493 const std::set<int64>& ids) {
494 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
497 bool ServiceWorkerDatabase::ClearPurgeableResourceIds(
498 const std::set<int64>& ids) {
499 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
502 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) {
503 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
504 if (IsOpen())
505 return true;
507 // Do not try to open a database if we tried and failed once.
508 if (is_disabled_)
509 return false;
511 // When |path_| is empty, open a database in-memory.
512 bool use_in_memory_db = path_.empty();
514 if (!create_if_needed) {
515 // Avoid opening a database if it does not exist at the |path_|.
516 if (use_in_memory_db ||
517 !base::PathExists(path_) ||
518 base::IsDirectoryEmpty(path_)) {
519 return false;
523 leveldb::Options options;
524 options.create_if_missing = create_if_needed;
525 if (use_in_memory_db) {
526 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
527 options.env = env_.get();
530 leveldb::DB* db = NULL;
531 leveldb::Status status =
532 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
533 if (!status.ok()) {
534 DCHECK(!db);
535 // TODO(nhiroki): Should we retry to open the database?
536 HandleError(FROM_HERE, status);
537 return false;
539 db_.reset(db);
541 int64 db_version;
542 if (!ReadDatabaseVersion(&db_version))
543 return false;
544 if (db_version > 0)
545 is_initialized_ = true;
546 return true;
549 bool ServiceWorkerDatabase::ReadNextAvailableId(
550 const char* id_key, int64* next_avail_id) {
551 DCHECK(id_key);
552 DCHECK(next_avail_id);
554 std::string value;
555 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value);
556 if (status.IsNotFound()) {
557 // Nobody has gotten the next resource id for |id_key|.
558 *next_avail_id = 0;
559 return true;
562 if (!status.ok()) {
563 HandleError(FROM_HERE, status);
564 return false;
567 int64 parsed;
568 if (!base::StringToInt64(value, &parsed)) {
569 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
570 return false;
573 *next_avail_id = parsed;
574 return true;
577 bool ServiceWorkerDatabase::ReadRegistrationData(
578 int64 registration_id,
579 const GURL& origin,
580 RegistrationData* registration) {
581 DCHECK(registration);
583 std::string key = CreateRegistrationKey(registration_id, origin);
585 std::string value;
586 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value);
587 if (!status.ok()) {
588 if (!status.IsNotFound())
589 HandleError(FROM_HERE, status);
590 return false;
593 RegistrationData parsed;
594 if (!ParseRegistrationData(value, &parsed)) {
595 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
596 return false;
599 *registration = parsed;
600 return true;
603 bool ServiceWorkerDatabase::ReadResourceRecords(
604 int64 version_id,
605 std::vector<ResourceRecord>* resources) {
606 DCHECK(resources);
608 std::string prefix = CreateResourceRecordKeyPrefix(version_id);
609 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
610 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
611 if (!itr->status().ok()) {
612 HandleError(FROM_HERE, itr->status());
613 resources->clear();
614 return false;
617 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
618 break;
620 ResourceRecord resource;
621 if (!ParseResourceRecord(itr->value().ToString(), &resource)) {
622 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
623 resources->clear();
624 return false;
626 resources->push_back(resource);
628 return true;
631 bool ServiceWorkerDatabase::DeleteResourceRecords(
632 int64 version_id,
633 leveldb::WriteBatch* batch) {
634 DCHECK(batch);
636 std::string prefix = CreateResourceRecordKeyPrefix(version_id);
637 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
638 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
639 if (!itr->status().ok()) {
640 HandleError(FROM_HERE, itr->status());
641 return false;
644 std::string key = itr->key().ToString();
645 std::string unprefixed;
646 if (!RemovePrefix(key, prefix, &unprefixed))
647 break;
649 int64 resource_id;
650 if (!base::StringToInt64(unprefixed, &resource_id)) {
651 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
652 return false;
655 // Remove a resource record.
656 batch->Delete(key);
658 // Currently resource sharing across versions and registrations is not
659 // supported, so we can purge this without caring about it.
660 PutPurgeableResourceIdToBatch(resource_id, batch);
662 return true;
665 bool ServiceWorkerDatabase::ReadResourceIds(const char* id_key_prefix,
666 std::set<int64>* ids) {
667 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
668 DCHECK(id_key_prefix);
669 DCHECK(ids);
671 if (!LazyOpen(false) || is_disabled_)
672 return false;
674 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
675 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
676 if (!itr->status().ok()) {
677 HandleError(FROM_HERE, itr->status());
678 ids->clear();
679 return false;
682 std::string unprefixed;
683 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
684 break;
686 int64 resource_id;
687 if (!base::StringToInt64(unprefixed, &resource_id)) {
688 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
689 ids->clear();
690 return false;
692 ids->insert(resource_id);
694 return true;
697 bool ServiceWorkerDatabase::WriteResourceIds(const char* id_key_prefix,
698 const std::set<int64>& ids) {
699 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
700 DCHECK(id_key_prefix);
702 if (!LazyOpen(true) || is_disabled_)
703 return false;
704 if (ids.empty())
705 return true;
707 leveldb::WriteBatch batch;
708 for (std::set<int64>::const_iterator itr = ids.begin();
709 itr != ids.end(); ++itr) {
710 // Value should be empty.
711 batch.Put(CreateResourceIdKey(id_key_prefix, *itr), "");
713 return WriteBatch(&batch);
716 bool ServiceWorkerDatabase::DeleteResourceIds(const char* id_key_prefix,
717 const std::set<int64>& ids) {
718 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
719 DCHECK(id_key_prefix);
721 if (!LazyOpen(true) || is_disabled_)
722 return false;
723 if (ids.empty())
724 return true;
726 leveldb::WriteBatch batch;
727 for (std::set<int64>::const_iterator itr = ids.begin();
728 itr != ids.end(); ++itr) {
729 batch.Delete(CreateResourceIdKey(id_key_prefix, *itr));
731 return WriteBatch(&batch);
734 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) {
735 std::string value;
736 leveldb::Status status =
737 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
738 if (status.IsNotFound()) {
739 // The database hasn't been initialized yet.
740 *db_version = 0;
741 return true;
743 if (!status.ok()) {
744 HandleError(FROM_HERE, status);
745 return false;
748 int64 parsed;
749 if (!base::StringToInt64(value, &parsed)) {
750 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
751 return false;
754 const int kFirstValidVersion = 1;
755 if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) {
756 HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version"));
757 return false;
760 *db_version = parsed;
761 return true;
764 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
765 DCHECK(batch);
766 DCHECK(!is_disabled_);
768 if (!is_initialized_) {
769 // Write the database schema version.
770 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
771 is_initialized_ = true;
774 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
775 if (!status.ok()) {
776 HandleError(FROM_HERE, status);
777 return false;
779 return true;
782 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
783 int64 used_id, leveldb::WriteBatch* batch) {
784 DCHECK(batch);
785 if (next_avail_registration_id_ <= used_id) {
786 next_avail_registration_id_ = used_id + 1;
787 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
791 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
792 int64 used_id, leveldb::WriteBatch* batch) {
793 DCHECK(batch);
794 if (next_avail_version_id_ <= used_id) {
795 next_avail_version_id_ = used_id + 1;
796 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
800 bool ServiceWorkerDatabase::IsOpen() {
801 return db_.get() != NULL;
804 void ServiceWorkerDatabase::HandleError(
805 const tracked_objects::Location& from_here,
806 const leveldb::Status& status) {
807 // TODO(nhiroki): Add an UMA histogram.
808 DLOG(ERROR) << "Failed at: " << from_here.ToString()
809 << " with error: " << status.ToString();
810 is_disabled_ = true;
811 if (status.IsCorruption())
812 was_corruption_detected_ = true;
813 db_.reset();
816 } // namespace content