Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/service_worker/service_worker_database.h" | 5 #include "content/browser/service_worker/service_worker_database.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/strings/string_split.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "content/browser/service_worker/service_worker_database.pb.h" | |
| 13 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" | 17 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
| 14 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 18 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 15 #include "third_party/leveldatabase/src/include/leveldb/env.h" | 19 #include "third_party/leveldatabase/src/include/leveldb/env.h" |
| 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| 17 | 21 |
| 18 // LevelDB database schema | 22 // LevelDB database schema |
| 19 // ======================= | 23 // ======================= |
| 20 // | 24 // |
| 21 // NOTE | 25 // NOTE |
| 22 // - int64 value is serialized as a string by base::Int64ToString(). | 26 // - int64 value is serialized as a string by base::Int64ToString(). |
| 23 // | 27 // |
| 24 // Version 1 (in sorted order) | 28 // Version 1 (in sorted order) |
| 25 // key: "DB_VERSION" | 29 // key: "INITDATA_DB_VERSION" |
| 26 // value: "1" | 30 // value: "1" |
| 27 // | 31 // |
| 28 // key: "NEXT_REGISTRATION_ID" | 32 // key: "INITDATA_NEXT_REGISTRATION_ID" |
| 29 // value: <int64 'next_available_registration_id'> | 33 // value: <int64 'next_available_registration_id'> |
| 30 // | 34 // |
| 31 // key: "NEXT_RESOURCE_ID" | 35 // key: "INITDATA_NEXT_RESOURCE_ID" |
| 32 // value: <int64 'next_available_resource_id'> | 36 // value: <int64 'next_available_resource_id'> |
| 33 // | 37 // |
| 34 // key: "NEXT_VERSION_ID" | 38 // key: "INITDATA_NEXT_VERSION_ID" |
| 35 // value: <int64 'next_available_version_id'> | 39 // value: <int64 'next_available_version_id'> |
| 40 // | |
| 41 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin' serialized by GURL::spec()> | |
| 42 // value: <empty> | |
| 43 // | |
| 44 // key: "REG:" + (1) + '\x00' + (2) | |
| 45 // (1) <GURL 'origin' serialized by GURL::spec()> | |
| 46 // (2) <int64 'registration_id'> | |
| 47 // (ex. "REG:http://example.com\x00123456") | |
| 48 // value: <ServiceWorkerRegistrationData serialized as a string> | |
| 36 | 49 |
| 37 namespace content { | 50 namespace content { |
| 38 | 51 |
| 39 namespace { | 52 namespace { |
| 40 | 53 |
| 41 const char kDatabaseVersionKey[] = "DB_VERSION"; | 54 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION"; |
| 42 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; | 55 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID"; |
| 43 const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; | 56 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID"; |
| 44 const char kNextVerIdKey[] = "NEXT_VERSION_ID"; | 57 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID"; |
| 58 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:"; | |
| 59 | |
| 60 const char kRegKeyPrefix[] = "REG:"; | |
| 61 const char kKeySeparator = '\x00'; | |
| 45 | 62 |
| 46 const int64 kCurrentSchemaVersion = 1; | 63 const int64 kCurrentSchemaVersion = 1; |
| 47 | 64 |
| 65 bool RemovePrefix(const std::string& str, | |
| 66 const std::string& prefix, | |
| 67 std::string* out) { | |
| 68 if (!StartsWithASCII(str, prefix, true)) | |
| 69 return false; | |
| 70 if (out) | |
| 71 *out = str.substr(prefix.size()); | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 std::string CreateRegistrationKey(int64 registration_id, | |
| 76 const GURL& origin) { | |
| 77 return base::StringPrintf("%s%s%c%s", | |
| 78 kRegKeyPrefix, | |
| 79 origin.spec().c_str(), | |
| 80 kKeySeparator, | |
| 81 base::Int64ToString(registration_id).c_str()); | |
| 82 } | |
| 83 | |
| 84 std::string CreateUniqueOriginKey(const GURL& origin) { | |
| 85 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str()); | |
| 86 } | |
| 87 | |
| 88 void PutRegistrationDataToBatch( | |
| 89 const ServiceWorkerDatabase::RegistrationData& input, | |
| 90 leveldb::WriteBatch* batch) { | |
| 91 DCHECK(batch); | |
| 92 | |
| 93 // Convert RegistrationData to ServiceWorkerRegistrationData. | |
| 94 ServiceWorkerRegistrationData data; | |
| 95 data.set_registration_id(input.registration_id); | |
| 96 data.set_scope_url(input.scope.spec()); | |
| 97 data.set_script_url(input.script.spec()); | |
| 98 data.set_version_id(input.version_id); | |
| 99 data.set_is_active(input.is_active); | |
| 100 data.set_has_fetch_handler(input.has_fetch_handler); | |
| 101 data.set_last_update_check_time(input.last_update_check.ToInternalValue()); | |
| 102 | |
| 103 std::string value; | |
| 104 bool success = data.SerializeToString(&value); | |
| 105 DCHECK(success); | |
| 106 GURL origin = GURL(data.scope_url()).GetOrigin(); | |
|
michaeln
2014/04/26 00:29:02
could use input.scope.GetOrigin()
nhiroki
2014/04/28 04:15:59
Done.
| |
| 107 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value); | |
| 108 } | |
| 109 | |
| 110 void PutUniqueOriginToBatch(const GURL& origin, | |
| 111 leveldb::WriteBatch* batch) { | |
| 112 // Value should be empty. | |
| 113 batch->Put(CreateUniqueOriginKey(origin), ""); | |
| 114 } | |
| 115 | |
| 116 bool ParseRegistrationData( | |
| 117 const std::string& serialized, | |
| 118 ServiceWorkerDatabase::RegistrationData* out) { | |
| 119 DCHECK(out); | |
| 120 ServiceWorkerRegistrationData data; | |
| 121 if (!data.ParseFromString(serialized)) | |
| 122 return false; | |
| 123 | |
| 124 GURL scope_url(data.scope_url()); | |
| 125 GURL script_url(data.script_url()); | |
| 126 if (scope_url.GetOrigin() != script_url.GetOrigin()) | |
| 127 return false; | |
| 128 | |
| 129 // Convert ServiceWorkerRegistrationData to RegistrationData. | |
| 130 out->registration_id = data.registration_id(); | |
| 131 out->scope = scope_url; | |
| 132 out->script = script_url; | |
| 133 out->version_id = data.version_id(); | |
| 134 out->is_active = data.is_active(); | |
| 135 out->has_fetch_handler = data.has_fetch_handler(); | |
| 136 out->last_update_check = | |
| 137 base::Time::FromInternalValue(data.last_update_check_time()); | |
| 138 return true; | |
| 139 } | |
| 140 | |
| 48 } // namespace | 141 } // namespace |
| 49 | 142 |
| 50 ServiceWorkerDatabase::RegistrationData::RegistrationData() | 143 ServiceWorkerDatabase::RegistrationData::RegistrationData() |
| 51 : registration_id(-1), | 144 : registration_id(-1), |
| 52 version_id(-1), | 145 version_id(-1), |
| 53 is_active(false), | 146 is_active(false), |
| 54 has_fetch_handler(false) { | 147 has_fetch_handler(false) { |
| 55 } | 148 } |
| 56 | 149 |
| 57 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { | 150 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { |
| 58 } | 151 } |
| 59 | 152 |
| 60 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) | 153 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) |
| 61 : path_(path), | 154 : path_(path), |
| 155 next_avail_registration_id_(0), | |
| 156 next_avail_resource_id_(0), | |
| 157 next_avail_version_id_(0), | |
| 62 is_disabled_(false), | 158 is_disabled_(false), |
| 63 was_corruption_detected_(false) { | 159 was_corruption_detected_(false) { |
| 64 } | 160 } |
| 65 | 161 |
| 66 ServiceWorkerDatabase::~ServiceWorkerDatabase() { | 162 ServiceWorkerDatabase::~ServiceWorkerDatabase() { |
| 67 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 163 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| 68 db_.reset(); | 164 db_.reset(); |
| 69 } | 165 } |
| 70 | 166 |
| 71 bool ServiceWorkerDatabase::GetNextAvailableIds( | 167 bool ServiceWorkerDatabase::GetNextAvailableIds( |
| 72 int64* next_avail_registration_id, | 168 int64* next_avail_registration_id, |
| 73 int64* next_avail_version_id, | 169 int64* next_avail_version_id, |
| 74 int64* next_avail_resource_id) { | 170 int64* next_avail_resource_id) { |
| 75 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 171 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| 76 DCHECK(next_avail_registration_id); | 172 DCHECK(next_avail_registration_id); |
| 77 DCHECK(next_avail_version_id); | 173 DCHECK(next_avail_version_id); |
| 78 DCHECK(next_avail_resource_id); | 174 DCHECK(next_avail_resource_id); |
| 79 | 175 |
| 80 if (!LazyOpen(false) || is_disabled_) | 176 if (!LazyOpen(false) || is_disabled_) |
| 81 return false; | 177 return false; |
| 82 | 178 |
| 83 int64 reg_id = -1; | 179 if (!ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_) || |
| 84 int64 ver_id = -1; | 180 !ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_) || |
| 85 int64 res_id = -1; | 181 !ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_)) { |
| 182 return false; | |
| 183 } | |
| 86 | 184 |
| 87 if (!ReadNextAvailableId(kNextRegIdKey, ®_id) || | 185 *next_avail_registration_id = next_avail_registration_id_; |
| 88 !ReadNextAvailableId(kNextVerIdKey, &ver_id) || | 186 *next_avail_version_id = next_avail_version_id_; |
| 89 !ReadNextAvailableId(kNextResIdKey, &res_id)) | 187 *next_avail_resource_id = next_avail_resource_id_; |
| 188 return true; | |
| 189 } | |
| 190 | |
| 191 bool ServiceWorkerDatabase::GetOriginsWithRegistrations( | |
| 192 std::set<GURL>* origins) { | |
| 193 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 194 DCHECK(origins); | |
| 195 | |
| 196 if (!LazyOpen(false) || is_disabled_) | |
| 90 return false; | 197 return false; |
| 91 | 198 |
| 92 *next_avail_registration_id = reg_id; | 199 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
| 93 *next_avail_version_id = ver_id; | 200 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { |
| 94 *next_avail_resource_id = res_id; | 201 if (!itr->status().ok()) { |
| 202 HandleError(FROM_HERE, itr->status()); | |
| 203 origins->clear(); | |
| 204 return false; | |
| 205 } | |
| 206 | |
| 207 std::string origin; | |
| 208 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) | |
| 209 break; | |
| 210 origins->insert(GURL(origin)); | |
| 211 } | |
| 95 return true; | 212 return true; |
| 96 } | 213 } |
| 97 | 214 |
| 215 bool ServiceWorkerDatabase::GetRegistrationsForOrigin( | |
| 216 const GURL& origin, | |
| 217 std::vector<RegistrationData>* registrations) { | |
| 218 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 219 DCHECK(registrations); | |
| 220 | |
| 221 if (!LazyOpen(false) || is_disabled_) | |
| 222 return false; | |
| 223 | |
| 224 // Create a key prefix for registrations. | |
| 225 std::string prefix = base::StringPrintf( | |
| 226 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); | |
| 227 | |
| 228 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
| 229 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | |
| 230 if (!itr->status().ok()) { | |
| 231 HandleError(FROM_HERE, itr->status()); | |
| 232 registrations->clear(); | |
| 233 return false; | |
| 234 } | |
| 235 | |
| 236 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | |
| 237 break; | |
| 238 | |
| 239 RegistrationData registration; | |
| 240 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | |
| 241 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
| 242 registrations->clear(); | |
| 243 return false; | |
| 244 } | |
| 245 registrations->push_back(registration); | |
| 246 } | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 bool ServiceWorkerDatabase::ReadRegistration( | |
| 251 int64 registration_id, | |
| 252 const GURL& origin, | |
| 253 RegistrationData* registration, | |
| 254 std::vector<ResourceRecord>* resources) { | |
| 255 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 256 DCHECK(registration); | |
| 257 DCHECK(resources); | |
| 258 | |
| 259 if (!LazyOpen(false) || is_disabled_) | |
| 260 return false; | |
| 261 | |
| 262 RegistrationData value; | |
| 263 if (!ReadRegistrationData(registration_id, origin, &value)) | |
| 264 return false; | |
| 265 | |
| 266 // TODO(nhiroki): Read ResourceRecords tied with this registration. | |
| 267 | |
| 268 *registration = value; | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 bool ServiceWorkerDatabase::WriteRegistration( | |
| 273 const RegistrationData& registration, | |
| 274 const std::vector<ResourceRecord>& resources) { | |
| 275 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 276 if (!LazyOpen(true) || is_disabled_) | |
| 277 return false; | |
| 278 | |
| 279 leveldb::WriteBatch batch; | |
| 280 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch); | |
| 281 BumpNextVersionIdIfNeeded(registration.version_id, &batch); | |
| 282 | |
| 283 // TODO(nhiroki): Skip to add the origin into the unique origin list if it | |
| 284 // has already been added. | |
| 285 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch); | |
| 286 | |
| 287 PutRegistrationDataToBatch(registration, &batch); | |
| 288 | |
| 289 // TODO(nhiroki): Write |resources| into the database. | |
| 290 return WriteBatch(&batch); | |
| 291 } | |
| 292 | |
| 293 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id, | |
| 294 const GURL& origin) { | |
| 295 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 296 if (!LazyOpen(false) || is_disabled_) | |
| 297 return false; | |
| 298 | |
| 299 RegistrationData registration; | |
| 300 if (!ReadRegistrationData(registration_id, origin, ®istration)) | |
| 301 return false; | |
| 302 | |
| 303 registration.is_active = true; | |
| 304 | |
| 305 leveldb::WriteBatch batch; | |
| 306 PutRegistrationDataToBatch(registration, &batch); | |
| 307 return WriteBatch(&batch); | |
| 308 } | |
| 309 | |
| 310 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id, | |
| 311 const GURL& origin, | |
| 312 const base::Time& time) { | |
| 313 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 314 if (!LazyOpen(false) || is_disabled_) | |
| 315 return false; | |
| 316 | |
| 317 RegistrationData registration; | |
| 318 if (!ReadRegistrationData(registration_id, origin, ®istration)) | |
| 319 return false; | |
| 320 | |
| 321 registration.last_update_check = time; | |
| 322 | |
| 323 leveldb::WriteBatch batch; | |
| 324 PutRegistrationDataToBatch(registration, &batch); | |
| 325 return WriteBatch(&batch); | |
| 326 } | |
| 327 | |
| 328 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id, | |
| 329 const GURL& origin) { | |
| 330 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
| 331 if (!LazyOpen(false) || is_disabled_) | |
| 332 return false; | |
| 333 | |
| 334 leveldb::WriteBatch batch; | |
| 335 | |
| 336 // Remove |origin| from unique origins if a registration specified by | |
| 337 // |registration_id| is the only one for |origin|. | |
| 338 // TODO(nhiroki): Check the uniqueness by more efficient way. | |
| 339 std::vector<RegistrationData> registrations; | |
| 340 if (!GetRegistrationsForOrigin(origin, ®istrations)) | |
| 341 return false; | |
| 342 if (registrations.size() == 1 && | |
| 343 registrations[0].registration_id == registration_id) { | |
| 344 batch.Delete(CreateUniqueOriginKey(origin)); | |
| 345 } | |
| 346 | |
| 347 batch.Delete(CreateRegistrationKey(registration_id, origin)); | |
| 348 | |
| 349 // TODO(nhiroki): Delete ResourceRecords tied with this registration. | |
| 350 return WriteBatch(&batch); | |
| 351 } | |
| 352 | |
| 98 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { | 353 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
| 99 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 354 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| 100 if (IsOpen()) | 355 if (IsOpen()) |
| 101 return true; | 356 return true; |
| 102 | 357 |
| 103 // Do not try to open a database if we tried and failed once. | 358 // Do not try to open a database if we tried and failed once. |
| 104 if (is_disabled_) | 359 if (is_disabled_) |
| 105 return false; | 360 return false; |
| 106 | 361 |
| 107 // When |path_| is empty, open a database in-memory. | 362 // When |path_| is empty, open a database in-memory. |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 127 leveldb::Status status = | 382 leveldb::Status status = |
| 128 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); | 383 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); |
| 129 if (!status.ok()) { | 384 if (!status.ok()) { |
| 130 DCHECK(!db); | 385 DCHECK(!db); |
| 131 // TODO(nhiroki): Should we retry to open the database? | 386 // TODO(nhiroki): Should we retry to open the database? |
| 132 HandleError(FROM_HERE, status); | 387 HandleError(FROM_HERE, status); |
| 133 return false; | 388 return false; |
| 134 } | 389 } |
| 135 db_.reset(db); | 390 db_.reset(db); |
| 136 | 391 |
| 137 if (IsEmpty() && !PopulateInitialData()) { | 392 int64 db_version; |
|
nhiroki
2014/04/25 11:05:01
fyi: Removed all write operations from LazyOpen().
michaeln
2014/04/26 00:29:02
fantastique ;)
| |
| 138 DLOG(ERROR) << "Failed to populate the database."; | 393 if (!ReadDatabaseVersion(&db_version)) |
| 139 db_.reset(); | |
| 140 return false; | 394 return false; |
| 141 } | 395 if (db_version > 0) |
| 396 is_initialized_ = true; | |
| 142 return true; | 397 return true; |
| 143 } | 398 } |
| 144 | 399 |
| 145 bool ServiceWorkerDatabase::PopulateInitialData() { | |
| 146 leveldb::WriteBatch batch; | |
| 147 batch.Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | |
| 148 batch.Put(kNextRegIdKey, base::Int64ToString(0)); | |
| 149 batch.Put(kNextResIdKey, base::Int64ToString(0)); | |
| 150 batch.Put(kNextVerIdKey, base::Int64ToString(0)); | |
| 151 return WriteBatch(&batch); | |
| 152 } | |
| 153 | |
| 154 bool ServiceWorkerDatabase::ReadNextAvailableId( | 400 bool ServiceWorkerDatabase::ReadNextAvailableId( |
| 155 const char* id_key, int64* next_avail_id) { | 401 const char* id_key, int64* next_avail_id) { |
| 156 DCHECK(id_key); | 402 DCHECK(id_key); |
| 157 DCHECK(next_avail_id); | 403 DCHECK(next_avail_id); |
| 158 | 404 |
| 159 std::string value; | 405 std::string value; |
| 160 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); | 406 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); |
| 407 if (status.IsNotFound()) { | |
| 408 // Nobody has gotten the next resource id for |id_key|. | |
| 409 *next_avail_id = 0; | |
| 410 return true; | |
| 411 } | |
| 412 | |
| 161 if (!status.ok()) { | 413 if (!status.ok()) { |
| 162 HandleError(FROM_HERE, status); | 414 HandleError(FROM_HERE, status); |
| 163 return false; | 415 return false; |
| 164 } | 416 } |
| 165 | 417 |
| 166 int64 parsed; | 418 int64 parsed; |
| 167 if (!base::StringToInt64(value, &parsed)) { | 419 if (!base::StringToInt64(value, &parsed)) { |
| 168 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 420 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
| 169 return false; | 421 return false; |
| 170 } | 422 } |
| 171 | 423 |
| 172 *next_avail_id = parsed; | 424 *next_avail_id = parsed; |
| 173 return true; | 425 return true; |
| 174 } | 426 } |
| 175 | 427 |
| 428 bool ServiceWorkerDatabase::ReadRegistrationData( | |
| 429 int64 registration_id, | |
| 430 const GURL& origin, | |
| 431 RegistrationData* registration) { | |
| 432 DCHECK(registration); | |
| 433 | |
| 434 std::string key = CreateRegistrationKey(registration_id, origin); | |
| 435 | |
| 436 std::string value; | |
| 437 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); | |
| 438 if (!status.ok()) { | |
| 439 if (!status.IsNotFound()) | |
| 440 HandleError(FROM_HERE, status); | |
| 441 return false; | |
| 442 } | |
| 443 | |
| 444 RegistrationData parsed; | |
| 445 if (!ParseRegistrationData(value, &parsed)) { | |
| 446 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
| 447 return false; | |
| 448 } | |
| 449 | |
| 450 *registration = parsed; | |
| 451 return true; | |
| 452 } | |
| 453 | |
| 454 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) { | |
| 455 std::string value; | |
| 456 leveldb::Status status = | |
| 457 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); | |
| 458 if (status.IsNotFound()) { | |
| 459 // The database hasn't been initialized yet. | |
| 460 *db_version = 0; | |
| 461 return true; | |
| 462 } | |
| 463 if (!status.ok()) { | |
| 464 HandleError(FROM_HERE, status); | |
| 465 return false; | |
| 466 } | |
| 467 | |
| 468 int64 parsed; | |
| 469 if (!base::StringToInt64(value, &parsed)) { | |
| 470 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
| 471 return false; | |
| 472 } | |
| 473 | |
|
michaeln
2014/04/26 00:29:02
??
const int kFirstValidVersion = 1;
if (parsed <
nhiroki
2014/04/28 04:15:59
Done.
| |
| 474 *db_version = parsed; | |
| 475 return true; | |
| 476 } | |
| 477 | |
| 176 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { | 478 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { |
| 177 DCHECK(batch); | 479 DCHECK(batch); |
| 178 DCHECK(!is_disabled_); | 480 DCHECK(!is_disabled_); |
| 481 | |
| 482 if (!is_initialized_) { | |
| 483 // Write the database schema version. | |
| 484 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | |
| 485 is_initialized_ = true; | |
| 486 } | |
| 487 | |
| 179 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); | 488 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
| 180 if (!status.ok()) { | 489 if (!status.ok()) { |
| 181 HandleError(FROM_HERE, status); | 490 HandleError(FROM_HERE, status); |
| 182 return false; | 491 return false; |
| 183 } | 492 } |
| 184 return true; | 493 return true; |
| 185 } | 494 } |
| 186 | 495 |
| 496 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( | |
| 497 int64 used_id, leveldb::WriteBatch* batch) { | |
| 498 DCHECK(batch); | |
| 499 if (next_avail_registration_id_ <= used_id) { | |
| 500 next_avail_registration_id_ = used_id + 1; | |
| 501 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); | |
| 502 } | |
| 503 } | |
| 504 | |
| 505 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( | |
| 506 int64 used_id, leveldb::WriteBatch* batch) { | |
| 507 DCHECK(batch); | |
| 508 if (next_avail_version_id_ <= used_id) { | |
| 509 next_avail_version_id_ = used_id + 1; | |
| 510 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); | |
| 511 } | |
| 512 } | |
| 513 | |
| 187 bool ServiceWorkerDatabase::IsOpen() { | 514 bool ServiceWorkerDatabase::IsOpen() { |
| 188 return db_.get() != NULL; | 515 return db_.get() != NULL; |
| 189 } | 516 } |
| 190 | 517 |
| 191 bool ServiceWorkerDatabase::IsEmpty() { | |
| 192 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
| 193 itr->SeekToFirst(); | |
| 194 // TODO(nhiroki): Handle an error. | |
| 195 DCHECK(itr->status().ok()); | |
| 196 return !itr->Valid(); | |
| 197 } | |
| 198 | |
| 199 void ServiceWorkerDatabase::HandleError( | 518 void ServiceWorkerDatabase::HandleError( |
| 200 const tracked_objects::Location& from_here, | 519 const tracked_objects::Location& from_here, |
| 201 const leveldb::Status& status) { | 520 const leveldb::Status& status) { |
| 202 // TODO(nhiroki): Add an UMA histogram. | 521 // TODO(nhiroki): Add an UMA histogram. |
| 203 DLOG(ERROR) << "Failed at: " << from_here.ToString() | 522 DLOG(ERROR) << "Failed at: " << from_here.ToString() |
| 204 << " with error: " << status.ToString(); | 523 << " with error: " << status.ToString(); |
| 205 is_disabled_ = true; | 524 is_disabled_ = true; |
| 206 if (status.IsCorruption()) | 525 if (status.IsCorruption()) |
| 207 was_corruption_detected_ = true; | 526 was_corruption_detected_ = true; |
| 208 db_.reset(); | 527 db_.reset(); |
| 209 } | 528 } |
| 210 | 529 |
| 211 } // namespace content | 530 } // namespace content |
| OLD | NEW |