Home | History | Annotate | Download | only in service_worker
      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
   1114