Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_database.cc
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.
4
5 #include "content/browser/service_worker/service_worker_database.h"
6
7 #include <string>
8
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/common/service_worker/service_worker_types.h"
20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
21 #include "third_party/leveldatabase/src/include/leveldb/db.h"
22 #include "third_party/leveldatabase/src/include/leveldb/env.h"
23 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
24
25 // LevelDB database schema
26 // =======================
27 //
28 // NOTE
29 // - int64 value is serialized as a string by base::Int64ToString().
30 // - GURL value is serialized as a string by GURL::spec().
31 //
32 // Version 1 (in sorted order)
33 //   key: "INITDATA_DB_VERSION"
34 //   value: "1"
35 //
36 //   key: "INITDATA_NEXT_REGISTRATION_ID"
37 //   value: <int64 'next_available_registration_id'>
38 //
39 //   key: "INITDATA_NEXT_RESOURCE_ID"
40 //   value: <int64 'next_available_resource_id'>
41 //
42 //   key: "INITDATA_NEXT_VERSION_ID"
43 //   value: <int64 'next_available_version_id'>
44 //
45 //   key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
46 //   value: <empty>
47 //
48 //   key: "PRES:" + <int64 'purgeable_resource_id'>
49 //   value: <empty>
50 //
51 //   key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
52 //     (ex. "REG:http://example.com\x00123456")
53 //   value: <ServiceWorkerRegistrationData serialized as a string>
54 //
55 //   key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
56 //     (ex. "RES:123456\x00654321")
57 //   value: <ServiceWorkerResourceRecord serialized as a string>
58 //
59 //   key: "URES:" + <int64 'uncommitted_resource_id'>
60 //   value: <empty>
61
62 namespace content {
63
64 namespace {
65
66 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
67 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
68 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
69 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
70 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
71
72 const char kRegKeyPrefix[] = "REG:";
73 const char kResKeyPrefix[] = "RES:";
74 const char kKeySeparator = '\x00';
75
76 const char kUncommittedResIdKeyPrefix[] = "URES:";
77 const char kPurgeableResIdKeyPrefix[] = "PRES:";
78
79 const int64 kCurrentSchemaVersion = 1;
80
81 // For histogram.
82 const char kOpenResultHistogramLabel[] =
83     "ServiceWorker.Database.OpenResult";
84 const char kReadResultHistogramLabel[] =
85     "ServiceWorker.Database.ReadResult";
86 const char kWriteResultHistogramLabel[] =
87     "ServiceWorker.Database.WriteResult";
88
89 bool RemovePrefix(const std::string& str,
90                   const std::string& prefix,
91                   std::string* out) {
92   if (!StartsWithASCII(str, prefix, true))
93     return false;
94   if (out)
95     *out = str.substr(prefix.size());
96   return true;
97 }
98
99 std::string CreateRegistrationKey(int64 registration_id,
100                                   const GURL& origin) {
101   return base::StringPrintf("%s%s%c%s",
102                             kRegKeyPrefix,
103                             origin.spec().c_str(),
104                             kKeySeparator,
105                             base::Int64ToString(registration_id).c_str());
106 }
107
108 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
109   return base::StringPrintf("%s%s%c",
110                             kResKeyPrefix,
111                             base::Int64ToString(version_id).c_str(),
112                             kKeySeparator);
113 }
114
115 std::string CreateResourceRecordKey(int64 version_id,
116                                     int64 resource_id) {
117   return CreateResourceRecordKeyPrefix(version_id).append(
118       base::Int64ToString(resource_id));
119 }
120
121 std::string CreateUniqueOriginKey(const GURL& origin) {
122   return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
123 }
124
125 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
126   return base::StringPrintf(
127       "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
128 }
129
130 void PutRegistrationDataToBatch(
131     const ServiceWorkerDatabase::RegistrationData& input,
132     leveldb::WriteBatch* batch) {
133   DCHECK(batch);
134
135   // Convert RegistrationData to ServiceWorkerRegistrationData.
136   ServiceWorkerRegistrationData data;
137   data.set_registration_id(input.registration_id);
138   data.set_scope_url(input.scope.spec());
139   data.set_script_url(input.script.spec());
140   data.set_version_id(input.version_id);
141   data.set_is_active(input.is_active);
142   data.set_has_fetch_handler(input.has_fetch_handler);
143   data.set_last_update_check_time(input.last_update_check.ToInternalValue());
144
145   std::string value;
146   bool success = data.SerializeToString(&value);
147   DCHECK(success);
148   GURL origin = input.scope.GetOrigin();
149   batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
150 }
151
152 void PutResourceRecordToBatch(
153     const ServiceWorkerDatabase::ResourceRecord& input,
154     int64 version_id,
155     leveldb::WriteBatch* batch) {
156   DCHECK(batch);
157
158   // Convert ResourceRecord to ServiceWorkerResourceRecord.
159   ServiceWorkerResourceRecord record;
160   record.set_resource_id(input.resource_id);
161   record.set_url(input.url.spec());
162
163   std::string value;
164   bool success = record.SerializeToString(&value);
165   DCHECK(success);
166   batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
167 }
168
169 void PutUniqueOriginToBatch(const GURL& origin,
170                             leveldb::WriteBatch* batch) {
171   // Value should be empty.
172   batch->Put(CreateUniqueOriginKey(origin), "");
173 }
174
175 void PutPurgeableResourceIdToBatch(int64 resource_id,
176                                    leveldb::WriteBatch* batch) {
177   // Value should be empty.
178   batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
179 }
180
181 ServiceWorkerDatabase::Status ParseId(
182     const std::string& serialized,
183     int64* out) {
184   DCHECK(out);
185   int64 id;
186   if (!base::StringToInt64(serialized, &id) || id < 0)
187     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
188   *out = id;
189   return ServiceWorkerDatabase::STATUS_OK;
190 }
191
192 ServiceWorkerDatabase::Status ParseDatabaseVersion(
193     const std::string& serialized,
194     int64* out) {
195   DCHECK(out);
196   const int kFirstValidVersion = 1;
197   int64 version;
198   if (!base::StringToInt64(serialized, &version) ||
199       version < kFirstValidVersion) {
200     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
201   }
202   if (kCurrentSchemaVersion < version) {
203     DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version"
204                 << " than the current latest version: "
205                 << version << " vs " << kCurrentSchemaVersion;
206     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
207   }
208   *out = version;
209   return ServiceWorkerDatabase::STATUS_OK;
210 }
211
212 ServiceWorkerDatabase::Status ParseRegistrationData(
213     const std::string& serialized,
214     ServiceWorkerDatabase::RegistrationData* out) {
215   DCHECK(out);
216   ServiceWorkerRegistrationData data;
217   if (!data.ParseFromString(serialized))
218     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
219
220   GURL scope_url(data.scope_url());
221   GURL script_url(data.script_url());
222   if (!scope_url.is_valid() ||
223       !script_url.is_valid() ||
224       scope_url.GetOrigin() != script_url.GetOrigin()) {
225     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
226   }
227
228   // Convert ServiceWorkerRegistrationData to RegistrationData.
229   out->registration_id = data.registration_id();
230   out->scope = scope_url;
231   out->script = script_url;
232   out->version_id = data.version_id();
233   out->is_active = data.is_active();
234   out->has_fetch_handler = data.has_fetch_handler();
235   out->last_update_check =
236       base::Time::FromInternalValue(data.last_update_check_time());
237   return ServiceWorkerDatabase::STATUS_OK;
238 }
239
240 ServiceWorkerDatabase::Status ParseResourceRecord(
241     const std::string& serialized,
242     ServiceWorkerDatabase::ResourceRecord* out) {
243   DCHECK(out);
244   ServiceWorkerResourceRecord record;
245   if (!record.ParseFromString(serialized))
246     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
247
248   GURL url(record.url());
249   if (!url.is_valid())
250     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
251
252   // Convert ServiceWorkerResourceRecord to ResourceRecord.
253   out->resource_id = record.resource_id();
254   out->url = url;
255   return ServiceWorkerDatabase::STATUS_OK;
256 }
257
258 ServiceWorkerDatabase::Status LevelDBStatusToStatus(
259     const leveldb::Status& status) {
260   if (status.ok())
261     return ServiceWorkerDatabase::STATUS_OK;
262   else if (status.IsNotFound())
263     return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
264   else if (status.IsIOError())
265     return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR;
266   else if (status.IsCorruption())
267     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
268   else
269     return ServiceWorkerDatabase::STATUS_ERROR_FAILED;
270 }
271
272 const char* StatusToString(ServiceWorkerDatabase::Status status) {
273   switch (status) {
274     case ServiceWorkerDatabase::STATUS_OK:
275       return "Database OK";
276     case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
277       return "Database not found";
278     case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR:
279       return "Database IO error";
280     case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED:
281       return "Database corrupted";
282     case ServiceWorkerDatabase::STATUS_ERROR_FAILED:
283       return "Database operation failed";
284     case ServiceWorkerDatabase::STATUS_ERROR_MAX:
285       NOTREACHED();
286       return "Database unknown error";
287   }
288   NOTREACHED();
289   return "Database unknown error";
290 }
291
292 }  // namespace
293
294 ServiceWorkerDatabase::RegistrationData::RegistrationData()
295     : registration_id(kInvalidServiceWorkerRegistrationId),
296       version_id(kInvalidServiceWorkerVersionId),
297       is_active(false),
298       has_fetch_handler(false) {
299 }
300
301 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
302 }
303
304 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
305     : path_(path),
306       next_avail_registration_id_(0),
307       next_avail_resource_id_(0),
308       next_avail_version_id_(0),
309       state_(UNINITIALIZED) {
310   sequence_checker_.DetachFromSequence();
311 }
312
313 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
314   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
315   db_.reset();
316 }
317
318 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds(
319     int64* next_avail_registration_id,
320     int64* next_avail_version_id,
321     int64* next_avail_resource_id) {
322   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
323   DCHECK(next_avail_registration_id);
324   DCHECK(next_avail_version_id);
325   DCHECK(next_avail_resource_id);
326
327   Status status = LazyOpen(false);
328   if (IsNewOrNonexistentDatabase(status)) {
329     *next_avail_registration_id = 0;
330     *next_avail_version_id = 0;
331     *next_avail_resource_id = 0;
332     return STATUS_OK;
333   }
334   if (status != STATUS_OK)
335     return status;
336
337   status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_);
338   if (status != STATUS_OK)
339     return status;
340   status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_);
341   if (status != STATUS_OK)
342     return status;
343   status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_);
344   if (status != STATUS_OK)
345     return status;
346
347   *next_avail_registration_id = next_avail_registration_id_;
348   *next_avail_version_id = next_avail_version_id_;
349   *next_avail_resource_id = next_avail_resource_id_;
350   return STATUS_OK;
351 }
352
353 ServiceWorkerDatabase::Status
354 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
355   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
356   DCHECK(origins->empty());
357
358   Status status = LazyOpen(false);
359   if (IsNewOrNonexistentDatabase(status))
360     return STATUS_OK;
361   if (status != STATUS_OK)
362     return status;
363
364   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
365   for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
366     status = LevelDBStatusToStatus(itr->status());
367     if (status != STATUS_OK) {
368       HandleReadResult(FROM_HERE, status);
369       origins->clear();
370       return status;
371     }
372
373     std::string origin;
374     if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
375       break;
376     origins->insert(GURL(origin));
377   }
378
379   HandleReadResult(FROM_HERE, status);
380   return status;
381 }
382
383 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
384     const GURL& origin,
385     std::vector<RegistrationData>* registrations) {
386   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
387   DCHECK(registrations->empty());
388
389   Status status = LazyOpen(false);
390   if (IsNewOrNonexistentDatabase(status))
391     return STATUS_OK;
392   if (status != STATUS_OK)
393     return status;
394
395   // Create a key prefix for registrations.
396   std::string prefix = base::StringPrintf(
397       "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
398
399   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
400   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
401     status = LevelDBStatusToStatus(itr->status());
402     if (status != STATUS_OK) {
403       HandleReadResult(FROM_HERE, status);
404       registrations->clear();
405       return status;
406     }
407
408     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
409       break;
410
411     RegistrationData registration;
412     status = ParseRegistrationData(itr->value().ToString(), &registration);
413     if (status != STATUS_OK) {
414       HandleReadResult(FROM_HERE, status);
415       registrations->clear();
416       return status;
417     }
418     registrations->push_back(registration);
419   }
420
421   HandleReadResult(FROM_HERE, status);
422   return status;
423 }
424
425 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
426     std::vector<RegistrationData>* registrations) {
427   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
428   DCHECK(registrations->empty());
429
430   Status status = LazyOpen(false);
431   if (IsNewOrNonexistentDatabase(status))
432     return STATUS_OK;
433   if (status != STATUS_OK)
434     return status;
435
436   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
437   for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
438     status = LevelDBStatusToStatus(itr->status());
439     if (status != STATUS_OK) {
440       HandleReadResult(FROM_HERE, status);
441       registrations->clear();
442       return status;
443     }
444
445     if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
446       break;
447
448     RegistrationData registration;
449     status = ParseRegistrationData(itr->value().ToString(), &registration);
450     if (status != STATUS_OK) {
451       HandleReadResult(FROM_HERE, status);
452       registrations->clear();
453       return status;
454     }
455     registrations->push_back(registration);
456   }
457
458   HandleReadResult(FROM_HERE, status);
459   return status;
460 }
461
462 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration(
463     int64 registration_id,
464     const GURL& origin,
465     RegistrationData* registration,
466     std::vector<ResourceRecord>* resources) {
467   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
468   DCHECK(registration);
469   DCHECK(resources);
470
471   Status status = LazyOpen(false);
472   if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
473     return status;
474
475   RegistrationData value;
476   status = ReadRegistrationData(registration_id, origin, &value);
477   if (status != STATUS_OK)
478     return status;
479
480   status = ReadResourceRecords(value.version_id, resources);
481   if (status != STATUS_OK)
482     return status;
483
484   *registration = value;
485   return STATUS_OK;
486 }
487
488 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration(
489     const RegistrationData& registration,
490     const std::vector<ResourceRecord>& resources,
491     std::vector<int64>* newly_purgeable_resources) {
492   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
493   Status status = LazyOpen(true);
494   if (status != STATUS_OK)
495     return status;
496
497   leveldb::WriteBatch batch;
498   BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
499   BumpNextVersionIdIfNeeded(registration.version_id, &batch);
500
501   PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
502   PutRegistrationDataToBatch(registration, &batch);
503
504   // Used for avoiding multiple writes for the same resource id or url.
505   std::set<int64> pushed_resources;
506   std::set<GURL> pushed_urls;
507   for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
508        itr != resources.end(); ++itr) {
509     if (!itr->url.is_valid())
510       return STATUS_ERROR_FAILED;
511
512     // Duplicated resource id or url should not exist.
513     DCHECK(pushed_resources.insert(itr->resource_id).second);
514     DCHECK(pushed_urls.insert(itr->url).second);
515
516     PutResourceRecordToBatch(*itr, registration.version_id, &batch);
517
518     // Delete a resource from the uncommitted list.
519     batch.Delete(CreateResourceIdKey(
520         kUncommittedResIdKeyPrefix, itr->resource_id));
521   }
522
523   // Retrieve a previous version to sweep purgeable resources.
524   RegistrationData old_registration;
525   status = ReadRegistrationData(registration.registration_id,
526                                 registration.scope.GetOrigin(),
527                                 &old_registration);
528   if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND)
529     return status;
530   if (status == STATUS_OK) {
531     DCHECK_LT(old_registration.version_id, registration.version_id);
532     status = DeleteResourceRecords(
533         old_registration.version_id, newly_purgeable_resources, &batch);
534     if (status != STATUS_OK)
535       return status;
536
537     // Currently resource sharing across versions and registrations is not
538     // supported, so resource ids should not be overlapped between
539     // |registration| and |old_registration|.
540     std::set<int64> deleted_resources(newly_purgeable_resources->begin(),
541                                       newly_purgeable_resources->end());
542     DCHECK(base::STLSetIntersection<std::set<int64> >(
543         pushed_resources, deleted_resources).empty());
544   }
545
546   return WriteBatch(&batch);
547 }
548
549 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive(
550     int64 registration_id,
551     const GURL& origin) {
552   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
553   ServiceWorkerDatabase::Status status = LazyOpen(false);
554   if (IsNewOrNonexistentDatabase(status))
555     return STATUS_ERROR_NOT_FOUND;
556   if (status != STATUS_OK)
557     return status;
558   if (!origin.is_valid())
559     return STATUS_ERROR_FAILED;
560
561   RegistrationData registration;
562   status = ReadRegistrationData(registration_id, origin, &registration);
563   if (status != STATUS_OK)
564     return status;
565
566   registration.is_active = true;
567
568   leveldb::WriteBatch batch;
569   PutRegistrationDataToBatch(registration, &batch);
570   return WriteBatch(&batch);
571 }
572
573 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime(
574     int64 registration_id,
575     const GURL& origin,
576     const base::Time& time) {
577   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
578   ServiceWorkerDatabase::Status status = LazyOpen(false);
579   if (IsNewOrNonexistentDatabase(status))
580     return STATUS_ERROR_NOT_FOUND;
581   if (status != STATUS_OK)
582     return status;
583   if (!origin.is_valid())
584     return STATUS_ERROR_FAILED;
585
586   RegistrationData registration;
587   status = ReadRegistrationData(registration_id, origin, &registration);
588   if (status != STATUS_OK)
589     return status;
590
591   registration.last_update_check = time;
592
593   leveldb::WriteBatch batch;
594   PutRegistrationDataToBatch(registration, &batch);
595   return WriteBatch(&batch);
596 }
597
598 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration(
599     int64 registration_id,
600     const GURL& origin,
601     std::vector<int64>* newly_purgeable_resources) {
602   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
603   Status status = LazyOpen(false);
604   if (IsNewOrNonexistentDatabase(status))
605     return STATUS_OK;
606   if (status != STATUS_OK)
607     return status;
608   if (!origin.is_valid())
609     return STATUS_ERROR_FAILED;
610
611   leveldb::WriteBatch batch;
612
613   // Remove |origin| from unique origins if a registration specified by
614   // |registration_id| is the only one for |origin|.
615   // TODO(nhiroki): Check the uniqueness by more efficient way.
616   std::vector<RegistrationData> registrations;
617   status = GetRegistrationsForOrigin(origin, &registrations);
618   if (status != STATUS_OK)
619     return status;
620
621   if (registrations.size() == 1 &&
622       registrations[0].registration_id == registration_id) {
623     batch.Delete(CreateUniqueOriginKey(origin));
624   }
625
626   // Delete a registration specified by |registration_id|.
627   batch.Delete(CreateRegistrationKey(registration_id, origin));
628
629   // Delete resource records associated with the registration.
630   for (std::vector<RegistrationData>::const_iterator itr =
631            registrations.begin(); itr != registrations.end(); ++itr) {
632     if (itr->registration_id == registration_id) {
633       status = DeleteResourceRecords(
634           itr->version_id, newly_purgeable_resources, &batch);
635       if (status != STATUS_OK)
636         return status;
637       break;
638     }
639   }
640
641   return WriteBatch(&batch);
642 }
643
644 ServiceWorkerDatabase::Status
645 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
646   return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
647 }
648
649 ServiceWorkerDatabase::Status
650 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
651   return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
652 }
653
654 ServiceWorkerDatabase::Status
655 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
656   return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
657 }
658
659 ServiceWorkerDatabase::Status
660 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
661   return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
662 }
663
664 ServiceWorkerDatabase::Status
665 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
666   return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
667 }
668
669 ServiceWorkerDatabase::Status
670 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
671   return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
672 }
673
674 ServiceWorkerDatabase::Status
675 ServiceWorkerDatabase::PurgeUncommittedResourceIds(
676     const std::set<int64>& ids) {
677   leveldb::WriteBatch batch;
678   Status status = DeleteResourceIdsInBatch(
679       kUncommittedResIdKeyPrefix, ids, &batch);
680   if (status != STATUS_OK)
681     return status;
682   status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch);
683   if (status != STATUS_OK)
684     return status;
685   return WriteBatch(&batch);
686 }
687
688 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigin(
689     const GURL& origin,
690     std::vector<int64>* newly_purgeable_resources) {
691   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
692   Status status = LazyOpen(false);
693   if (IsNewOrNonexistentDatabase(status))
694     return STATUS_OK;
695   if (status != STATUS_OK)
696     return status;
697   if (!origin.is_valid())
698     return STATUS_ERROR_FAILED;
699
700   leveldb::WriteBatch batch;
701
702   // Delete from the unique origin list.
703   batch.Delete(CreateUniqueOriginKey(origin));
704
705   std::vector<RegistrationData> registrations;
706   status = GetRegistrationsForOrigin(origin, &registrations);
707   if (status != STATUS_OK)
708     return status;
709
710   // Delete registrations and resource records.
711   for (std::vector<RegistrationData>::const_iterator itr =
712            registrations.begin(); itr != registrations.end(); ++itr) {
713     batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
714     status = DeleteResourceRecords(
715         itr->version_id, newly_purgeable_resources, &batch);
716     if (status != STATUS_OK)
717       return status;
718   }
719
720   return WriteBatch(&batch);
721 }
722
723 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
724   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
725   Disable(FROM_HERE, STATUS_OK);
726   return LevelDBStatusToStatus(
727       leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options()));
728 }
729
730 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
731     bool create_if_missing) {
732   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
733
734   // Do not try to open a database if we tried and failed once.
735   if (state_ == DISABLED)
736     return STATUS_ERROR_FAILED;
737   if (IsOpen())
738     return STATUS_OK;
739
740   // When |path_| is empty, open a database in-memory.
741   bool use_in_memory_db = path_.empty();
742
743   if (!create_if_missing) {
744     // Avoid opening a database if it does not exist at the |path_|.
745     if (use_in_memory_db ||
746         !base::PathExists(path_) ||
747         base::IsDirectoryEmpty(path_)) {
748       return STATUS_ERROR_NOT_FOUND;
749     }
750   }
751
752   leveldb::Options options;
753   options.create_if_missing = create_if_missing;
754   if (use_in_memory_db) {
755     env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
756     options.env = env_.get();
757   }
758
759   leveldb::DB* db = NULL;
760   Status status = LevelDBStatusToStatus(
761       leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
762   HandleOpenResult(FROM_HERE, status);
763   if (status != STATUS_OK) {
764     DCHECK(!db);
765     // TODO(nhiroki): Should we retry to open the database?
766     return status;
767   }
768   db_.reset(db);
769
770   int64 db_version;
771   status = ReadDatabaseVersion(&db_version);
772   if (status != STATUS_OK)
773     return status;
774   DCHECK_LE(0, db_version);
775   if (db_version > 0)
776     state_ = INITIALIZED;
777   return STATUS_OK;
778 }
779
780 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase(
781     ServiceWorkerDatabase::Status status) {
782   if (status == STATUS_ERROR_NOT_FOUND)
783     return true;
784   if (status == STATUS_OK && state_ == UNINITIALIZED)
785     return true;
786   return false;
787 }
788
789 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
790     const char* id_key,
791     int64* next_avail_id) {
792   DCHECK(id_key);
793   DCHECK(next_avail_id);
794
795   std::string value;
796   Status status = LevelDBStatusToStatus(
797       db_->Get(leveldb::ReadOptions(), id_key, &value));
798   if (status == STATUS_ERROR_NOT_FOUND) {
799     // Nobody has gotten the next resource id for |id_key|.
800     *next_avail_id = 0;
801     HandleReadResult(FROM_HERE, STATUS_OK);
802     return STATUS_OK;
803   } else if (status != STATUS_OK) {
804     HandleReadResult(FROM_HERE, status);
805     return status;
806   }
807
808   status = ParseId(value, next_avail_id);
809   HandleReadResult(FROM_HERE, status);
810   return status;
811 }
812
813 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
814     int64 registration_id,
815     const GURL& origin,
816     RegistrationData* registration) {
817   DCHECK(registration);
818
819   const std::string key = CreateRegistrationKey(registration_id, origin);
820   std::string value;
821   Status status = LevelDBStatusToStatus(
822       db_->Get(leveldb::ReadOptions(), key, &value));
823   if (status != STATUS_OK) {
824     HandleReadResult(
825         FROM_HERE,
826         status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status);
827     return status;
828   }
829
830   status = ParseRegistrationData(value, registration);
831   HandleReadResult(FROM_HERE, status);
832   return status;
833 }
834
835 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
836     int64 version_id,
837     std::vector<ResourceRecord>* resources) {
838   DCHECK(resources->empty());
839
840   Status status = STATUS_OK;
841   const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
842
843   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
844   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
845     Status status = LevelDBStatusToStatus(itr->status());
846     if (status != STATUS_OK) {
847       HandleReadResult(FROM_HERE, status);
848       resources->clear();
849       return status;
850     }
851
852     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
853       break;
854
855     ResourceRecord resource;
856     status = ParseResourceRecord(itr->value().ToString(), &resource);
857     if (status != STATUS_OK) {
858       HandleReadResult(FROM_HERE, status);
859       resources->clear();
860       return status;
861     }
862     resources->push_back(resource);
863   }
864
865   HandleReadResult(FROM_HERE, status);
866   return status;
867 }
868
869 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
870     int64 version_id,
871     std::vector<int64>* newly_purgeable_resources,
872     leveldb::WriteBatch* batch) {
873   DCHECK(batch);
874
875   Status status = STATUS_OK;
876   const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
877
878   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
879   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
880     status = LevelDBStatusToStatus(itr->status());
881     if (status != STATUS_OK) {
882       HandleReadResult(FROM_HERE, status);
883       return status;
884     }
885
886     const std::string key = itr->key().ToString();
887     std::string unprefixed;
888     if (!RemovePrefix(key, prefix, &unprefixed))
889       break;
890
891     int64 resource_id;
892     status = ParseId(unprefixed, &resource_id);
893     if (status != STATUS_OK) {
894       HandleReadResult(FROM_HERE, status);
895       return status;
896     }
897
898     // Remove a resource record.
899     batch->Delete(key);
900
901     // Currently resource sharing across versions and registrations is not
902     // supported, so we can purge this without caring about it.
903     PutPurgeableResourceIdToBatch(resource_id, batch);
904     newly_purgeable_resources->push_back(resource_id);
905   }
906
907   HandleReadResult(FROM_HERE, status);
908   return status;
909 }
910
911 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds(
912     const char* id_key_prefix,
913     std::set<int64>* ids) {
914   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
915   DCHECK(id_key_prefix);
916   DCHECK(ids->empty());
917
918   Status status = LazyOpen(false);
919   if (IsNewOrNonexistentDatabase(status))
920     return STATUS_OK;
921   if (status != STATUS_OK)
922     return status;
923
924   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
925   for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
926     status = LevelDBStatusToStatus(itr->status());
927     if (status != STATUS_OK) {
928       HandleReadResult(FROM_HERE, status);
929       ids->clear();
930       return status;
931     }
932
933     std::string unprefixed;
934     if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
935       break;
936
937     int64 resource_id;
938     status = ParseId(unprefixed, &resource_id);
939     if (status != STATUS_OK) {
940       HandleReadResult(FROM_HERE, status);
941       ids->clear();
942       return status;
943     }
944     ids->insert(resource_id);
945   }
946
947   HandleReadResult(FROM_HERE, status);
948   return status;
949 }
950
951 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds(
952     const char* id_key_prefix,
953     const std::set<int64>& ids) {
954   leveldb::WriteBatch batch;
955   Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch);
956   if (status != STATUS_OK)
957     return status;
958   return WriteBatch(&batch);
959 }
960
961 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch(
962     const char* id_key_prefix,
963     const std::set<int64>& ids,
964     leveldb::WriteBatch* batch) {
965   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
966   DCHECK(id_key_prefix);
967
968   Status status = LazyOpen(true);
969   if (status != STATUS_OK)
970     return status;
971
972   for (std::set<int64>::const_iterator itr = ids.begin();
973        itr != ids.end(); ++itr) {
974     // Value should be empty.
975     batch->Put(CreateResourceIdKey(id_key_prefix, *itr), "");
976   }
977   return STATUS_OK;
978 }
979
980 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds(
981     const char* id_key_prefix,
982     const std::set<int64>& ids) {
983   leveldb::WriteBatch batch;
984   Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch);
985   if (status != STATUS_OK)
986     return status;
987   return WriteBatch(&batch);
988 }
989
990 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch(
991     const char* id_key_prefix,
992     const std::set<int64>& ids,
993     leveldb::WriteBatch* batch) {
994   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
995   DCHECK(id_key_prefix);
996
997   Status status = LazyOpen(false);
998   if (IsNewOrNonexistentDatabase(status))
999     return STATUS_OK;
1000   if (status != STATUS_OK)
1001     return status;
1002
1003   for (std::set<int64>::const_iterator itr = ids.begin();
1004        itr != ids.end(); ++itr) {
1005     batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1006   }
1007   return STATUS_OK;
1008 }
1009
1010 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion(
1011     int64* db_version) {
1012   std::string value;
1013   Status status = LevelDBStatusToStatus(
1014       db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value));
1015   if (status == STATUS_ERROR_NOT_FOUND) {
1016     // The database hasn't been initialized yet.
1017     *db_version = 0;
1018     HandleReadResult(FROM_HERE, STATUS_OK);
1019     return STATUS_OK;
1020   }
1021
1022   if (status != STATUS_OK) {
1023     HandleReadResult(FROM_HERE, status);
1024     return status;
1025   }
1026
1027   status = ParseDatabaseVersion(value, db_version);
1028   HandleReadResult(FROM_HERE, status);
1029   return status;
1030 }
1031
1032 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1033     leveldb::WriteBatch* batch) {
1034   DCHECK(batch);
1035   DCHECK_NE(DISABLED, state_);
1036
1037   if (state_ == UNINITIALIZED) {
1038     // Write the database schema version.
1039     batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1040     state_ = INITIALIZED;
1041   }
1042
1043   Status status = LevelDBStatusToStatus(
1044       db_->Write(leveldb::WriteOptions(), batch));
1045   HandleWriteResult(FROM_HERE, status);
1046   return status;
1047 }
1048
1049 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
1050     int64 used_id, leveldb::WriteBatch* batch) {
1051   DCHECK(batch);
1052   if (next_avail_registration_id_ <= used_id) {
1053     next_avail_registration_id_ = used_id + 1;
1054     batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
1055   }
1056 }
1057
1058 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
1059     int64 used_id, leveldb::WriteBatch* batch) {
1060   DCHECK(batch);
1061   if (next_avail_version_id_ <= used_id) {
1062     next_avail_version_id_ = used_id + 1;
1063     batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
1064   }
1065 }
1066
1067 bool ServiceWorkerDatabase::IsOpen() {
1068   return db_ != NULL;
1069 }
1070
1071 void ServiceWorkerDatabase::Disable(
1072     const tracked_objects::Location& from_here,
1073     Status status) {
1074   if (status != STATUS_OK) {
1075     DLOG(ERROR) << "Failed at: " << from_here.ToString()
1076                 << " with error: " << StatusToString(status);
1077     DLOG(ERROR) << "ServiceWorkerDatabase is disabled.";
1078   }
1079   state_ = DISABLED;
1080   db_.reset();
1081 }
1082
1083 void ServiceWorkerDatabase::HandleOpenResult(
1084     const tracked_objects::Location& from_here,
1085     Status status) {
1086   if (status != ServiceWorkerDatabase::STATUS_OK)
1087     Disable(from_here, status);
1088   UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel,
1089                             status,
1090                             ServiceWorkerDatabase::STATUS_ERROR_MAX);
1091 }
1092
1093 void ServiceWorkerDatabase::HandleReadResult(
1094     const tracked_objects::Location& from_here,
1095     Status status) {
1096   if (status != ServiceWorkerDatabase::STATUS_OK)
1097     Disable(from_here, status);
1098   UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel,
1099                             status,
1100                             ServiceWorkerDatabase::STATUS_ERROR_MAX);
1101 }
1102
1103 void ServiceWorkerDatabase::HandleWriteResult(
1104     const tracked_objects::Location& from_here,
1105     Status status) {
1106   if (status != ServiceWorkerDatabase::STATUS_OK)
1107     Disable(from_here, status);
1108   UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel,
1109                             status,
1110                             ServiceWorkerDatabase::STATUS_ERROR_MAX);
1111 }
1112
1113 }  // namespace content