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/metrics/histogram.h" |
12 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
13 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
14 #include "base/strings/string_split.h" | 15 #include "base/strings/string_split.h" |
15 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
17 #include "content/browser/service_worker/service_worker_database.pb.h" | 18 #include "content/browser/service_worker/service_worker_database.pb.h" |
18 #include "content/common/service_worker/service_worker_types.h" | 19 #include "content/common/service_worker/service_worker_types.h" |
19 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" | 20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
20 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 21 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
21 #include "third_party/leveldatabase/src/include/leveldb/env.h" | 22 #include "third_party/leveldatabase/src/include/leveldb/env.h" |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 | 71 |
71 const char kRegKeyPrefix[] = "REG:"; | 72 const char kRegKeyPrefix[] = "REG:"; |
72 const char kResKeyPrefix[] = "RES:"; | 73 const char kResKeyPrefix[] = "RES:"; |
73 const char kKeySeparator = '\x00'; | 74 const char kKeySeparator = '\x00'; |
74 | 75 |
75 const char kUncommittedResIdKeyPrefix[] = "URES:"; | 76 const char kUncommittedResIdKeyPrefix[] = "URES:"; |
76 const char kPurgeableResIdKeyPrefix[] = "PRES:"; | 77 const char kPurgeableResIdKeyPrefix[] = "PRES:"; |
77 | 78 |
78 const int64 kCurrentSchemaVersion = 1; | 79 const int64 kCurrentSchemaVersion = 1; |
79 | 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 |
80 bool RemovePrefix(const std::string& str, | 89 bool RemovePrefix(const std::string& str, |
81 const std::string& prefix, | 90 const std::string& prefix, |
82 std::string* out) { | 91 std::string* out) { |
83 if (!StartsWithASCII(str, prefix, true)) | 92 if (!StartsWithASCII(str, prefix, true)) |
84 return false; | 93 return false; |
85 if (out) | 94 if (out) |
86 *out = str.substr(prefix.size()); | 95 *out = str.substr(prefix.size()); |
87 return true; | 96 return true; |
88 } | 97 } |
89 | 98 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 // Value should be empty. | 171 // Value should be empty. |
163 batch->Put(CreateUniqueOriginKey(origin), ""); | 172 batch->Put(CreateUniqueOriginKey(origin), ""); |
164 } | 173 } |
165 | 174 |
166 void PutPurgeableResourceIdToBatch(int64 resource_id, | 175 void PutPurgeableResourceIdToBatch(int64 resource_id, |
167 leveldb::WriteBatch* batch) { | 176 leveldb::WriteBatch* batch) { |
168 // Value should be empty. | 177 // Value should be empty. |
169 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); | 178 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); |
170 } | 179 } |
171 | 180 |
172 bool ParseRegistrationData(const std::string& serialized, | 181 ServiceWorkerDatabase::Status ParseId( |
173 ServiceWorkerDatabase::RegistrationData* out) { | 182 const std::string& serialized, |
| 183 int64* out) { |
| 184 DCHECK(out); |
| 185 int64 id; |
| 186 if (!base::StringToInt64(serialized, &id) || id < 0) |
| 187 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
| 188 *out = id; |
| 189 return ServiceWorkerDatabase::STATUS_OK; |
| 190 } |
| 191 |
| 192 ServiceWorkerDatabase::Status ParseDatabaseVersion( |
| 193 const std::string& serialized, |
| 194 int64* out) { |
| 195 DCHECK(out); |
| 196 const int kFirstValidVersion = 1; |
| 197 int64 version; |
| 198 if (!base::StringToInt64(serialized, &version) || |
| 199 version < kFirstValidVersion) { |
| 200 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
| 201 } |
| 202 if (kCurrentSchemaVersion < version) { |
| 203 DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version" |
| 204 << " than the current latest version: " |
| 205 << version << " vs " << kCurrentSchemaVersion; |
| 206 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
| 207 } |
| 208 *out = version; |
| 209 return ServiceWorkerDatabase::STATUS_OK; |
| 210 } |
| 211 |
| 212 ServiceWorkerDatabase::Status ParseRegistrationData( |
| 213 const std::string& serialized, |
| 214 ServiceWorkerDatabase::RegistrationData* out) { |
174 DCHECK(out); | 215 DCHECK(out); |
175 ServiceWorkerRegistrationData data; | 216 ServiceWorkerRegistrationData data; |
176 if (!data.ParseFromString(serialized)) | 217 if (!data.ParseFromString(serialized)) |
177 return false; | 218 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
178 | 219 |
179 GURL scope_url(data.scope_url()); | 220 GURL scope_url(data.scope_url()); |
180 GURL script_url(data.script_url()); | 221 GURL script_url(data.script_url()); |
181 if (!scope_url.is_valid() || | 222 if (!scope_url.is_valid() || |
182 !script_url.is_valid() || | 223 !script_url.is_valid() || |
183 scope_url.GetOrigin() != script_url.GetOrigin()) { | 224 scope_url.GetOrigin() != script_url.GetOrigin()) { |
184 return false; | 225 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
185 } | 226 } |
186 | 227 |
187 // Convert ServiceWorkerRegistrationData to RegistrationData. | 228 // Convert ServiceWorkerRegistrationData to RegistrationData. |
188 out->registration_id = data.registration_id(); | 229 out->registration_id = data.registration_id(); |
189 out->scope = scope_url; | 230 out->scope = scope_url; |
190 out->script = script_url; | 231 out->script = script_url; |
191 out->version_id = data.version_id(); | 232 out->version_id = data.version_id(); |
192 out->is_active = data.is_active(); | 233 out->is_active = data.is_active(); |
193 out->has_fetch_handler = data.has_fetch_handler(); | 234 out->has_fetch_handler = data.has_fetch_handler(); |
194 out->last_update_check = | 235 out->last_update_check = |
195 base::Time::FromInternalValue(data.last_update_check_time()); | 236 base::Time::FromInternalValue(data.last_update_check_time()); |
196 return true; | 237 return ServiceWorkerDatabase::STATUS_OK; |
197 } | 238 } |
198 | 239 |
199 bool ParseResourceRecord(const std::string& serialized, | 240 ServiceWorkerDatabase::Status ParseResourceRecord( |
200 ServiceWorkerDatabase::ResourceRecord* out) { | 241 const std::string& serialized, |
| 242 ServiceWorkerDatabase::ResourceRecord* out) { |
201 DCHECK(out); | 243 DCHECK(out); |
202 ServiceWorkerResourceRecord record; | 244 ServiceWorkerResourceRecord record; |
203 if (!record.ParseFromString(serialized)) | 245 if (!record.ParseFromString(serialized)) |
204 return false; | 246 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
205 | 247 |
206 GURL url(record.url()); | 248 GURL url(record.url()); |
207 if (!url.is_valid()) | 249 if (!url.is_valid()) |
208 return false; | 250 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
209 | 251 |
210 // Convert ServiceWorkerResourceRecord to ResourceRecord. | 252 // Convert ServiceWorkerResourceRecord to ResourceRecord. |
211 out->resource_id = record.resource_id(); | 253 out->resource_id = record.resource_id(); |
212 out->url = url; | 254 out->url = url; |
213 return true; | 255 return ServiceWorkerDatabase::STATUS_OK; |
214 } | 256 } |
215 | 257 |
216 ServiceWorkerDatabase::Status LevelDBStatusToStatus( | 258 ServiceWorkerDatabase::Status LevelDBStatusToStatus( |
217 const leveldb::Status& status) { | 259 const leveldb::Status& status) { |
218 if (status.ok()) | 260 if (status.ok()) |
219 return ServiceWorkerDatabase::STATUS_OK; | 261 return ServiceWorkerDatabase::STATUS_OK; |
220 else if (status.IsNotFound()) | 262 else if (status.IsNotFound()) |
221 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; | 263 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; |
| 264 else if (status.IsIOError()) |
| 265 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR; |
222 else if (status.IsCorruption()) | 266 else if (status.IsCorruption()) |
223 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; | 267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
224 else | 268 else |
225 return ServiceWorkerDatabase::STATUS_ERROR_FAILED; | 269 return ServiceWorkerDatabase::STATUS_ERROR_FAILED; |
226 } | 270 } |
227 | 271 |
| 272 const char* StatusToString(ServiceWorkerDatabase::Status status) { |
| 273 switch (status) { |
| 274 case ServiceWorkerDatabase::STATUS_OK: |
| 275 return "Database OK"; |
| 276 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: |
| 277 return "Database not found"; |
| 278 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR: |
| 279 return "Database IO error"; |
| 280 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED: |
| 281 return "Database corrupted"; |
| 282 case ServiceWorkerDatabase::STATUS_ERROR_FAILED: |
| 283 return "Database operation failed"; |
| 284 case ServiceWorkerDatabase::STATUS_ERROR_MAX: |
| 285 NOTREACHED(); |
| 286 return "Database unknown error"; |
| 287 } |
| 288 NOTREACHED(); |
| 289 return "Database unknown error"; |
| 290 } |
| 291 |
228 } // namespace | 292 } // namespace |
229 | 293 |
230 ServiceWorkerDatabase::RegistrationData::RegistrationData() | 294 ServiceWorkerDatabase::RegistrationData::RegistrationData() |
231 : registration_id(kInvalidServiceWorkerRegistrationId), | 295 : registration_id(kInvalidServiceWorkerRegistrationId), |
232 version_id(kInvalidServiceWorkerVersionId), | 296 version_id(kInvalidServiceWorkerVersionId), |
233 is_active(false), | 297 is_active(false), |
234 has_fetch_handler(false) { | 298 has_fetch_handler(false) { |
235 } | 299 } |
236 | 300 |
237 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { | 301 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 DCHECK(origins->empty()); | 356 DCHECK(origins->empty()); |
293 | 357 |
294 Status status = LazyOpen(false); | 358 Status status = LazyOpen(false); |
295 if (IsNewOrNonexistentDatabase(status)) | 359 if (IsNewOrNonexistentDatabase(status)) |
296 return STATUS_OK; | 360 return STATUS_OK; |
297 if (status != STATUS_OK) | 361 if (status != STATUS_OK) |
298 return status; | 362 return status; |
299 | 363 |
300 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 364 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
301 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { | 365 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { |
302 if (!itr->status().ok()) { | 366 status = LevelDBStatusToStatus(itr->status()); |
303 HandleError(FROM_HERE, itr->status()); | 367 if (status != STATUS_OK) { |
| 368 HandleReadResult(FROM_HERE, status); |
304 origins->clear(); | 369 origins->clear(); |
305 return LevelDBStatusToStatus(itr->status()); | 370 return status; |
306 } | 371 } |
307 | 372 |
308 std::string origin; | 373 std::string origin; |
309 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) | 374 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) |
310 break; | 375 break; |
311 origins->insert(GURL(origin)); | 376 origins->insert(GURL(origin)); |
312 } | 377 } |
313 return STATUS_OK; | 378 |
| 379 HandleReadResult(FROM_HERE, status); |
| 380 return status; |
314 } | 381 } |
315 | 382 |
316 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( | 383 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( |
317 const GURL& origin, | 384 const GURL& origin, |
318 std::vector<RegistrationData>* registrations) { | 385 std::vector<RegistrationData>* registrations) { |
319 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 386 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
320 DCHECK(registrations->empty()); | 387 DCHECK(registrations->empty()); |
321 | 388 |
322 Status status = LazyOpen(false); | 389 Status status = LazyOpen(false); |
323 if (IsNewOrNonexistentDatabase(status)) | 390 if (IsNewOrNonexistentDatabase(status)) |
324 return STATUS_OK; | 391 return STATUS_OK; |
325 if (status != STATUS_OK) | 392 if (status != STATUS_OK) |
326 return status; | 393 return status; |
327 | 394 |
328 // Create a key prefix for registrations. | 395 // Create a key prefix for registrations. |
329 std::string prefix = base::StringPrintf( | 396 std::string prefix = base::StringPrintf( |
330 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); | 397 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); |
331 | 398 |
332 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 399 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
333 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 400 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
334 if (!itr->status().ok()) { | 401 status = LevelDBStatusToStatus(itr->status()); |
335 HandleError(FROM_HERE, itr->status()); | 402 if (status != STATUS_OK) { |
| 403 HandleReadResult(FROM_HERE, status); |
336 registrations->clear(); | 404 registrations->clear(); |
337 return LevelDBStatusToStatus(itr->status()); | 405 return status; |
338 } | 406 } |
339 | 407 |
340 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | 408 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) |
341 break; | 409 break; |
342 | 410 |
343 RegistrationData registration; | 411 RegistrationData registration; |
344 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | 412 status = ParseRegistrationData(itr->value().ToString(), ®istration); |
345 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 413 if (status != STATUS_OK) { |
| 414 HandleReadResult(FROM_HERE, status); |
346 registrations->clear(); | 415 registrations->clear(); |
347 return STATUS_ERROR_CORRUPTED; | 416 return status; |
348 } | 417 } |
349 registrations->push_back(registration); | 418 registrations->push_back(registration); |
350 } | 419 } |
351 return STATUS_OK; | 420 |
| 421 HandleReadResult(FROM_HERE, status); |
| 422 return status; |
352 } | 423 } |
353 | 424 |
354 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( | 425 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( |
355 std::vector<RegistrationData>* registrations) { | 426 std::vector<RegistrationData>* registrations) { |
356 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 427 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
357 DCHECK(registrations->empty()); | 428 DCHECK(registrations->empty()); |
358 | 429 |
359 Status status = LazyOpen(false); | 430 Status status = LazyOpen(false); |
360 if (IsNewOrNonexistentDatabase(status)) | 431 if (IsNewOrNonexistentDatabase(status)) |
361 return STATUS_OK; | 432 return STATUS_OK; |
362 if (status != STATUS_OK) | 433 if (status != STATUS_OK) |
363 return status; | 434 return status; |
364 | 435 |
365 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 436 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
366 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { | 437 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { |
367 if (!itr->status().ok()) { | 438 status = LevelDBStatusToStatus(itr->status()); |
368 HandleError(FROM_HERE, itr->status()); | 439 if (status != STATUS_OK) { |
| 440 HandleReadResult(FROM_HERE, status); |
369 registrations->clear(); | 441 registrations->clear(); |
370 return LevelDBStatusToStatus(itr->status()); | 442 return status; |
371 } | 443 } |
372 | 444 |
373 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) | 445 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) |
374 break; | 446 break; |
375 | 447 |
376 RegistrationData registration; | 448 RegistrationData registration; |
377 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | 449 status = ParseRegistrationData(itr->value().ToString(), ®istration); |
378 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 450 if (status != STATUS_OK) { |
| 451 HandleReadResult(FROM_HERE, status); |
379 registrations->clear(); | 452 registrations->clear(); |
380 return STATUS_ERROR_CORRUPTED; | 453 return status; |
381 } | 454 } |
382 registrations->push_back(registration); | 455 registrations->push_back(registration); |
383 } | 456 } |
384 return STATUS_OK; | 457 |
| 458 HandleReadResult(FROM_HERE, status); |
| 459 return status; |
385 } | 460 } |
386 | 461 |
387 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( | 462 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( |
388 int64 registration_id, | 463 int64 registration_id, |
389 const GURL& origin, | 464 const GURL& origin, |
390 RegistrationData* registration, | 465 RegistrationData* registration, |
391 std::vector<ResourceRecord>* resources) { | 466 std::vector<ResourceRecord>* resources) { |
392 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 467 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
393 DCHECK(registration); | 468 DCHECK(registration); |
394 DCHECK(resources); | 469 DCHECK(resources); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
668 } | 743 } |
669 | 744 |
670 leveldb::Options options; | 745 leveldb::Options options; |
671 options.create_if_missing = create_if_missing; | 746 options.create_if_missing = create_if_missing; |
672 if (use_in_memory_db) { | 747 if (use_in_memory_db) { |
673 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); | 748 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); |
674 options.env = env_.get(); | 749 options.env = env_.get(); |
675 } | 750 } |
676 | 751 |
677 leveldb::DB* db = NULL; | 752 leveldb::DB* db = NULL; |
678 leveldb::Status db_status = | 753 Status status = LevelDBStatusToStatus( |
679 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); | 754 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db)); |
680 if (!db_status.ok()) { | 755 HandleOpenResult(FROM_HERE, status); |
| 756 if (status != STATUS_OK) { |
681 DCHECK(!db); | 757 DCHECK(!db); |
682 // TODO(nhiroki): Should we retry to open the database? | 758 // TODO(nhiroki): Should we retry to open the database? |
683 HandleError(FROM_HERE, db_status); | 759 return status; |
684 return LevelDBStatusToStatus(db_status); | |
685 } | 760 } |
686 db_.reset(db); | 761 db_.reset(db); |
687 | 762 |
688 int64 db_version; | 763 int64 db_version; |
689 Status status = ReadDatabaseVersion(&db_version); | 764 status = ReadDatabaseVersion(&db_version); |
690 if (status != STATUS_OK) | 765 if (status != STATUS_OK) |
691 return status; | 766 return status; |
692 DCHECK_LE(0, db_version); | 767 DCHECK_LE(0, db_version); |
693 if (db_version > 0) | 768 if (db_version > 0) |
694 state_ = INITIALIZED; | 769 state_ = INITIALIZED; |
695 return STATUS_OK; | 770 return STATUS_OK; |
696 } | 771 } |
697 | 772 |
698 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase( | 773 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase( |
699 ServiceWorkerDatabase::Status status) { | 774 ServiceWorkerDatabase::Status status) { |
700 if (status == STATUS_ERROR_NOT_FOUND) | 775 if (status == STATUS_ERROR_NOT_FOUND) |
701 return true; | 776 return true; |
702 if (status == STATUS_OK && state_ == UNINITIALIZED) | 777 if (status == STATUS_OK && state_ == UNINITIALIZED) |
703 return true; | 778 return true; |
704 return false; | 779 return false; |
705 } | 780 } |
706 | 781 |
707 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( | 782 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( |
708 const char* id_key, | 783 const char* id_key, |
709 int64* next_avail_id) { | 784 int64* next_avail_id) { |
710 DCHECK(id_key); | 785 DCHECK(id_key); |
711 DCHECK(next_avail_id); | 786 DCHECK(next_avail_id); |
712 | 787 |
713 std::string value; | 788 std::string value; |
714 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); | 789 Status status = LevelDBStatusToStatus( |
715 if (status.IsNotFound()) { | 790 db_->Get(leveldb::ReadOptions(), id_key, &value)); |
| 791 if (status == STATUS_ERROR_NOT_FOUND) { |
716 // Nobody has gotten the next resource id for |id_key|. | 792 // Nobody has gotten the next resource id for |id_key|. |
717 *next_avail_id = 0; | 793 *next_avail_id = 0; |
| 794 HandleReadResult(FROM_HERE, STATUS_OK); |
718 return STATUS_OK; | 795 return STATUS_OK; |
| 796 } else if (status != STATUS_OK) { |
| 797 HandleReadResult(FROM_HERE, status); |
| 798 return status; |
719 } | 799 } |
720 | 800 |
721 if (!status.ok()) { | 801 status = ParseId(value, next_avail_id); |
722 HandleError(FROM_HERE, status); | 802 HandleReadResult(FROM_HERE, status); |
723 return LevelDBStatusToStatus(status); | 803 return status; |
724 } | |
725 | |
726 int64 parsed; | |
727 if (!base::StringToInt64(value, &parsed)) { | |
728 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
729 return STATUS_ERROR_CORRUPTED; | |
730 } | |
731 | |
732 *next_avail_id = parsed; | |
733 return STATUS_OK; | |
734 } | 804 } |
735 | 805 |
736 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( | 806 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( |
737 int64 registration_id, | 807 int64 registration_id, |
738 const GURL& origin, | 808 const GURL& origin, |
739 RegistrationData* registration) { | 809 RegistrationData* registration) { |
740 DCHECK(registration); | 810 DCHECK(registration); |
741 | 811 |
742 std::string key = CreateRegistrationKey(registration_id, origin); | 812 const std::string key = CreateRegistrationKey(registration_id, origin); |
743 | |
744 std::string value; | 813 std::string value; |
745 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); | 814 Status status = LevelDBStatusToStatus( |
746 if (!status.ok()) { | 815 db_->Get(leveldb::ReadOptions(), key, &value)); |
747 if (!status.IsNotFound()) | 816 if (status != STATUS_OK) { |
748 HandleError(FROM_HERE, status); | 817 HandleReadResult( |
749 return LevelDBStatusToStatus(status); | 818 FROM_HERE, |
| 819 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status); |
| 820 return status; |
750 } | 821 } |
751 | 822 |
752 RegistrationData parsed; | 823 status = ParseRegistrationData(value, registration); |
753 if (!ParseRegistrationData(value, &parsed)) { | 824 HandleReadResult(FROM_HERE, status); |
754 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 825 return status; |
755 return STATUS_ERROR_CORRUPTED; | |
756 } | |
757 | |
758 *registration = parsed; | |
759 return STATUS_OK; | |
760 } | 826 } |
761 | 827 |
762 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( | 828 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( |
763 int64 version_id, | 829 int64 version_id, |
764 std::vector<ResourceRecord>* resources) { | 830 std::vector<ResourceRecord>* resources) { |
765 DCHECK(resources); | 831 DCHECK(resources->empty()); |
766 | 832 |
767 std::string prefix = CreateResourceRecordKeyPrefix(version_id); | 833 Status status = STATUS_OK; |
| 834 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
| 835 |
768 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 836 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
769 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 837 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
770 if (!itr->status().ok()) { | 838 Status status = LevelDBStatusToStatus(itr->status()); |
771 HandleError(FROM_HERE, itr->status()); | 839 if (status != STATUS_OK) { |
| 840 HandleReadResult(FROM_HERE, status); |
772 resources->clear(); | 841 resources->clear(); |
773 return LevelDBStatusToStatus(itr->status()); | 842 return status; |
774 } | 843 } |
775 | 844 |
776 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | 845 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) |
777 break; | 846 break; |
778 | 847 |
779 ResourceRecord resource; | 848 ResourceRecord resource; |
780 if (!ParseResourceRecord(itr->value().ToString(), &resource)) { | 849 status = ParseResourceRecord(itr->value().ToString(), &resource); |
781 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 850 if (status != STATUS_OK) { |
| 851 HandleReadResult(FROM_HERE, status); |
782 resources->clear(); | 852 resources->clear(); |
783 return STATUS_ERROR_CORRUPTED; | 853 return status; |
784 } | 854 } |
785 resources->push_back(resource); | 855 resources->push_back(resource); |
786 } | 856 } |
787 return STATUS_OK; | 857 |
| 858 HandleReadResult(FROM_HERE, status); |
| 859 return status; |
788 } | 860 } |
789 | 861 |
790 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( | 862 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( |
791 int64 version_id, | 863 int64 version_id, |
792 std::vector<int64>* newly_purgeable_resources, | 864 std::vector<int64>* newly_purgeable_resources, |
793 leveldb::WriteBatch* batch) { | 865 leveldb::WriteBatch* batch) { |
794 DCHECK(batch); | 866 DCHECK(batch); |
795 | 867 |
796 std::string prefix = CreateResourceRecordKeyPrefix(version_id); | 868 Status status = STATUS_OK; |
| 869 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
| 870 |
797 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 871 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
798 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 872 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
799 if (!itr->status().ok()) { | 873 status = LevelDBStatusToStatus(itr->status()); |
800 HandleError(FROM_HERE, itr->status()); | 874 if (status != STATUS_OK) { |
801 return LevelDBStatusToStatus(itr->status()); | 875 HandleReadResult(FROM_HERE, status); |
| 876 return status; |
802 } | 877 } |
803 | 878 |
804 std::string key = itr->key().ToString(); | 879 const std::string key = itr->key().ToString(); |
805 std::string unprefixed; | 880 std::string unprefixed; |
806 if (!RemovePrefix(key, prefix, &unprefixed)) | 881 if (!RemovePrefix(key, prefix, &unprefixed)) |
807 break; | 882 break; |
808 | 883 |
809 int64 resource_id; | 884 int64 resource_id; |
810 if (!base::StringToInt64(unprefixed, &resource_id)) { | 885 status = ParseId(unprefixed, &resource_id); |
811 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 886 if (status != STATUS_OK) { |
812 return STATUS_ERROR_CORRUPTED; | 887 HandleReadResult(FROM_HERE, status); |
| 888 return status; |
813 } | 889 } |
814 | 890 |
815 // Remove a resource record. | 891 // Remove a resource record. |
816 batch->Delete(key); | 892 batch->Delete(key); |
817 | 893 |
818 // Currently resource sharing across versions and registrations is not | 894 // Currently resource sharing across versions and registrations is not |
819 // supported, so we can purge this without caring about it. | 895 // supported, so we can purge this without caring about it. |
820 PutPurgeableResourceIdToBatch(resource_id, batch); | 896 PutPurgeableResourceIdToBatch(resource_id, batch); |
821 newly_purgeable_resources->push_back(resource_id); | 897 newly_purgeable_resources->push_back(resource_id); |
822 } | 898 } |
823 return STATUS_OK; | 899 |
| 900 HandleReadResult(FROM_HERE, status); |
| 901 return status; |
824 } | 902 } |
825 | 903 |
826 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( | 904 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( |
827 const char* id_key_prefix, | 905 const char* id_key_prefix, |
828 std::set<int64>* ids) { | 906 std::set<int64>* ids) { |
829 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 907 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
830 DCHECK(id_key_prefix); | 908 DCHECK(id_key_prefix); |
831 DCHECK(ids->empty()); | 909 DCHECK(ids->empty()); |
832 | 910 |
833 Status status = LazyOpen(false); | 911 Status status = LazyOpen(false); |
834 if (IsNewOrNonexistentDatabase(status)) | 912 if (IsNewOrNonexistentDatabase(status)) |
835 return STATUS_OK; | 913 return STATUS_OK; |
836 if (status != STATUS_OK) | 914 if (status != STATUS_OK) |
837 return status; | 915 return status; |
838 | 916 |
839 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 917 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
840 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { | 918 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { |
841 if (!itr->status().ok()) { | 919 status = LevelDBStatusToStatus(itr->status()); |
842 HandleError(FROM_HERE, itr->status()); | 920 if (status != STATUS_OK) { |
| 921 HandleReadResult(FROM_HERE, status); |
843 ids->clear(); | 922 ids->clear(); |
844 return LevelDBStatusToStatus(itr->status()); | 923 return status; |
845 } | 924 } |
846 | 925 |
847 std::string unprefixed; | 926 std::string unprefixed; |
848 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) | 927 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) |
849 break; | 928 break; |
850 | 929 |
851 int64 resource_id; | 930 int64 resource_id; |
852 if (!base::StringToInt64(unprefixed, &resource_id)) { | 931 status = ParseId(unprefixed, &resource_id); |
853 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 932 if (status != STATUS_OK) { |
| 933 HandleReadResult(FROM_HERE, status); |
854 ids->clear(); | 934 ids->clear(); |
855 return STATUS_ERROR_CORRUPTED; | 935 return status; |
856 } | 936 } |
857 ids->insert(resource_id); | 937 ids->insert(resource_id); |
858 } | 938 } |
859 return STATUS_OK; | 939 |
| 940 HandleReadResult(FROM_HERE, status); |
| 941 return status; |
860 } | 942 } |
861 | 943 |
862 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( | 944 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( |
863 const char* id_key_prefix, | 945 const char* id_key_prefix, |
864 const std::set<int64>& ids) { | 946 const std::set<int64>& ids) { |
865 leveldb::WriteBatch batch; | 947 leveldb::WriteBatch batch; |
866 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); | 948 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); |
867 if (status != STATUS_OK) | 949 if (status != STATUS_OK) |
868 return status; | 950 return status; |
869 return WriteBatch(&batch); | 951 return WriteBatch(&batch); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
914 for (std::set<int64>::const_iterator itr = ids.begin(); | 996 for (std::set<int64>::const_iterator itr = ids.begin(); |
915 itr != ids.end(); ++itr) { | 997 itr != ids.end(); ++itr) { |
916 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr)); | 998 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr)); |
917 } | 999 } |
918 return STATUS_OK; | 1000 return STATUS_OK; |
919 } | 1001 } |
920 | 1002 |
921 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( | 1003 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( |
922 int64* db_version) { | 1004 int64* db_version) { |
923 std::string value; | 1005 std::string value; |
924 leveldb::Status status = | 1006 Status status = LevelDBStatusToStatus( |
925 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); | 1007 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value)); |
926 if (status.IsNotFound()) { | 1008 if (status == STATUS_ERROR_NOT_FOUND) { |
927 // The database hasn't been initialized yet. | 1009 // The database hasn't been initialized yet. |
928 *db_version = 0; | 1010 *db_version = 0; |
| 1011 HandleReadResult(FROM_HERE, STATUS_OK); |
929 return STATUS_OK; | 1012 return STATUS_OK; |
930 } | 1013 } |
931 if (!status.ok()) { | 1014 |
932 HandleError(FROM_HERE, status); | 1015 if (status != STATUS_OK) { |
933 return LevelDBStatusToStatus(status); | 1016 HandleReadResult(FROM_HERE, status); |
| 1017 return status; |
934 } | 1018 } |
935 | 1019 |
936 int64 parsed; | 1020 status = ParseDatabaseVersion(value, db_version); |
937 if (!base::StringToInt64(value, &parsed)) { | 1021 HandleReadResult(FROM_HERE, status); |
938 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 1022 return status; |
939 return STATUS_ERROR_CORRUPTED; | |
940 } | |
941 | |
942 const int kFirstValidVersion = 1; | |
943 if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) { | |
944 HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version")); | |
945 return STATUS_ERROR_CORRUPTED; | |
946 } | |
947 | |
948 *db_version = parsed; | |
949 return STATUS_OK; | |
950 } | 1023 } |
951 | 1024 |
952 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( | 1025 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( |
953 leveldb::WriteBatch* batch) { | 1026 leveldb::WriteBatch* batch) { |
954 DCHECK(batch); | 1027 DCHECK(batch); |
955 DCHECK_NE(DISABLED, state_); | 1028 DCHECK_NE(DISABLED, state_); |
956 | 1029 |
957 if (state_ == UNINITIALIZED) { | 1030 if (state_ == UNINITIALIZED) { |
958 // Write the database schema version. | 1031 // Write the database schema version. |
959 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | 1032 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); |
960 state_ = INITIALIZED; | 1033 state_ = INITIALIZED; |
961 } | 1034 } |
962 | 1035 |
963 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); | 1036 Status status = LevelDBStatusToStatus( |
964 if (!status.ok()) | 1037 db_->Write(leveldb::WriteOptions(), batch)); |
965 HandleError(FROM_HERE, status); | 1038 HandleWriteResult(FROM_HERE, status); |
966 return LevelDBStatusToStatus(status); | 1039 return status; |
967 } | 1040 } |
968 | 1041 |
969 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( | 1042 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( |
970 int64 used_id, leveldb::WriteBatch* batch) { | 1043 int64 used_id, leveldb::WriteBatch* batch) { |
971 DCHECK(batch); | 1044 DCHECK(batch); |
972 if (next_avail_registration_id_ <= used_id) { | 1045 if (next_avail_registration_id_ <= used_id) { |
973 next_avail_registration_id_ = used_id + 1; | 1046 next_avail_registration_id_ = used_id + 1; |
974 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); | 1047 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); |
975 } | 1048 } |
976 } | 1049 } |
977 | 1050 |
978 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( | 1051 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( |
979 int64 used_id, leveldb::WriteBatch* batch) { | 1052 int64 used_id, leveldb::WriteBatch* batch) { |
980 DCHECK(batch); | 1053 DCHECK(batch); |
981 if (next_avail_version_id_ <= used_id) { | 1054 if (next_avail_version_id_ <= used_id) { |
982 next_avail_version_id_ = used_id + 1; | 1055 next_avail_version_id_ = used_id + 1; |
983 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); | 1056 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); |
984 } | 1057 } |
985 } | 1058 } |
986 | 1059 |
987 bool ServiceWorkerDatabase::IsOpen() { | 1060 bool ServiceWorkerDatabase::IsOpen() { |
988 return db_ != NULL; | 1061 return db_ != NULL; |
989 } | 1062 } |
990 | 1063 |
991 void ServiceWorkerDatabase::HandleError( | 1064 void ServiceWorkerDatabase::Disable( |
992 const tracked_objects::Location& from_here, | 1065 const tracked_objects::Location& from_here, |
993 const leveldb::Status& status) { | 1066 Status status) { |
994 // TODO(nhiroki): Add an UMA histogram. | |
995 DLOG(ERROR) << "Failed at: " << from_here.ToString() | 1067 DLOG(ERROR) << "Failed at: " << from_here.ToString() |
996 << " with error: " << status.ToString(); | 1068 << " with error: " << StatusToString(status); |
| 1069 DLOG(ERROR) << "ServiceWorkerDatabase is disabled."; |
997 state_ = DISABLED; | 1070 state_ = DISABLED; |
998 db_.reset(); | 1071 db_.reset(); |
999 } | 1072 } |
1000 | 1073 |
| 1074 void ServiceWorkerDatabase::HandleOpenResult( |
| 1075 const tracked_objects::Location& from_here, |
| 1076 Status status) { |
| 1077 if (status != ServiceWorkerDatabase::STATUS_OK) |
| 1078 Disable(from_here, status); |
| 1079 UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel, |
| 1080 status, |
| 1081 ServiceWorkerDatabase::STATUS_ERROR_MAX); |
| 1082 } |
| 1083 |
| 1084 void ServiceWorkerDatabase::HandleReadResult( |
| 1085 const tracked_objects::Location& from_here, |
| 1086 Status status) { |
| 1087 if (status != ServiceWorkerDatabase::STATUS_OK) |
| 1088 Disable(from_here, status); |
| 1089 UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel, |
| 1090 status, |
| 1091 ServiceWorkerDatabase::STATUS_ERROR_MAX); |
| 1092 } |
| 1093 |
| 1094 void ServiceWorkerDatabase::HandleWriteResult( |
| 1095 const tracked_objects::Location& from_here, |
| 1096 Status status) { |
| 1097 if (status != ServiceWorkerDatabase::STATUS_OK) |
| 1098 Disable(from_here, status); |
| 1099 UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel, |
| 1100 status, |
| 1101 ServiceWorkerDatabase::STATUS_ERROR_MAX); |
| 1102 } |
| 1103 |
1001 } // namespace content | 1104 } // namespace content |
OLD | NEW |