Upstream version 7.36.149.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/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/browser/service_worker/service_worker_database.pb.h"
17 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
18 #include "third_party/leveldatabase/src/include/leveldb/db.h"
19 #include "third_party/leveldatabase/src/include/leveldb/env.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
21
22 // LevelDB database schema
23 // =======================
24 //
25 // NOTE
26 // - int64 value is serialized as a string by base::Int64ToString().
27 // - GURL value is serialized as a string by GURL::spec().
28 //
29 // Version 1 (in sorted order)
30 //   key: "INITDATA_DB_VERSION"
31 //   value: "1"
32 //
33 //   key: "INITDATA_NEXT_REGISTRATION_ID"
34 //   value: <int64 'next_available_registration_id'>
35 //
36 //   key: "INITDATA_NEXT_RESOURCE_ID"
37 //   value: <int64 'next_available_resource_id'>
38 //
39 //   key: "INITDATA_NEXT_VERSION_ID"
40 //   value: <int64 'next_available_version_id'>
41 //
42 //   key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
43 //   value: <empty>
44 //
45 //   key: "PRES:" + <int64 'purgeable_resource_id'>
46 //   value: <empty>
47 //
48 //   key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
49 //     (ex. "REG:http://example.com\x00123456")
50 //   value: <ServiceWorkerRegistrationData serialized as a string>
51 //
52 //   key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
53 //     (ex. "RES:123456\x00654321")
54 //   value: <ServiceWorkerResourceRecord serialized as a string>
55 //
56 //   key: "URES:" + <int64 'uncommitted_resource_id'>
57 //   value: <empty>
58
59 namespace content {
60
61 namespace {
62
63 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
64 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
65 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
66 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
67 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
68
69 const char kRegKeyPrefix[] = "REG:";
70 const char kResKeyPrefix[] = "RES:";
71 const char kKeySeparator = '\x00';
72
73 const char kUncommittedResIdKeyPrefix[] = "URES:";
74 const char kPurgeableResIdKeyPrefix[] = "PRES:";
75
76 const int64 kCurrentSchemaVersion = 1;
77
78 bool RemovePrefix(const std::string& str,
79                   const std::string& prefix,
80                   std::string* out) {
81   if (!StartsWithASCII(str, prefix, true))
82     return false;
83   if (out)
84     *out = str.substr(prefix.size());
85   return true;
86 }
87
88 std::string CreateRegistrationKey(int64 registration_id,
89                                   const GURL& origin) {
90   return base::StringPrintf("%s%s%c%s",
91                             kRegKeyPrefix,
92                             origin.spec().c_str(),
93                             kKeySeparator,
94                             base::Int64ToString(registration_id).c_str());
95 }
96
97 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
98   return base::StringPrintf("%s%s%c",
99                             kResKeyPrefix,
100                             base::Int64ToString(version_id).c_str(),
101                             kKeySeparator);
102 }
103
104 std::string CreateResourceRecordKey(int64 version_id,
105                                     int64 resource_id) {
106   return CreateResourceRecordKeyPrefix(version_id).append(
107       base::Int64ToString(resource_id));
108 }
109
110 std::string CreateUniqueOriginKey(const GURL& origin) {
111   return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
112 }
113
114 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
115   return base::StringPrintf(
116       "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
117 }
118
119 void PutRegistrationDataToBatch(
120     const ServiceWorkerDatabase::RegistrationData& input,
121     leveldb::WriteBatch* batch) {
122   DCHECK(batch);
123
124   // Convert RegistrationData to ServiceWorkerRegistrationData.
125   ServiceWorkerRegistrationData data;
126   data.set_registration_id(input.registration_id);
127   data.set_scope_url(input.scope.spec());
128   data.set_script_url(input.script.spec());
129   data.set_version_id(input.version_id);
130   data.set_is_active(input.is_active);
131   data.set_has_fetch_handler(input.has_fetch_handler);
132   data.set_last_update_check_time(input.last_update_check.ToInternalValue());
133
134   std::string value;
135   bool success = data.SerializeToString(&value);
136   DCHECK(success);
137   GURL origin = input.scope.GetOrigin();
138   batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
139 }
140
141 void PutResourceRecordToBatch(
142     const ServiceWorkerDatabase::ResourceRecord& input,
143     int64 version_id,
144     leveldb::WriteBatch* batch) {
145   DCHECK(batch);
146
147   // Convert ResourceRecord to ServiceWorkerResourceRecord.
148   ServiceWorkerResourceRecord record;
149   record.set_resource_id(input.resource_id);
150   record.set_url(input.url.spec());
151
152   std::string value;
153   bool success = record.SerializeToString(&value);
154   DCHECK(success);
155   batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
156 }
157
158 void PutUniqueOriginToBatch(const GURL& origin,
159                             leveldb::WriteBatch* batch) {
160   // Value should be empty.
161   batch->Put(CreateUniqueOriginKey(origin), "");
162 }
163
164 void PutPurgeableResourceIdToBatch(int64 resource_id,
165                                    leveldb::WriteBatch* batch) {
166   // Value should be empty.
167   batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
168 }
169
170 bool ParseRegistrationData(const std::string& serialized,
171                            ServiceWorkerDatabase::RegistrationData* out) {
172   DCHECK(out);
173   ServiceWorkerRegistrationData data;
174   if (!data.ParseFromString(serialized))
175     return false;
176
177   GURL scope_url(data.scope_url());
178   GURL script_url(data.script_url());
179   if (!scope_url.is_valid() ||
180       !script_url.is_valid() ||
181       scope_url.GetOrigin() != script_url.GetOrigin()) {
182     return false;
183   }
184
185   // Convert ServiceWorkerRegistrationData to RegistrationData.
186   out->registration_id = data.registration_id();
187   out->scope = scope_url;
188   out->script = script_url;
189   out->version_id = data.version_id();
190   out->is_active = data.is_active();
191   out->has_fetch_handler = data.has_fetch_handler();
192   out->last_update_check =
193       base::Time::FromInternalValue(data.last_update_check_time());
194   return true;
195 }
196
197 bool ParseResourceRecord(const std::string& serialized,
198                          ServiceWorkerDatabase::ResourceRecord* out) {
199   DCHECK(out);
200   ServiceWorkerResourceRecord record;
201   if (!record.ParseFromString(serialized))
202     return false;
203
204   GURL url(record.url());
205   if (!url.is_valid())
206     return false;
207
208   // Convert ServiceWorkerResourceRecord to ResourceRecord.
209   out->resource_id = record.resource_id();
210   out->url = url;
211   return true;
212 }
213
214 }  // namespace
215
216 ServiceWorkerDatabase::RegistrationData::RegistrationData()
217     : registration_id(-1),
218       version_id(-1),
219       is_active(false),
220       has_fetch_handler(false) {
221 }
222
223 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
224 }
225
226 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
227     : path_(path),
228       next_avail_registration_id_(0),
229       next_avail_resource_id_(0),
230       next_avail_version_id_(0),
231       is_disabled_(false),
232       was_corruption_detected_(false),
233       is_initialized_(false) {
234   sequence_checker_.DetachFromSequence();
235 }
236
237 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
238   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
239   db_.reset();
240 }
241
242 bool ServiceWorkerDatabase::GetNextAvailableIds(
243     int64* next_avail_registration_id,
244     int64* next_avail_version_id,
245     int64* next_avail_resource_id) {
246   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
247   DCHECK(next_avail_registration_id);
248   DCHECK(next_avail_version_id);
249   DCHECK(next_avail_resource_id);
250
251   if (!LazyOpen(false)) {
252     if (is_disabled_)
253       return false;
254     // Database has never been used.
255     *next_avail_registration_id = 0;
256     *next_avail_version_id = 0;
257     *next_avail_resource_id = 0;
258     return true;
259   }
260
261   if (!ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_) ||
262       !ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_) ||
263       !ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_)) {
264     return false;
265   }
266
267   *next_avail_registration_id = next_avail_registration_id_;
268   *next_avail_version_id = next_avail_version_id_;
269   *next_avail_resource_id = next_avail_resource_id_;
270   return true;
271 }
272
273 bool ServiceWorkerDatabase::GetOriginsWithRegistrations(
274     std::set<GURL>* origins) {
275   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
276   DCHECK(origins);
277
278   if (!LazyOpen(false)) {
279     if (is_disabled_)
280       return false;
281     // Database has never been used.
282     origins->clear();
283     return true;
284   }
285
286   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
287   for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
288     if (!itr->status().ok()) {
289       HandleError(FROM_HERE, itr->status());
290       origins->clear();
291       return false;
292     }
293
294     std::string origin;
295     if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
296       break;
297     origins->insert(GURL(origin));
298   }
299   return true;
300 }
301
302 bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
303     const GURL& origin,
304     std::vector<RegistrationData>* registrations) {
305   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
306   DCHECK(registrations);
307
308   if (!LazyOpen(false)) {
309     if (is_disabled_)
310       return false;
311     // Database has never been used.
312     registrations->clear();
313     return true;
314   }
315
316   // Create a key prefix for registrations.
317   std::string prefix = base::StringPrintf(
318       "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
319
320   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
321   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
322     if (!itr->status().ok()) {
323       HandleError(FROM_HERE, itr->status());
324       registrations->clear();
325       return false;
326     }
327
328     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
329       break;
330
331     RegistrationData registration;
332     if (!ParseRegistrationData(itr->value().ToString(), &registration)) {
333       HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
334       registrations->clear();
335       return false;
336     }
337     registrations->push_back(registration);
338   }
339   return true;
340 }
341
342 bool ServiceWorkerDatabase::GetAllRegistrations(
343     std::vector<RegistrationData>* registrations) {
344   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
345   DCHECK(registrations);
346
347   if (!LazyOpen(false)) {
348     if (is_disabled_)
349       return false;
350     // Database has never been used.
351     registrations->clear();
352     return true;
353   }
354
355   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
356   for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
357     if (!itr->status().ok()) {
358       HandleError(FROM_HERE, itr->status());
359       registrations->clear();
360       return false;
361     }
362
363     if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
364       break;
365
366     RegistrationData registration;
367     if (!ParseRegistrationData(itr->value().ToString(), &registration)) {
368       HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
369       registrations->clear();
370       return false;
371     }
372     registrations->push_back(registration);
373   }
374   return true;
375 }
376
377 bool ServiceWorkerDatabase::ReadRegistration(
378     int64 registration_id,
379     const GURL& origin,
380     RegistrationData* registration,
381     std::vector<ResourceRecord>* resources) {
382   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
383   DCHECK(registration);
384   DCHECK(resources);
385
386   if (!LazyOpen(false) || is_disabled_)
387     return false;
388
389   RegistrationData value;
390   if (!ReadRegistrationData(registration_id, origin, &value))
391     return false;
392
393   if (!ReadResourceRecords(value.version_id, resources))
394     return false;
395
396   *registration = value;
397   return true;
398 }
399
400 bool ServiceWorkerDatabase::WriteRegistration(
401     const RegistrationData& registration,
402     const std::vector<ResourceRecord>& resources) {
403   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
404   if (!LazyOpen(true) || is_disabled_)
405     return false;
406
407   leveldb::WriteBatch batch;
408   BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
409   BumpNextVersionIdIfNeeded(registration.version_id, &batch);
410
411   // TODO(nhiroki): Skip to add the origin into the unique origin list if it
412   // has already been added.
413   PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
414
415   PutRegistrationDataToBatch(registration, &batch);
416
417   // Retrieve a previous version to sweep purgeable resources.
418   RegistrationData old_registration;
419   if (!ReadRegistrationData(registration.registration_id,
420                             registration.scope.GetOrigin(),
421                             &old_registration)) {
422     if (is_disabled_)
423       return false;
424     // Just not found.
425   } else {
426     DCHECK_LT(old_registration.version_id, registration.version_id);
427     // Currently resource sharing across versions and registrations is not
428     // suppported, so resource ids should not be overlapped between
429     // |registration| and |old_registration|.
430     // TODO(nhiroki): Add DCHECK to make sure the overlap does not exist.
431     if (!DeleteResourceRecords(old_registration.version_id, &batch))
432       return false;
433   }
434
435   // Used for avoiding multiple writes for the same resource id or url.
436   std::set<int64> pushed_resources;
437   std::set<GURL> pushed_urls;
438   for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
439        itr != resources.end(); ++itr) {
440     if (!itr->url.is_valid())
441       return false;
442
443     // Duplicated resource id or url should not exist.
444     DCHECK(pushed_resources.insert(itr->resource_id).second);
445     DCHECK(pushed_urls.insert(itr->url).second);
446
447     PutResourceRecordToBatch(*itr, registration.version_id, &batch);
448
449     // Delete a resource from the uncommitted list.
450     batch.Delete(CreateResourceIdKey(
451         kUncommittedResIdKeyPrefix, itr->resource_id));
452   }
453
454   return WriteBatch(&batch);
455 }
456
457 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id,
458                                                   const GURL& origin) {
459   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
460   if (!LazyOpen(false) || is_disabled_)
461     return false;
462
463   RegistrationData registration;
464   if (!ReadRegistrationData(registration_id, origin, &registration))
465     return false;
466
467   registration.is_active = true;
468
469   leveldb::WriteBatch batch;
470   PutRegistrationDataToBatch(registration, &batch);
471   return WriteBatch(&batch);
472 }
473
474 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id,
475                                                 const GURL& origin,
476                                                 const base::Time& time) {
477   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
478   if (!LazyOpen(false) || is_disabled_)
479     return false;
480
481   RegistrationData registration;
482   if (!ReadRegistrationData(registration_id, origin, &registration))
483     return false;
484
485   registration.last_update_check = time;
486
487   leveldb::WriteBatch batch;
488   PutRegistrationDataToBatch(registration, &batch);
489   return WriteBatch(&batch);
490 }
491
492 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id,
493                                                const GURL& origin) {
494   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
495   if (!LazyOpen(false) || is_disabled_)
496     return false;
497
498   leveldb::WriteBatch batch;
499
500   // Remove |origin| from unique origins if a registration specified by
501   // |registration_id| is the only one for |origin|.
502   // TODO(nhiroki): Check the uniqueness by more efficient way.
503   std::vector<RegistrationData> registrations;
504   if (!GetRegistrationsForOrigin(origin, &registrations))
505     return false;
506   if (registrations.size() == 1 &&
507       registrations[0].registration_id == registration_id) {
508     batch.Delete(CreateUniqueOriginKey(origin));
509   }
510
511   // Delete a registration specified by |registration_id|.
512   batch.Delete(CreateRegistrationKey(registration_id, origin));
513
514   // Delete resource records associated with the registration.
515   for (std::vector<RegistrationData>::const_iterator itr =
516            registrations.begin(); itr != registrations.end(); ++itr) {
517     if (itr->registration_id == registration_id) {
518       if (!DeleteResourceRecords(itr->version_id, &batch))
519         return false;
520       break;
521     }
522   }
523
524   return WriteBatch(&batch);
525 }
526
527 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
528   return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
529 }
530
531 bool ServiceWorkerDatabase::WriteUncommittedResourceIds(
532     const std::set<int64>& ids) {
533   return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
534 }
535
536 bool ServiceWorkerDatabase::ClearUncommittedResourceIds(
537     const std::set<int64>& ids) {
538   return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
539 }
540
541 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
542   return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
543 }
544
545 bool ServiceWorkerDatabase::WritePurgeableResourceIds(
546     const std::set<int64>& ids) {
547   return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
548 }
549
550 bool ServiceWorkerDatabase::ClearPurgeableResourceIds(
551     const std::set<int64>& ids) {
552   return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
553 }
554
555 bool ServiceWorkerDatabase::DeleteAllDataForOrigin(const GURL& origin) {
556   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
557   if (!LazyOpen(true) || is_disabled_ || !origin.is_valid())
558     return false;
559
560   leveldb::WriteBatch batch;
561
562   // Delete from the unique origin list.
563   batch.Delete(CreateUniqueOriginKey(origin));
564
565   std::vector<RegistrationData> registrations;
566   if (!GetRegistrationsForOrigin(origin, &registrations))
567     return false;
568
569   // Delete registrations and resource records.
570   for (std::vector<RegistrationData>::const_iterator itr =
571            registrations.begin(); itr != registrations.end(); ++itr) {
572     batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
573     if (!DeleteResourceRecords(itr->version_id, &batch))
574       return false;
575   }
576
577   return WriteBatch(&batch);
578 }
579
580 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) {
581   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
582   if (IsOpen())
583     return true;
584
585   // Do not try to open a database if we tried and failed once.
586   if (is_disabled_)
587     return false;
588
589   // When |path_| is empty, open a database in-memory.
590   bool use_in_memory_db = path_.empty();
591
592   if (!create_if_needed) {
593     // Avoid opening a database if it does not exist at the |path_|.
594     if (use_in_memory_db ||
595         !base::PathExists(path_) ||
596         base::IsDirectoryEmpty(path_)) {
597       return false;
598     }
599   }
600
601   leveldb::Options options;
602   options.create_if_missing = create_if_needed;
603   if (use_in_memory_db) {
604     env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
605     options.env = env_.get();
606   }
607
608   leveldb::DB* db = NULL;
609   leveldb::Status status =
610       leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
611   if (!status.ok()) {
612     DCHECK(!db);
613     // TODO(nhiroki): Should we retry to open the database?
614     HandleError(FROM_HERE, status);
615     return false;
616   }
617   db_.reset(db);
618
619   int64 db_version;
620   if (!ReadDatabaseVersion(&db_version))
621     return false;
622   if (db_version > 0)
623     is_initialized_ = true;
624   return true;
625 }
626
627 bool ServiceWorkerDatabase::ReadNextAvailableId(
628     const char* id_key, int64* next_avail_id) {
629   DCHECK(id_key);
630   DCHECK(next_avail_id);
631
632   std::string value;
633   leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value);
634   if (status.IsNotFound()) {
635     // Nobody has gotten the next resource id for |id_key|.
636     *next_avail_id = 0;
637     return true;
638   }
639
640   if (!status.ok()) {
641     HandleError(FROM_HERE, status);
642     return false;
643   }
644
645   int64 parsed;
646   if (!base::StringToInt64(value, &parsed)) {
647     HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
648     return false;
649   }
650
651   *next_avail_id = parsed;
652   return true;
653 }
654
655 bool ServiceWorkerDatabase::ReadRegistrationData(
656     int64 registration_id,
657     const GURL& origin,
658     RegistrationData* registration) {
659   DCHECK(registration);
660
661   std::string key = CreateRegistrationKey(registration_id, origin);
662
663   std::string value;
664   leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value);
665   if (!status.ok()) {
666     if (!status.IsNotFound())
667       HandleError(FROM_HERE, status);
668     return false;
669   }
670
671   RegistrationData parsed;
672   if (!ParseRegistrationData(value, &parsed)) {
673     HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
674     return false;
675   }
676
677   *registration = parsed;
678   return true;
679 }
680
681 bool ServiceWorkerDatabase::ReadResourceRecords(
682     int64 version_id,
683     std::vector<ResourceRecord>* resources) {
684   DCHECK(resources);
685
686   std::string prefix = CreateResourceRecordKeyPrefix(version_id);
687   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
688   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
689     if (!itr->status().ok()) {
690       HandleError(FROM_HERE, itr->status());
691       resources->clear();
692       return false;
693     }
694
695     if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
696       break;
697
698     ResourceRecord resource;
699     if (!ParseResourceRecord(itr->value().ToString(), &resource)) {
700       HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
701       resources->clear();
702       return false;
703     }
704     resources->push_back(resource);
705   }
706   return true;
707 }
708
709 bool ServiceWorkerDatabase::DeleteResourceRecords(
710     int64 version_id,
711     leveldb::WriteBatch* batch) {
712   DCHECK(batch);
713
714   std::string prefix = CreateResourceRecordKeyPrefix(version_id);
715   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
716   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
717     if (!itr->status().ok()) {
718       HandleError(FROM_HERE, itr->status());
719       return false;
720     }
721
722     std::string key = itr->key().ToString();
723     std::string unprefixed;
724     if (!RemovePrefix(key, prefix, &unprefixed))
725       break;
726
727     int64 resource_id;
728     if (!base::StringToInt64(unprefixed, &resource_id)) {
729       HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
730       return false;
731     }
732
733     // Remove a resource record.
734     batch->Delete(key);
735
736     // Currently resource sharing across versions and registrations is not
737     // supported, so we can purge this without caring about it.
738     PutPurgeableResourceIdToBatch(resource_id, batch);
739   }
740   return true;
741 }
742
743 bool ServiceWorkerDatabase::ReadResourceIds(const char* id_key_prefix,
744                                             std::set<int64>* ids) {
745   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
746   DCHECK(id_key_prefix);
747   DCHECK(ids);
748
749   if (!LazyOpen(false) || is_disabled_)
750     return false;
751
752   scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
753   for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
754     if (!itr->status().ok()) {
755       HandleError(FROM_HERE, itr->status());
756       ids->clear();
757       return false;
758     }
759
760     std::string unprefixed;
761     if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
762       break;
763
764     int64 resource_id;
765     if (!base::StringToInt64(unprefixed, &resource_id)) {
766       HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
767       ids->clear();
768       return false;
769     }
770     ids->insert(resource_id);
771   }
772   return true;
773 }
774
775 bool ServiceWorkerDatabase::WriteResourceIds(const char* id_key_prefix,
776                                              const std::set<int64>& ids) {
777   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
778   DCHECK(id_key_prefix);
779
780   if (!LazyOpen(true) || is_disabled_)
781     return false;
782   if (ids.empty())
783     return true;
784
785   leveldb::WriteBatch batch;
786   for (std::set<int64>::const_iterator itr = ids.begin();
787        itr != ids.end(); ++itr) {
788     // Value should be empty.
789     batch.Put(CreateResourceIdKey(id_key_prefix, *itr), "");
790   }
791   return WriteBatch(&batch);
792 }
793
794 bool ServiceWorkerDatabase::DeleteResourceIds(const char* id_key_prefix,
795                                               const std::set<int64>& ids) {
796   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
797   DCHECK(id_key_prefix);
798
799   if (!LazyOpen(true) || is_disabled_)
800     return false;
801   if (ids.empty())
802     return true;
803
804   leveldb::WriteBatch batch;
805   for (std::set<int64>::const_iterator itr = ids.begin();
806        itr != ids.end(); ++itr) {
807     batch.Delete(CreateResourceIdKey(id_key_prefix, *itr));
808   }
809   return WriteBatch(&batch);
810 }
811
812 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) {
813   std::string value;
814   leveldb::Status status =
815       db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
816   if (status.IsNotFound()) {
817     // The database hasn't been initialized yet.
818     *db_version = 0;
819     return true;
820   }
821   if (!status.ok()) {
822     HandleError(FROM_HERE, status);
823     return false;
824   }
825
826   int64 parsed;
827   if (!base::StringToInt64(value, &parsed)) {
828     HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
829     return false;
830   }
831
832   const int kFirstValidVersion = 1;
833   if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) {
834     HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version"));
835     return false;
836   }
837
838   *db_version = parsed;
839   return true;
840 }
841
842 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
843   DCHECK(batch);
844   DCHECK(!is_disabled_);
845
846   if (!is_initialized_) {
847     // Write the database schema version.
848     batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
849     is_initialized_ = true;
850   }
851
852   leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
853   if (!status.ok()) {
854     HandleError(FROM_HERE, status);
855     return false;
856   }
857   return true;
858 }
859
860 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
861     int64 used_id, leveldb::WriteBatch* batch) {
862   DCHECK(batch);
863   if (next_avail_registration_id_ <= used_id) {
864     next_avail_registration_id_ = used_id + 1;
865     batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
866   }
867 }
868
869 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
870     int64 used_id, leveldb::WriteBatch* batch) {
871   DCHECK(batch);
872   if (next_avail_version_id_ <= used_id) {
873     next_avail_version_id_ = used_id + 1;
874     batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
875   }
876 }
877
878 bool ServiceWorkerDatabase::IsOpen() {
879   return db_.get() != NULL;
880 }
881
882 void ServiceWorkerDatabase::HandleError(
883     const tracked_objects::Location& from_here,
884     const leveldb::Status& status) {
885   // TODO(nhiroki): Add an UMA histogram.
886   DLOG(ERROR) << "Failed at: " << from_here.ToString()
887               << " with error: " << status.ToString();
888   is_disabled_ = true;
889   if (status.IsCorruption())
890     was_corruption_detected_ = true;
891   db_.reset();
892 }
893
894 }  // namespace content