Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_database.cc
blobcd0d959ef473ab200f9e346b28a31d46bef5a632
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/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "content/browser/service_worker/service_worker_database.pb.h"
19 #include "content/browser/service_worker/service_worker_metrics.h"
20 #include "content/common/service_worker/service_worker_types.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: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
57 // (ex. "RES:123456\x00654321")
58 // value: <ServiceWorkerResourceRecord serialized as a string>
60 // key: "URES:" + <int64 'uncommitted_resource_id'>
61 // value: <empty>
63 namespace content {
65 namespace {
67 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
68 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
69 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
70 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
71 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
73 const char kRegKeyPrefix[] = "REG:";
74 const char kResKeyPrefix[] = "RES:";
75 const char kKeySeparator = '\x00';
77 const char kUncommittedResIdKeyPrefix[] = "URES:";
78 const char kPurgeableResIdKeyPrefix[] = "PRES:";
80 const int64 kCurrentSchemaVersion = 1;
82 bool RemovePrefix(const std::string& str,
83 const std::string& prefix,
84 std::string* out) {
85 if (!StartsWithASCII(str, prefix, true))
86 return false;
87 if (out)
88 *out = str.substr(prefix.size());
89 return true;
92 std::string CreateRegistrationKey(int64 registration_id,
93 const GURL& origin) {
94 return base::StringPrintf("%s%s%c%s",
95 kRegKeyPrefix,
96 origin.spec().c_str(),
97 kKeySeparator,
98 base::Int64ToString(registration_id).c_str());
101 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
102 return base::StringPrintf("%s%s%c",
103 kResKeyPrefix,
104 base::Int64ToString(version_id).c_str(),
105 kKeySeparator);
108 std::string CreateResourceRecordKey(int64 version_id,
109 int64 resource_id) {
110 return CreateResourceRecordKeyPrefix(version_id).append(
111 base::Int64ToString(resource_id));
114 std::string CreateUniqueOriginKey(const GURL& origin) {
115 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
118 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
119 return base::StringPrintf(
120 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
123 void PutRegistrationDataToBatch(
124 const ServiceWorkerDatabase::RegistrationData& input,
125 leveldb::WriteBatch* batch) {
126 DCHECK(batch);
128 // Convert RegistrationData to ServiceWorkerRegistrationData.
129 ServiceWorkerRegistrationData data;
130 data.set_registration_id(input.registration_id);
131 data.set_scope_url(input.scope.spec());
132 data.set_script_url(input.script.spec());
133 data.set_version_id(input.version_id);
134 data.set_is_active(input.is_active);
135 data.set_has_fetch_handler(input.has_fetch_handler);
136 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
138 std::string value;
139 bool success = data.SerializeToString(&value);
140 DCHECK(success);
141 GURL origin = input.scope.GetOrigin();
142 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
145 void PutResourceRecordToBatch(
146 const ServiceWorkerDatabase::ResourceRecord& input,
147 int64 version_id,
148 leveldb::WriteBatch* batch) {
149 DCHECK(batch);
151 // Convert ResourceRecord to ServiceWorkerResourceRecord.
152 ServiceWorkerResourceRecord record;
153 record.set_resource_id(input.resource_id);
154 record.set_url(input.url.spec());
156 std::string value;
157 bool success = record.SerializeToString(&value);
158 DCHECK(success);
159 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
162 void PutUniqueOriginToBatch(const GURL& origin,
163 leveldb::WriteBatch* batch) {
164 // Value should be empty.
165 batch->Put(CreateUniqueOriginKey(origin), "");
168 void PutPurgeableResourceIdToBatch(int64 resource_id,
169 leveldb::WriteBatch* batch) {
170 // Value should be empty.
171 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
174 ServiceWorkerDatabase::Status ParseId(
175 const std::string& serialized,
176 int64* out) {
177 DCHECK(out);
178 int64 id;
179 if (!base::StringToInt64(serialized, &id) || id < 0)
180 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
181 *out = id;
182 return ServiceWorkerDatabase::STATUS_OK;
185 ServiceWorkerDatabase::Status ParseDatabaseVersion(
186 const std::string& serialized,
187 int64* out) {
188 DCHECK(out);
189 const int kFirstValidVersion = 1;
190 int64 version;
191 if (!base::StringToInt64(serialized, &version) ||
192 version < kFirstValidVersion) {
193 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
195 if (kCurrentSchemaVersion < version) {
196 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
197 << " than the current latest version: "
198 << version << " vs " << kCurrentSchemaVersion;
199 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
201 *out = version;
202 return ServiceWorkerDatabase::STATUS_OK;
205 ServiceWorkerDatabase::Status ParseRegistrationData(
206 const std::string& serialized,
207 ServiceWorkerDatabase::RegistrationData* out) {
208 DCHECK(out);
209 ServiceWorkerRegistrationData data;
210 if (!data.ParseFromString(serialized))
211 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
213 GURL scope_url(data.scope_url());
214 GURL script_url(data.script_url());
215 if (!scope_url.is_valid() ||
216 !script_url.is_valid() ||
217 scope_url.GetOrigin() != script_url.GetOrigin()) {
218 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
221 // Convert ServiceWorkerRegistrationData to RegistrationData.
222 out->registration_id = data.registration_id();
223 out->scope = scope_url;
224 out->script = script_url;
225 out->version_id = data.version_id();
226 out->is_active = data.is_active();
227 out->has_fetch_handler = data.has_fetch_handler();
228 out->last_update_check =
229 base::Time::FromInternalValue(data.last_update_check_time());
230 return ServiceWorkerDatabase::STATUS_OK;
233 ServiceWorkerDatabase::Status ParseResourceRecord(
234 const std::string& serialized,
235 ServiceWorkerDatabase::ResourceRecord* out) {
236 DCHECK(out);
237 ServiceWorkerResourceRecord record;
238 if (!record.ParseFromString(serialized))
239 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
241 GURL url(record.url());
242 if (!url.is_valid())
243 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
245 // Convert ServiceWorkerResourceRecord to ResourceRecord.
246 out->resource_id = record.resource_id();
247 out->url = url;
248 return ServiceWorkerDatabase::STATUS_OK;
251 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
252 const leveldb::Status& status) {
253 if (status.ok())
254 return ServiceWorkerDatabase::STATUS_OK;
255 else if (status.IsNotFound())
256 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
257 else if (status.IsIOError())
258 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
259 else if (status.IsCorruption())
260 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
261 else
262 return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
265 const char* StatusToString(ServiceWorkerDatabase::Status status) {
266 switch (status) {
267 case ServiceWorkerDatabase::STATUS_OK:
268 return "Database OK";
269 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
270 return "Database not found";
271 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
272 return "Database IO error";
273 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
274 return "Database corrupted";
275 case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
276 return "Database operation failed";
277 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
278 NOTREACHED();
279 return "Database unknown error";
281 NOTREACHED();
282 return "Database unknown error";
285 } // namespace
287 ServiceWorkerDatabase::RegistrationData::RegistrationData()
288 : registration_id(kInvalidServiceWorkerRegistrationId),
289 version_id(kInvalidServiceWorkerVersionId),
290 is_active(false),
291 has_fetch_handler(false) {
294 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
297 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
298 : path_(path),
299 next_avail_registration_id_(0),
300 next_avail_resource_id_(0),
301 next_avail_version_id_(0),
302 state_(UNINITIALIZED) {
303 sequence_checker_.DetachFromSequence();
306 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
307 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
308 db_.reset();
311 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
312 int64* next_avail_registration_id,
313 int64* next_avail_version_id,
314 int64* next_avail_resource_id) {
315 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
316 DCHECK(next_avail_registration_id);
317 DCHECK(next_avail_version_id);
318 DCHECK(next_avail_resource_id);
320 Status status = LazyOpen(false);
321 if (IsNewOrNonexistentDatabase(status)) {
322 *next_avail_registration_id = 0;
323 *next_avail_version_id = 0;
324 *next_avail_resource_id = 0;
325 return STATUS_OK;
327 if (status != STATUS_OK)
328 return status;
330 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
331 if (status != STATUS_OK)
332 return status;
333 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
334 if (status != STATUS_OK)
335 return status;
336 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
337 if (status != STATUS_OK)
338 return status;
340 *next_avail_registration_id = next_avail_registration_id_;
341 *next_avail_version_id = next_avail_version_id_;
342 *next_avail_resource_id = next_avail_resource_id_;
343 return STATUS_OK;
346 ServiceWorkerDatabase::Status
347 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
348 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
349 DCHECK(origins->empty());
351 Status status = LazyOpen(false);
352 if (IsNewOrNonexistentDatabase(status))
353 return STATUS_OK;
354 if (status != STATUS_OK)
355 return status;
357 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
358 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
359 status = LevelDBStatusToStatus(itr->status());
360 if (status != STATUS_OK) {
361 HandleReadResult(FROM_HERE, status);
362 origins->clear();
363 return status;
366 std::string origin;
367 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
368 break;
369 origins->insert(GURL(origin));
372 HandleReadResult(FROM_HERE, status);
373 return status;
376 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
377 const GURL& origin,
378 std::vector<RegistrationData>* registrations) {
379 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
380 DCHECK(registrations->empty());
382 Status status = LazyOpen(false);
383 if (IsNewOrNonexistentDatabase(status))
384 return STATUS_OK;
385 if (status != STATUS_OK)
386 return status;
388 // Create a key prefix for registrations.
389 std::string prefix = base::StringPrintf(
390 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
392 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
393 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
394 status = LevelDBStatusToStatus(itr->status());
395 if (status != STATUS_OK) {
396 HandleReadResult(FROM_HERE, status);
397 registrations->clear();
398 return status;
401 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
402 break;
404 RegistrationData registration;
405 status = ParseRegistrationData(itr->value().ToString(), &registration);
406 if (status != STATUS_OK) {
407 HandleReadResult(FROM_HERE, status);
408 registrations->clear();
409 return status;
411 registrations->push_back(registration);
414 HandleReadResult(FROM_HERE, status);
415 return status;
418 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
419 std::vector<RegistrationData>* registrations) {
420 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
421 DCHECK(registrations->empty());
423 Status status = LazyOpen(false);
424 if (IsNewOrNonexistentDatabase(status))
425 return STATUS_OK;
426 if (status != STATUS_OK)
427 return status;
429 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
430 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
431 status = LevelDBStatusToStatus(itr->status());
432 if (status != STATUS_OK) {
433 HandleReadResult(FROM_HERE, status);
434 registrations->clear();
435 return status;
438 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
439 break;
441 RegistrationData registration;
442 status = ParseRegistrationData(itr->value().ToString(), &registration);
443 if (status != STATUS_OK) {
444 HandleReadResult(FROM_HERE, status);
445 registrations->clear();
446 return status;
448 registrations->push_back(registration);
451 HandleReadResult(FROM_HERE, status);
452 return status;
455 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
456 int64 registration_id,
457 const GURL& origin,
458 RegistrationData* registration,
459 std::vector<ResourceRecord>* resources) {
460 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
461 DCHECK(registration);
462 DCHECK(resources);
464 Status status = LazyOpen(false);
465 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
466 return status;
468 RegistrationData value;
469 status = ReadRegistrationData(registration_id, origin, &value);
470 if (status != STATUS_OK)
471 return status;
473 status = ReadResourceRecords(value.version_id, resources);
474 if (status != STATUS_OK)
475 return status;
477 *registration = value;
478 return STATUS_OK;
481 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
482 const RegistrationData& registration,
483 const std::vector<ResourceRecord>& resources,
484 int64* deleted_version_id,
485 std::vector<int64>* newly_purgeable_resources) {
486 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
487 *deleted_version_id = kInvalidServiceWorkerVersionId;
488 Status status = LazyOpen(true);
489 if (status != STATUS_OK)
490 return status;
492 leveldb::WriteBatch batch;
493 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
494 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
496 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
497 PutRegistrationDataToBatch(registration, &batch);
499 // Used for avoiding multiple writes for the same resource id or url.
500 std::set<int64> pushed_resources;
501 std::set<GURL> pushed_urls;
502 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
503 itr != resources.end(); ++itr) {
504 if (!itr->url.is_valid())
505 return STATUS_ERROR_FAILED;
507 // Duplicated resource id or url should not exist.
508 DCHECK(pushed_resources.insert(itr->resource_id).second);
509 DCHECK(pushed_urls.insert(itr->url).second);
511 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
513 // Delete a resource from the uncommitted list.
514 batch.Delete(CreateResourceIdKey(
515 kUncommittedResIdKeyPrefix, itr->resource_id));
516 // Delete from the purgeable list in case this version was once deleted.
517 batch.Delete(
518 CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id));
521 // Retrieve a previous version to sweep purgeable resources.
522 RegistrationData old_registration;
523 status = ReadRegistrationData(registration.registration_id,
524 registration.scope.GetOrigin(),
525 &old_registration);
526 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
527 return status;
528 if (status == STATUS_OK) {
529 DCHECK_LT(old_registration.version_id, registration.version_id);
530 *deleted_version_id = old_registration.version_id;
531 status = DeleteResourceRecords(
532 old_registration.version_id, newly_purgeable_resources, &batch);
533 if (status != STATUS_OK)
534 return status;
536 // Currently resource sharing across versions and registrations is not
537 // supported, so resource ids should not be overlapped between
538 // |registration| and |old_registration|.
539 std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
540 newly_purgeable_resources->end());
541 DCHECK(base::STLSetIntersection<std::set<int64> >(
542 pushed_resources, deleted_resources).empty());
545 return WriteBatch(&batch);
548 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
549 int64 registration_id,
550 const GURL& origin) {
551 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
552 Status status = LazyOpen(false);
553 if (IsNewOrNonexistentDatabase(status))
554 return STATUS_ERROR_NOT_FOUND;
555 if (status != STATUS_OK)
556 return status;
557 if (!origin.is_valid())
558 return STATUS_ERROR_FAILED;
560 RegistrationData registration;
561 status = ReadRegistrationData(registration_id, origin, &registration);
562 if (status != STATUS_OK)
563 return status;
565 registration.is_active = true;
567 leveldb::WriteBatch batch;
568 PutRegistrationDataToBatch(registration, &batch);
569 return WriteBatch(&batch);
572 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
573 int64 registration_id,
574 const GURL& origin,
575 const base::Time& time) {
576 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
577 Status status = LazyOpen(false);
578 if (IsNewOrNonexistentDatabase(status))
579 return STATUS_ERROR_NOT_FOUND;
580 if (status != STATUS_OK)
581 return status;
582 if (!origin.is_valid())
583 return STATUS_ERROR_FAILED;
585 RegistrationData registration;
586 status = ReadRegistrationData(registration_id, origin, &registration);
587 if (status != STATUS_OK)
588 return status;
590 registration.last_update_check = time;
592 leveldb::WriteBatch batch;
593 PutRegistrationDataToBatch(registration, &batch);
594 return WriteBatch(&batch);
597 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
598 int64 registration_id,
599 const GURL& origin,
600 int64* version_id,
601 std::vector<int64>* newly_purgeable_resources) {
602 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
603 *version_id = kInvalidServiceWorkerVersionId;
604 Status status = LazyOpen(false);
605 if (IsNewOrNonexistentDatabase(status))
606 return STATUS_OK;
607 if (status != STATUS_OK)
608 return status;
609 if (!origin.is_valid())
610 return STATUS_ERROR_FAILED;
612 leveldb::WriteBatch batch;
614 // Remove |origin| from unique origins if a registration specified by
615 // |registration_id| is the only one for |origin|.
616 // TODO(nhiroki): Check the uniqueness by more efficient way.
617 std::vector<RegistrationData> registrations;
618 status = GetRegistrationsForOrigin(origin, &registrations);
619 if (status != STATUS_OK)
620 return status;
622 if (registrations.size() == 1 &&
623 registrations[0].registration_id == registration_id) {
624 batch.Delete(CreateUniqueOriginKey(origin));
627 // Delete a registration specified by |registration_id|.
628 batch.Delete(CreateRegistrationKey(registration_id, origin));
630 // Delete resource records associated with the registration.
631 for (std::vector<RegistrationData>::const_iterator itr =
632 registrations.begin(); itr != registrations.end(); ++itr) {
633 if (itr->registration_id == registration_id) {
634 *version_id = itr->version_id;
635 status = DeleteResourceRecords(
636 itr->version_id, newly_purgeable_resources, &batch);
637 if (status != STATUS_OK)
638 return status;
639 break;
643 return WriteBatch(&batch);
646 ServiceWorkerDatabase::Status
647 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
648 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
651 ServiceWorkerDatabase::Status
652 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
653 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
656 ServiceWorkerDatabase::Status
657 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
658 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
661 ServiceWorkerDatabase::Status
662 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
663 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
666 ServiceWorkerDatabase::Status
667 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
668 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
671 ServiceWorkerDatabase::Status
672 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
673 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
676 ServiceWorkerDatabase::Status
677 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
678 const std::set<int64>& ids) {
679 leveldb::WriteBatch batch;
680 Status status = DeleteResourceIdsInBatch(
681 kUncommittedResIdKeyPrefix, ids, &batch);
682 if (status != STATUS_OK)
683 return status;
684 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
685 if (status != STATUS_OK)
686 return status;
687 return WriteBatch(&batch);
690 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigin(
691 const GURL& origin,
692 std::vector<int64>* newly_purgeable_resources) {
693 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
694 Status status = LazyOpen(false);
695 if (IsNewOrNonexistentDatabase(status))
696 return STATUS_OK;
697 if (status != STATUS_OK)
698 return status;
699 if (!origin.is_valid())
700 return STATUS_ERROR_FAILED;
702 leveldb::WriteBatch batch;
704 // Delete from the unique origin list.
705 batch.Delete(CreateUniqueOriginKey(origin));
707 std::vector<RegistrationData> registrations;
708 status = GetRegistrationsForOrigin(origin, &registrations);
709 if (status != STATUS_OK)
710 return status;
712 // Delete registrations and resource records.
713 for (std::vector<RegistrationData>::const_iterator itr =
714 registrations.begin(); itr != registrations.end(); ++itr) {
715 batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
716 status = DeleteResourceRecords(
717 itr->version_id, newly_purgeable_resources, &batch);
718 if (status != STATUS_OK)
719 return status;
722 return WriteBatch(&batch);
725 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
726 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
727 Disable(FROM_HERE, STATUS_OK);
728 return LevelDBStatusToStatus(
729 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options()));
732 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
733 bool create_if_missing) {
734 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
736 // Do not try to open a database if we tried and failed once.
737 if (state_ == DISABLED)
738 return STATUS_ERROR_FAILED;
739 if (IsOpen())
740 return STATUS_OK;
742 // When |path_| is empty, open a database in-memory.
743 bool use_in_memory_db = path_.empty();
745 if (!create_if_missing) {
746 // Avoid opening a database if it does not exist at the |path_|.
747 if (use_in_memory_db ||
748 !base::PathExists(path_) ||
749 base::IsDirectoryEmpty(path_)) {
750 return STATUS_ERROR_NOT_FOUND;
754 leveldb::Options options;
755 options.create_if_missing = create_if_missing;
756 if (use_in_memory_db) {
757 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
758 options.env = env_.get();
761 leveldb::DB* db = NULL;
762 Status status = LevelDBStatusToStatus(
763 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
764 HandleOpenResult(FROM_HERE, status);
765 if (status != STATUS_OK) {
766 DCHECK(!db);
767 // TODO(nhiroki): Should we retry to open the database?
768 return status;
770 db_.reset(db);
772 int64 db_version;
773 status = ReadDatabaseVersion(&db_version);
774 if (status != STATUS_OK)
775 return status;
776 DCHECK_LE(0, db_version);
777 if (db_version > 0)
778 state_ = INITIALIZED;
779 return STATUS_OK;
782 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
783 ServiceWorkerDatabase::Status status) {
784 if (status == STATUS_ERROR_NOT_FOUND)
785 return true;
786 if (status == STATUS_OK && state_ == UNINITIALIZED)
787 return true;
788 return false;
791 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
792 const char* id_key,
793 int64* next_avail_id) {
794 DCHECK(id_key);
795 DCHECK(next_avail_id);
797 std::string value;
798 Status status = LevelDBStatusToStatus(
799 db_->Get(leveldb::ReadOptions(), id_key, &value));
800 if (status == STATUS_ERROR_NOT_FOUND) {
801 // Nobody has gotten the next resource id for |id_key|.
802 *next_avail_id = 0;
803 HandleReadResult(FROM_HERE, STATUS_OK);
804 return STATUS_OK;
805 } else if (status != STATUS_OK) {
806 HandleReadResult(FROM_HERE, status);
807 return status;
810 status = ParseId(value, next_avail_id);
811 HandleReadResult(FROM_HERE, status);
812 return status;
815 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
816 int64 registration_id,
817 const GURL& origin,
818 RegistrationData* registration) {
819 DCHECK(registration);
821 const std::string key = CreateRegistrationKey(registration_id, origin);
822 std::string value;
823 Status status = LevelDBStatusToStatus(
824 db_->Get(leveldb::ReadOptions(), key, &value));
825 if (status != STATUS_OK) {
826 HandleReadResult(
827 FROM_HERE,
828 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
829 return status;
832 status = ParseRegistrationData(value, registration);
833 HandleReadResult(FROM_HERE, status);
834 return status;
837 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
838 int64 version_id,
839 std::vector<ResourceRecord>* resources) {
840 DCHECK(resources->empty());
842 Status status = STATUS_OK;
843 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
845 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
846 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
847 Status status = LevelDBStatusToStatus(itr->status());
848 if (status != STATUS_OK) {
849 HandleReadResult(FROM_HERE, status);
850 resources->clear();
851 return status;
854 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
855 break;
857 ResourceRecord resource;
858 status = ParseResourceRecord(itr->value().ToString(), &resource);
859 if (status != STATUS_OK) {
860 HandleReadResult(FROM_HERE, status);
861 resources->clear();
862 return status;
864 resources->push_back(resource);
867 HandleReadResult(FROM_HERE, status);
868 return status;
871 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
872 int64 version_id,
873 std::vector<int64>* newly_purgeable_resources,
874 leveldb::WriteBatch* batch) {
875 DCHECK(batch);
877 Status status = STATUS_OK;
878 const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
880 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
881 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
882 status = LevelDBStatusToStatus(itr->status());
883 if (status != STATUS_OK) {
884 HandleReadResult(FROM_HERE, status);
885 return status;
888 const std::string key = itr->key().ToString();
889 std::string unprefixed;
890 if (!RemovePrefix(key, prefix, &unprefixed))
891 break;
893 int64 resource_id;
894 status = ParseId(unprefixed, &resource_id);
895 if (status != STATUS_OK) {
896 HandleReadResult(FROM_HERE, status);
897 return status;
900 // Remove a resource record.
901 batch->Delete(key);
903 // Currently resource sharing across versions and registrations is not
904 // supported, so we can purge this without caring about it.
905 PutPurgeableResourceIdToBatch(resource_id, batch);
906 newly_purgeable_resources->push_back(resource_id);
909 HandleReadResult(FROM_HERE, status);
910 return status;
913 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
914 const char* id_key_prefix,
915 std::set<int64>* ids) {
916 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
917 DCHECK(id_key_prefix);
918 DCHECK(ids->empty());
920 Status status = LazyOpen(false);
921 if (IsNewOrNonexistentDatabase(status))
922 return STATUS_OK;
923 if (status != STATUS_OK)
924 return status;
926 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
927 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
928 status = LevelDBStatusToStatus(itr->status());
929 if (status != STATUS_OK) {
930 HandleReadResult(FROM_HERE, status);
931 ids->clear();
932 return status;
935 std::string unprefixed;
936 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
937 break;
939 int64 resource_id;
940 status = ParseId(unprefixed, &resource_id);
941 if (status != STATUS_OK) {
942 HandleReadResult(FROM_HERE, status);
943 ids->clear();
944 return status;
946 ids->insert(resource_id);
949 HandleReadResult(FROM_HERE, status);
950 return status;
953 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
954 const char* id_key_prefix,
955 const std::set<int64>& ids) {
956 leveldb::WriteBatch batch;
957 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
958 if (status != STATUS_OK)
959 return status;
960 return WriteBatch(&batch);
963 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
964 const char* id_key_prefix,
965 const std::set<int64>& ids,
966 leveldb::WriteBatch* batch) {
967 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
968 DCHECK(id_key_prefix);
970 Status status = LazyOpen(true);
971 if (status != STATUS_OK)
972 return status;
974 if (ids.empty())
975 return STATUS_OK;
976 for (std::set<int64>::const_iterator itr = ids.begin();
977 itr != ids.end(); ++itr) {
978 // Value should be empty.
979 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
981 // std::set is sorted, so the last element is the largest.
982 BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
983 return STATUS_OK;
986 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
987 const char* id_key_prefix,
988 const std::set<int64>& ids) {
989 leveldb::WriteBatch batch;
990 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
991 if (status != STATUS_OK)
992 return status;
993 return WriteBatch(&batch);
996 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
997 const char* id_key_prefix,
998 const std::set<int64>& ids,
999 leveldb::WriteBatch* batch) {
1000 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
1001 DCHECK(id_key_prefix);
1003 Status status = LazyOpen(false);
1004 if (IsNewOrNonexistentDatabase(status))
1005 return STATUS_OK;
1006 if (status != STATUS_OK)
1007 return status;
1009 for (std::set<int64>::const_iterator itr = ids.begin();
1010 itr != ids.end(); ++itr) {
1011 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1013 return STATUS_OK;
1016 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1017 int64* db_version) {
1018 std::string value;
1019 Status status = LevelDBStatusToStatus(
1020 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1021 if (status == STATUS_ERROR_NOT_FOUND) {
1022 // The database hasn't been initialized yet.
1023 *db_version = 0;
1024 HandleReadResult(FROM_HERE, STATUS_OK);
1025 return STATUS_OK;
1028 if (status != STATUS_OK) {
1029 HandleReadResult(FROM_HERE, status);
1030 return status;
1033 status = ParseDatabaseVersion(value, db_version);
1034 HandleReadResult(FROM_HERE, status);
1035 return status;
1038 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1039 leveldb::WriteBatch* batch) {
1040 DCHECK(batch);
1041 DCHECK_NE(DISABLED, state_);
1043 if (state_ == UNINITIALIZED) {
1044 // Write the database schema version.
1045 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1046 state_ = INITIALIZED;
1049 Status status = LevelDBStatusToStatus(
1050 db_->Write(leveldb::WriteOptions(), batch));
1051 HandleWriteResult(FROM_HERE, status);
1052 return status;
1055 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1056 int64 used_id, leveldb::WriteBatch* batch) {
1057 DCHECK(batch);
1058 if (next_avail_registration_id_ <= used_id) {
1059 next_avail_registration_id_ = used_id + 1;
1060 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1064 void ServiceWorkerDatabase::BumpNextResourceIdIfNeeded(
1065 int64 used_id, leveldb::WriteBatch* batch) {
1066 DCHECK(batch);
1067 if (next_avail_resource_id_ <= used_id) {
1068 next_avail_resource_id_ = used_id + 1;
1069 batch->Put(kNextResIdKey, base::Int64ToString(next_avail_resource_id_));
1073 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1074 int64 used_id, leveldb::WriteBatch* batch) {
1075 DCHECK(batch);
1076 if (next_avail_version_id_ <= used_id) {
1077 next_avail_version_id_ = used_id + 1;
1078 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1082 bool ServiceWorkerDatabase::IsOpen() {
1083 return db_ != NULL;
1086 void ServiceWorkerDatabase::Disable(
1087 const tracked_objects::Location& from_here,
1088 Status status) {
1089 if (status != STATUS_OK) {
1090 DLOG(ERROR) << "Failed at: " << from_here.ToString()
1091 << " with error: " << StatusToString(status);
1092 DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1094 state_ = DISABLED;
1095 db_.reset();
1098 void ServiceWorkerDatabase::HandleOpenResult(
1099 const tracked_objects::Location& from_here,
1100 Status status) {
1101 if (status != STATUS_OK)
1102 Disable(from_here, status);
1103 ServiceWorkerMetrics::CountOpenDatabaseResult(status);
1106 void ServiceWorkerDatabase::HandleReadResult(
1107 const tracked_objects::Location& from_here,
1108 Status status) {
1109 if (status != STATUS_OK)
1110 Disable(from_here, status);
1111 ServiceWorkerMetrics::CountReadDatabaseResult(status);
1114 void ServiceWorkerDatabase::HandleWriteResult(
1115 const tracked_objects::Location& from_here,
1116 Status status) {
1117 if (status != STATUS_OK)
1118 Disable(from_here, status);
1119 ServiceWorkerMetrics::CountWriteDatabaseResult(status);
1122 } // namespace content