Upstream version 10.39.225.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/files/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"
25
26 // LevelDB database schema
27 // =======================
28 //
29 // NOTE
30 // - int64 value is serialized as a string by base::Int64ToString().
31 // - GURL value is serialized as a string by GURL::spec().
32 //
33 // Version 1 (in sorted order)
34 //   key: "INITDATA_DB_VERSION"
35 //   value: "1"
36 //
37 //   key: "INITDATA_NEXT_REGISTRATION_ID"
38 //   value: <int64 'next_available_registration_id'>
39 //
40 //   key: "INITDATA_NEXT_RESOURCE_ID"
41 //   value: <int64 'next_available_resource_id'>
42 //
43 //   key: "INITDATA_NEXT_VERSION_ID"
44 //   value: <int64 'next_available_version_id'>
45 //
46 //   key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
47 //   value: <empty>
48 //
49 //   key: "PRES:" + <int64 'purgeable_resource_id'>
50 //   value: <empty>
51 //
52 //   key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
53 //     (ex. "REG:http://example.com\x00123456")
54 //   value: <ServiceWorkerRegistrationData serialized as a string>
55 //
56 //   key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
57 //     (ex. "RES:123456\x00654321")
58 //   value: <ServiceWorkerResourceRecord serialized as a string>
59 //
60 //   key: "URES:" + <int64 'uncommitted_resource_id'>
61 //   value: <empty>
62
63 namespace content {
64
65 namespace {
66
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:";
72
73 const char kRegKeyPrefix[] = "REG:";
74 const char kResKeyPrefix[] = "RES:";
75 const char kKeySeparator = '\x00';
76
77 const char kUncommittedResIdKeyPrefix[] = "URES:";
78 const char kPurgeableResIdKeyPrefix[] = "PRES:";
79
80 const int64 kCurrentSchemaVersion = 1;
81
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;
90 }
91
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());
99 }
100
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);
106 }
107
108 std::string CreateResourceRecordKey(int64 version_id,
109                                     int64 resource_id) {
110   return CreateResourceRecordKeyPrefix(version_id).append(
111       base::Int64ToString(resource_id));
112 }
113
114 std::string CreateUniqueOriginKey(const GURL& origin) {
115   return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
116 }
117
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());
121 }
122
123 void PutRegistrationDataToBatch(
124     const ServiceWorkerDatabase::RegistrationData& input,
125     leveldb::WriteBatch* batch) {
126   DCHECK(batch);
127
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());
137
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);
143 }
144
145 void PutResourceRecordToBatch(
146     const ServiceWorkerDatabase::ResourceRecord& input,
147     int64 version_id,
148     leveldb::WriteBatch* batch) {
149   DCHECK(batch);
150
151   // Convert ResourceRecord to ServiceWorkerResourceRecord.
152   ServiceWorkerResourceRecord record;
153   record.set_resource_id(input.resource_id);
154   record.set_url(input.url.spec());
155
156   std::string value;
157   bool success = record.SerializeToString(&value);
158   DCHECK(success);
159   batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
160 }
161
162 void PutUniqueOriginToBatch(const GURL& origin,
163                             leveldb::WriteBatch* batch) {
164   // Value should be empty.
165   batch->Put(CreateUniqueOriginKey(origin), "");
166 }
167
168 void PutPurgeableResourceIdToBatch(int64 resource_id,
169                                    leveldb::WriteBatch* batch) {
170   // Value should be empty.
171   batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
172 }
173
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;
183 }
184
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;
194   }
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;
200   }
201   *out = version;
202   return ServiceWorkerDatabase::STATUS_OK;
203 }
204
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;
212
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;
219   }
220
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;
231 }
232
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;
240
241   GURL url(record.url());
242   if (!url.is_valid())
243     return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED;
244
245   // Convert ServiceWorkerResourceRecord to ResourceRecord.
246   out->resource_id = record.resource_id();
247   out->url = url;
248   return ServiceWorkerDatabase::STATUS_OK;
249 }
250
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;
263 }
264
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";
280   }
281   NOTREACHED();
282   return "Database unknown error";
283 }
284
285 }  // namespace
286
287 ServiceWorkerDatabase::RegistrationData::RegistrationData()
288     : registration_id(kInvalidServiceWorkerRegistrationId),
289       version_id(kInvalidServiceWorkerVersionId),
290       is_active(false),
291       has_fetch_handler(false) {
292 }
293
294 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
295 }
296
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();
304 }
305
306 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
307   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
308   db_.reset();
309 }
310
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);
319
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;
326   }
327   if (status != STATUS_OK)
328     return status;
329
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;
339
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;
344 }
345
346 ServiceWorkerDatabase::Status
347 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) {
348   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
349   DCHECK(origins->empty());
350
351   Status status = LazyOpen(false);
352   if (IsNewOrNonexistentDatabase(status))
353     return STATUS_OK;
354   if (status != STATUS_OK)
355     return status;
356
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;
364     }
365
366     std::string origin;
367     if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
368       break;
369     origins->insert(GURL(origin));
370   }
371
372   HandleReadResult(FROM_HERE, status);
373   return status;
374 }
375
376 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin(
377     const GURL& origin,
378     std::vector<RegistrationData>* registrations) {
379   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
380   DCHECK(registrations->empty());
381
382   Status status = LazyOpen(false);
383   if (IsNewOrNonexistentDatabase(status))
384     return STATUS_OK;
385   if (status != STATUS_OK)
386     return status;
387
388   // Create a key prefix for registrations.
389   std::string prefix = base::StringPrintf(
390       "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
391
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;
399     }
400
401     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
402       break;
403
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;
410     }
411     registrations->push_back(registration);
412   }
413
414   HandleReadResult(FROM_HERE, status);
415   return status;
416 }
417
418 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations(
419     std::vector<RegistrationData>* registrations) {
420   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
421   DCHECK(registrations->empty());
422
423   Status status = LazyOpen(false);
424   if (IsNewOrNonexistentDatabase(status))
425     return STATUS_OK;
426   if (status != STATUS_OK)
427     return status;
428
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;
436     }
437
438     if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
439       break;
440
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;
447     }
448     registrations->push_back(registration);
449   }
450
451   HandleReadResult(FROM_HERE, status);
452   return status;
453 }
454
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);
463
464   Status status = LazyOpen(false);
465   if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK)
466     return status;
467
468   RegistrationData value;
469   status = ReadRegistrationData(registration_id, origin, &value);
470   if (status != STATUS_OK)
471     return status;
472
473   status = ReadResourceRecords(value.version_id, resources);
474   if (status != STATUS_OK)
475     return status;
476
477   *registration = value;
478   return STATUS_OK;
479 }
480
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;
491
492   leveldb::WriteBatch batch;
493   BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
494   BumpNextVersionIdIfNeeded(registration.version_id, &batch);
495
496   PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
497   PutRegistrationDataToBatch(registration, &batch);
498
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;
506
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);
510
511     PutResourceRecordToBatch(*itr, registration.version_id, &batch);
512
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));
519   }
520
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;
535
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());
543   }
544
545   return WriteBatch(&batch);
546 }
547
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;
559
560   RegistrationData registration;
561   status = ReadRegistrationData(registration_id, origin, &registration);
562   if (status != STATUS_OK)
563     return status;
564
565   registration.is_active = true;
566
567   leveldb::WriteBatch batch;
568   PutRegistrationDataToBatch(registration, &batch);
569   return WriteBatch(&batch);
570 }
571
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;
584
585   RegistrationData registration;
586   status = ReadRegistrationData(registration_id, origin, &registration);
587   if (status != STATUS_OK)
588     return status;
589
590   registration.last_update_check = time;
591
592   leveldb::WriteBatch batch;
593   PutRegistrationDataToBatch(registration, &batch);
594   return WriteBatch(&batch);
595 }
596
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;
611
612   leveldb::WriteBatch batch;
613
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;
621
622   if (registrations.size() == 1 &&
623       registrations[0].registration_id == registration_id) {
624     batch.Delete(CreateUniqueOriginKey(origin));
625   }
626
627   // Delete a registration specified by |registration_id|.
628   batch.Delete(CreateRegistrationKey(registration_id, origin));
629
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;
640     }
641   }
642
643   return WriteBatch(&batch);
644 }
645
646 ServiceWorkerDatabase::Status
647 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
648   return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
649 }
650
651 ServiceWorkerDatabase::Status
652 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
653   return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
654 }
655
656 ServiceWorkerDatabase::Status
657 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
658   return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
659 }
660
661 ServiceWorkerDatabase::Status
662 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
663   return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
664 }
665
666 ServiceWorkerDatabase::Status
667 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
668   return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
669 }
670
671 ServiceWorkerDatabase::Status
672 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
673   return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
674 }
675
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);
688 }
689
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;
701
702   leveldb::WriteBatch batch;
703
704   // Delete from the unique origin list.
705   batch.Delete(CreateUniqueOriginKey(origin));
706
707   std::vector<RegistrationData> registrations;
708   status = GetRegistrationsForOrigin(origin, &registrations);
709   if (status != STATUS_OK)
710     return status;
711
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;
720   }
721
722   return WriteBatch(&batch);
723 }
724
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()));
730 }
731
732 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
733     bool create_if_missing) {
734   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
735
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;
741
742   // When |path_| is empty, open a database in-memory.
743   bool use_in_memory_db = path_.empty();
744
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;
751     }
752   }
753
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();
759   }
760
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;
769   }
770   db_.reset(db);
771
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;
780 }
781
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;
789 }
790
791 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId(
792     const char* id_key,
793     int64* next_avail_id) {
794   DCHECK(id_key);
795   DCHECK(next_avail_id);
796
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;
808   }
809
810   status = ParseId(value, next_avail_id);
811   HandleReadResult(FROM_HERE, status);
812   return status;
813 }
814
815 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData(
816     int64 registration_id,
817     const GURL& origin,
818     RegistrationData* registration) {
819   DCHECK(registration);
820
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;
830   }
831
832   status = ParseRegistrationData(value, registration);
833   HandleReadResult(FROM_HERE, status);
834   return status;
835 }
836
837 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords(
838     int64 version_id,
839     std::vector<ResourceRecord>* resources) {
840   DCHECK(resources->empty());
841
842   Status status = STATUS_OK;
843   const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
844
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;
852     }
853
854     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
855       break;
856
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;
863     }
864     resources->push_back(resource);
865   }
866
867   HandleReadResult(FROM_HERE, status);
868   return status;
869 }
870
871 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords(
872     int64 version_id,
873     std::vector<int64>* newly_purgeable_resources,
874     leveldb::WriteBatch* batch) {
875   DCHECK(batch);
876
877   Status status = STATUS_OK;
878   const std::string prefix = CreateResourceRecordKeyPrefix(version_id);
879
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;
886     }
887
888     const std::string key = itr->key().ToString();
889     std::string unprefixed;
890     if (!RemovePrefix(key, prefix, &unprefixed))
891       break;
892
893     int64 resource_id;
894     status = ParseId(unprefixed, &resource_id);
895     if (status != STATUS_OK) {
896       HandleReadResult(FROM_HERE, status);
897       return status;
898     }
899
900     // Remove a resource record.
901     batch->Delete(key);
902
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);
907   }
908
909   HandleReadResult(FROM_HERE, status);
910   return status;
911 }
912
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());
919
920   Status status = LazyOpen(false);
921   if (IsNewOrNonexistentDatabase(status))
922     return STATUS_OK;
923   if (status != STATUS_OK)
924     return status;
925
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;
933     }
934
935     std::string unprefixed;
936     if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
937       break;
938
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;
945     }
946     ids->insert(resource_id);
947   }
948
949   HandleReadResult(FROM_HERE, status);
950   return status;
951 }
952
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);
961 }
962
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);
969
970   Status status = LazyOpen(true);
971   if (status != STATUS_OK)
972     return status;
973
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), "");
980   }
981   // std::set is sorted, so the last element is the largest.
982   BumpNextResourceIdIfNeeded(*ids.rbegin(), batch);
983   return STATUS_OK;
984 }
985
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);
994 }
995
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);
1002
1003   Status status = LazyOpen(false);
1004   if (IsNewOrNonexistentDatabase(status))
1005     return STATUS_OK;
1006   if (status != STATUS_OK)
1007     return status;
1008
1009   for (std::set<int64>::const_iterator itr = ids.begin();
1010        itr != ids.end(); ++itr) {
1011     batch->Delete(CreateResourceIdKey(id_key_prefix, *itr));
1012   }
1013   return STATUS_OK;
1014 }
1015
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;
1026   }
1027
1028   if (status != STATUS_OK) {
1029     HandleReadResult(FROM_HERE, status);
1030     return status;
1031   }
1032
1033   status = ParseDatabaseVersion(value, db_version);
1034   HandleReadResult(FROM_HERE, status);
1035   return status;
1036 }
1037
1038 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch(
1039     leveldb::WriteBatch* batch) {
1040   DCHECK(batch);
1041   DCHECK_NE(DISABLED, state_);
1042
1043   if (state_ == UNINITIALIZED) {
1044     // Write the database schema version.
1045     batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
1046     state_ = INITIALIZED;
1047   }
1048
1049   Status status = LevelDBStatusToStatus(
1050       db_->Write(leveldb::WriteOptions(), batch));
1051   HandleWriteResult(FROM_HERE, status);
1052   return status;
1053 }
1054
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_));
1061   }
1062 }
1063
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_));
1070   }
1071 }
1072
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_));
1079   }
1080 }
1081
1082 bool ServiceWorkerDatabase::IsOpen() {
1083   return db_ != NULL;
1084 }
1085
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.";
1093   }
1094   state_ = DISABLED;
1095   db_.reset();
1096 }
1097
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);
1104 }
1105
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);
1112 }
1113
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);
1120 }
1121
1122 }  // namespace content