• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RemovePrefix(const std::string & str,const std::string & prefix,std::string * out)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 
CreateRegistrationKey(int64 registration_id,const GURL & origin)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 
CreateResourceRecordKeyPrefix(int64 version_id)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 
CreateResourceRecordKey(int64 version_id,int64 resource_id)115 std::string CreateResourceRecordKey(int64 version_id,
116                                     int64 resource_id) {
117   return CreateResourceRecordKeyPrefix(version_id).append(
118       base::Int64ToString(resource_id));
119 }
120 
CreateUniqueOriginKey(const GURL & origin)121 std::string CreateUniqueOriginKey(const GURL& origin) {
122   return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
123 }
124 
CreateResourceIdKey(const char * key_prefix,int64 resource_id)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 
PutRegistrationDataToBatch(const ServiceWorkerDatabase::RegistrationData & input,leveldb::WriteBatch * batch)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 
PutResourceRecordToBatch(const ServiceWorkerDatabase::ResourceRecord & input,int64 version_id,leveldb::WriteBatch * batch)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 
PutUniqueOriginToBatch(const GURL & origin,leveldb::WriteBatch * batch)169 void PutUniqueOriginToBatch(const GURL& origin,
170                             leveldb::WriteBatch* batch) {
171   // Value should be empty.
172   batch->Put(CreateUniqueOriginKey(origin), "");
173 }
174 
PutPurgeableResourceIdToBatch(int64 resource_id,leveldb::WriteBatch * batch)175 void PutPurgeableResourceIdToBatch(int64 resource_id,
176                                    leveldb::WriteBatch* batch) {
177   // Value should be empty.
178   batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
179 }
180 
ParseId(const std::string & serialized,int64 * out)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 
ParseDatabaseVersion(const std::string & serialized,int64 * out)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 
ParseRegistrationData(const std::string & serialized,ServiceWorkerDatabase::RegistrationData * out)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 
ParseResourceRecord(const std::string & serialized,ServiceWorkerDatabase::ResourceRecord * out)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 
LevelDBStatusToStatus(const leveldb::Status & status)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 
StatusToString(ServiceWorkerDatabase::Status status)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 
RegistrationData()294 ServiceWorkerDatabase::RegistrationData::RegistrationData()
295     : registration_id(kInvalidServiceWorkerRegistrationId),
296       version_id(kInvalidServiceWorkerVersionId),
297       is_active(false),
298       has_fetch_handler(false) {
299 }
300 
~RegistrationData()301 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
302 }
303 
ServiceWorkerDatabase(const base::FilePath & path)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 
~ServiceWorkerDatabase()313 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
314   DCHECK(sequence_checker_.CalledOnValidSequencedThread());
315   db_.reset();
316 }
317 
GetNextAvailableIds(int64 * next_avail_registration_id,int64 * next_avail_version_id,int64 * next_avail_resource_id)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
GetOriginsWithRegistrations(std::set<GURL> * origins)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 
GetRegistrationsForOrigin(const GURL & origin,std::vector<RegistrationData> * registrations)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 
GetAllRegistrations(std::vector<RegistrationData> * registrations)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 
ReadRegistration(int64 registration_id,const GURL & origin,RegistrationData * registration,std::vector<ResourceRecord> * resources)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 
WriteRegistration(const RegistrationData & registration,const std::vector<ResourceRecord> & resources,std::vector<int64> * newly_purgeable_resources)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 
UpdateVersionToActive(int64 registration_id,const GURL & origin)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 
UpdateLastCheckTime(int64 registration_id,const GURL & origin,const base::Time & time)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 
DeleteRegistration(int64 registration_id,const GURL & origin,std::vector<int64> * newly_purgeable_resources)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
GetUncommittedResourceIds(std::set<int64> * ids)645 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
646   return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
647 }
648 
649 ServiceWorkerDatabase::Status
WriteUncommittedResourceIds(const std::set<int64> & ids)650 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) {
651   return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
652 }
653 
654 ServiceWorkerDatabase::Status
ClearUncommittedResourceIds(const std::set<int64> & ids)655 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) {
656   return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
657 }
658 
659 ServiceWorkerDatabase::Status
GetPurgeableResourceIds(std::set<int64> * ids)660 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
661   return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
662 }
663 
664 ServiceWorkerDatabase::Status
WritePurgeableResourceIds(const std::set<int64> & ids)665 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) {
666   return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
667 }
668 
669 ServiceWorkerDatabase::Status
ClearPurgeableResourceIds(const std::set<int64> & ids)670 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) {
671   return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
672 }
673 
674 ServiceWorkerDatabase::Status
PurgeUncommittedResourceIds(const std::set<int64> & ids)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 
DeleteAllDataForOrigin(const GURL & origin,std::vector<int64> * newly_purgeable_resources)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 
DestroyDatabase()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 
LazyOpen(bool create_if_missing)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 
IsNewOrNonexistentDatabase(ServiceWorkerDatabase::Status status)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 
ReadNextAvailableId(const char * id_key,int64 * next_avail_id)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 
ReadRegistrationData(int64 registration_id,const GURL & origin,RegistrationData * registration)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 
ReadResourceRecords(int64 version_id,std::vector<ResourceRecord> * resources)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 
DeleteResourceRecords(int64 version_id,std::vector<int64> * newly_purgeable_resources,leveldb::WriteBatch * batch)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 
ReadResourceIds(const char * id_key_prefix,std::set<int64> * ids)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 
WriteResourceIds(const char * id_key_prefix,const std::set<int64> & ids)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 
WriteResourceIdsInBatch(const char * id_key_prefix,const std::set<int64> & ids,leveldb::WriteBatch * batch)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 
DeleteResourceIds(const char * id_key_prefix,const std::set<int64> & ids)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 
DeleteResourceIdsInBatch(const char * id_key_prefix,const std::set<int64> & ids,leveldb::WriteBatch * batch)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 
ReadDatabaseVersion(int64 * db_version)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 
WriteBatch(leveldb::WriteBatch * batch)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 
BumpNextRegistrationIdIfNeeded(int64 used_id,leveldb::WriteBatch * batch)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 
BumpNextVersionIdIfNeeded(int64 used_id,leveldb::WriteBatch * batch)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 
IsOpen()1067 bool ServiceWorkerDatabase::IsOpen() {
1068   return db_ != NULL;
1069 }
1070 
Disable(const tracked_objects::Location & from_here,Status status)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 
HandleOpenResult(const tracked_objects::Location & from_here,Status status)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 
HandleReadResult(const tracked_objects::Location & from_here,Status status)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 
HandleWriteResult(const tracked_objects::Location & from_here,Status status)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