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/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
   1123