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