Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(660)

Unified Diff: content/browser/service_worker/service_worker_database.cc

Issue 248803003: ServiceWorker: Store registration data in ServiceWorkerDatabase (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase on https://codereview.chromium.org/257593003/ Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/service_worker/service_worker_database.cc
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 469223ba3e980c6f6c42b4324d261894bd1bb7c4..2ff9008fe887da758fa47d6c1a5f3fbfce0e8630 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -10,6 +10,10 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/service_worker/service_worker_database.pb.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
@@ -22,41 +26,95 @@
// - int64 value is serialized as a string by base::Int64ToString().
//
// Version 1 (in sorted order)
-// key: "DB_VERSION"
+// key: "INITDATA_DB_VERSION"
// value: "1"
//
-// key: "NEXT_REGISTRATION_ID"
+// key: "INITDATA_NEXT_REGISTRATION_ID"
// value: <int64 'next_available_registration_id'>
//
-// key: "NEXT_RESOURCE_ID"
+// key: "INITDATA_NEXT_RESOURCE_ID"
// value: <int64 'next_available_resource_id'>
//
-// key: "NEXT_VERSION_ID"
+// key: "INITDATA_NEXT_VERSION_ID"
// value: <int64 'next_available_version_id'>
+//
+// key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin' serialized by GURL::spec()>
+// value: <empty>
+//
+// key: "REG:" + (1) + '\x00' + (2)
+// (1) <GURL 'origin' serialized by GURL::spec()>
+// (2) <int64 'registration_id'>
+// (ex. "REG:http://example.com\x00123456")
+// value: <ServiceWorkerRegistrationData serialized as a string>
namespace content {
namespace {
-const char kDatabaseVersionKey[] = "DB_VERSION";
-const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID";
-const char kNextResIdKey[] = "NEXT_RESOURCE_ID";
-const char kNextVerIdKey[] = "NEXT_VERSION_ID";
+const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
+const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
+const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
+const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
+const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
+
+const char kRegKeyPrefix[] = "REG:";
+const char kKeySeparator = '\x00';
const int64 kCurrentSchemaVersion = 1;
-} // namespace
+bool RemovePrefix(const std::string& str,
+ const std::string& prefix,
+ std::string* out) {
+ DCHECK(out);
+ if (!StartsWithASCII(str, prefix, true))
+ return false;
+ *out = str.substr(prefix.size());
+ return true;
+}
+
+std::string CreateRegistrationKey(int64 registration_id,
+ const GURL& origin) {
+ return base::StringPrintf("%s%s%c%s",
+ kRegKeyPrefix,
+ origin.spec().c_str(),
+ kKeySeparator,
+ base::Int64ToString(registration_id).c_str());
+}
+
+std::string CreateUniqueOriginKey(const GURL& origin) {
+ return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
+}
+
+void PutRegistrationDataToBatch(const ServiceWorkerRegistrationData& data,
+ leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+ std::string value;
+ bool success = data.SerializeToString(&value);
+ DCHECK(success);
+ GURL origin = GURL(data.scope_url()).GetOrigin();
michaeln 2014/04/25 00:48:21 Maybe this class should entirely encapsulate the u
nhiroki 2014/04/25 02:06:11 I see. Will make them as required.
kinuko 2014/04/25 03:43:27 Fyi, it's possible to populate a field with a defa
nhiroki 2014/04/25 11:01:05 And there is base::Time issue.
michaeln 2014/04/26 00:29:02 I think the serialization format is an impl detail
+ batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
+}
-ServiceWorkerDatabase::RegistrationData::RegistrationData()
- : registration_id(-1),
- version_id(-1),
- is_active(false),
- has_fetch_handler(false) {
+void PutUniqueOriginToBatch(const GURL& origin,
+ leveldb::WriteBatch* batch) {
+ // Value should be empty.
+ batch->Put(CreateUniqueOriginKey(origin), "");
}
-ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
+bool ParseRegistrationData(const std::string& serialized,
+ ServiceWorkerRegistrationData* out) {
+ DCHECK(out);
+ ServiceWorkerRegistrationData data;
+ if (!data.ParseFromString(serialized))
+ return false;
+ if (GURL(data.scope_url()).GetOrigin() != GURL(data.script_url()).GetOrigin())
+ return false;
+ *out = data;
+ return true;
}
+} // namespace
+
ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
: path_(path),
is_disabled_(false),
@@ -95,6 +153,175 @@ bool ServiceWorkerDatabase::GetNextAvailableIds(
return true;
}
+bool ServiceWorkerDatabase::GetOriginsWithRegistrations(
+ std::set<GURL>* origins) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(origins);
+
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
+ if (!itr->status().ok()) {
+ HandleError(FROM_HERE, itr->status());
+ origins->clear();
+ return false;
+ }
+
+ std::string key = itr->key().ToString();
+ if (!StartsWithASCII(key, kUniqueOriginKey, true))
+ break;
+
+ std::string origin;
+ bool success = RemovePrefix(key, kUniqueOriginKey, &origin);
michaeln 2014/04/25 00:48:21 This function is funky? Either it doesn't need to
nhiroki 2014/04/25 11:01:05 Done.
+ DCHECK(success);
+ origins->insert(GURL(origin));
+ }
+ return true;
+}
+
+bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
+ const GURL& origin,
+ std::vector<ServiceWorkerRegistrationData>* registrations) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(registrations);
+
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ // Create a key prefix for registrations.
+ std::string prefix = base::StringPrintf(
+ "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
+
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
+ if (!itr->status().ok()) {
+ HandleError(FROM_HERE, itr->status());
+ registrations->clear();
+ return false;
+ }
+
+ std::string key = itr->key().ToString();
+ if (!StartsWithASCII(key, prefix, true))
+ break;
+
+ ServiceWorkerRegistrationData registration;
+ if (!ParseRegistrationData(itr->value().ToString(), &registration)) {
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
+ registrations->clear();
+ return false;
+ }
+ registrations->push_back(registration);
+ }
+ return true;
+}
+
+bool ServiceWorkerDatabase::ReadRegistration(
+ int64 registration_id,
+ const GURL& origin,
+ ServiceWorkerRegistrationData* registration,
+ std::vector<ServiceWorkerResourceRecord>* resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(registration);
+ DCHECK(resources);
+
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ ServiceWorkerRegistrationData value;
+ if (!ReadRegistrationData(registration_id, origin, &value))
+ return false;
+
+ // TODO(nhiroki): Read ResourceRecords tied with this registration.
+
+ *registration = value;
+ return true;
+}
+
+bool ServiceWorkerDatabase::WriteRegistration(
+ const ServiceWorkerRegistrationData& registration,
+ const std::vector<ServiceWorkerResourceRecord>& resources) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!LazyOpen(true) || is_disabled_)
+ return false;
+
+ leveldb::WriteBatch batch;
+ if (!BumpNextAvailableIdIfNeeded(
+ kNextRegIdKey, registration.registration_id(), &batch) ||
+ !BumpNextAvailableIdIfNeeded(
+ kNextVerIdKey, registration.version_id(), &batch)) {
+ return false;
+ }
+
+ PutUniqueOriginToBatch(GURL(registration.scope_url()).GetOrigin(), &batch);
michaeln 2014/04/25 00:48:21 is leveldb smart about noticing when a write doesn
nhiroki 2014/04/25 02:06:11 AFAICS, leveldb always writes log record even if t
nhiroki 2014/04/25 11:01:05 Let me address this in a following patch (added TO
+ PutRegistrationDataToBatch(registration, &batch);
+
+ // TODO(nhiroki): Write |resources| into the database.
+ return WriteBatch(&batch);
+}
+
+bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id,
+ const GURL& origin) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ ServiceWorkerRegistrationData registration;
+ if (!ReadRegistrationData(registration_id, origin, &registration))
+ return false;
+
+ registration.set_is_active(true);
+
+ leveldb::WriteBatch batch;
+ PutRegistrationDataToBatch(registration, &batch);
+ return WriteBatch(&batch);
+}
+
+bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id,
+ const GURL& origin,
+ const base::Time& time) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ ServiceWorkerRegistrationData registration;
+ if (!ReadRegistrationData(registration_id, origin, &registration))
+ return false;
+
+ DCHECK_LT(registration.last_update_check_time(), time.ToInternalValue());
+ registration.set_last_update_check_time(time.ToInternalValue());
+
+ leveldb::WriteBatch batch;
+ PutRegistrationDataToBatch(registration, &batch);
+ return WriteBatch(&batch);
+}
+
+bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id,
+ const GURL& origin) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!LazyOpen(false) || is_disabled_)
+ return false;
+
+ leveldb::WriteBatch batch;
+
+ // Remove |origin| from unique origins if a registration specified by
+ // |registration_id| is the only one for |origin|.
+ // TODO(nhiroki): Check the uniqueness by more efficient way.
+ std::vector<ServiceWorkerRegistrationData> registrations;
+ if (!GetRegistrationsForOrigin(origin, &registrations))
+ return false;
+ if (registrations.size() == 1 &&
+ registrations[0].registration_id() == registration_id) {
+ batch.Delete(CreateUniqueOriginKey(origin));
+ }
+
+ batch.Delete(CreateRegistrationKey(registration_id, origin));
+
+ // TODO(nhiroki): Delete ResourceRecords tied with this registration.
+ return WriteBatch(&batch);
+}
+
bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) {
DCHECK(sequence_checker_.CalledOnValidSequencedThread());
if (IsOpen())
@@ -173,6 +400,32 @@ bool ServiceWorkerDatabase::ReadNextAvailableId(
return true;
}
+bool ServiceWorkerDatabase::ReadRegistrationData(
+ int64 registration_id,
+ const GURL& origin,
+ ServiceWorkerRegistrationData* registration) {
+ DCHECK(registration);
+
+ std::string key = CreateRegistrationKey(registration_id, origin);
+
+ std::string value;
+ leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value);
+ if (!status.ok()) {
+ if (!status.IsNotFound())
+ HandleError(FROM_HERE, status);
+ return false;
+ }
+
+ ServiceWorkerRegistrationData parsed;
+ if (!ParseRegistrationData(value, &parsed)) {
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
+ return false;
+ }
+
+ *registration = parsed;
+ return true;
+}
+
bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
DCHECK(batch);
DCHECK(!is_disabled_);
@@ -184,6 +437,17 @@ bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
return true;
}
+bool ServiceWorkerDatabase::BumpNextAvailableIdIfNeeded(
michaeln 2014/04/25 00:48:21 As coded this function will have problems if an id
nhiroki 2014/04/25 11:01:05 Good idea! Addressed it.
+ const char* id_key, int64 used_id, leveldb::WriteBatch* batch) {
+ DCHECK(batch);
+ int64 next_available_id;
+ if (!ReadNextAvailableId(id_key, &next_available_id))
+ return false;
+ if (next_available_id <= used_id)
+ batch->Put(id_key, base::Int64ToString(used_id + 1));
+ return true;
+}
+
bool ServiceWorkerDatabase::IsOpen() {
return db_.get() != NULL;
}
« no previous file with comments | « content/browser/service_worker/service_worker_database.h ('k') | content/browser/service_worker/service_worker_database.proto » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698