1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/service_worker/service_worker_database.h" 6 7 #include <string> 8 9 #include "base/file_util.h" 10 #include "base/location.h" 11 #include "base/logging.h" 12 #include "base/metrics/histogram.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_split.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "content/browser/service_worker/service_worker_database.pb.h" 19 #include "content/common/service_worker/service_worker_types.h" 20 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 21 #include "third_party/leveldatabase/src/include/leveldb/db.h" 22 #include "third_party/leveldatabase/src/include/leveldb/env.h" 23 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 24 25 // LevelDB database schema 26 // ======================= 27 // 28 // NOTE 29 // - int64 value is serialized as a string by base::Int64ToString(). 30 // - GURL value is serialized as a string by GURL::spec(). 31 // 32 // Version 1 (in sorted order) 33 // key: "INITDATA_DB_VERSION" 34 // value: "1" 35 // 36 // key: "INITDATA_NEXT_REGISTRATION_ID" 37 // value: <int64 'next_available_registration_id'> 38 // 39 // key: "INITDATA_NEXT_RESOURCE_ID" 40 // value: <int64 'next_available_resource_id'> 41 // 42 // key: "INITDATA_NEXT_VERSION_ID" 43 // value: <int64 'next_available_version_id'> 44 // 45 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'> 46 // value: <empty> 47 // 48 // key: "PRES:" + <int64 'purgeable_resource_id'> 49 // value: <empty> 50 // 51 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'> 52 // (ex. "REG:http://example.com\x00123456") 53 // value: <ServiceWorkerRegistrationData serialized as a string> 54 // 55 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'> 56 // (ex. "RES:123456\x00654321") 57 // value: <ServiceWorkerResourceRecord serialized as a string> 58 // 59 // key: "URES:" + <int64 'uncommitted_resource_id'> 60 // value: <empty> 61 62 namespace content { 63 64 namespace { 65 66 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION"; 67 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID"; 68 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID"; 69 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID"; 70 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:"; 71 72 const char kRegKeyPrefix[] = "REG:"; 73 const char kResKeyPrefix[] = "RES:"; 74 const char kKeySeparator = '\x00'; 75 76 const char kUncommittedResIdKeyPrefix[] = "URES:"; 77 const char kPurgeableResIdKeyPrefix[] = "PRES:"; 78 79 const int64 kCurrentSchemaVersion = 1; 80 81 // For histogram. 82 const char kOpenResultHistogramLabel[] = 83 "ServiceWorker.Database.OpenResult"; 84 const char kReadResultHistogramLabel[] = 85 "ServiceWorker.Database.ReadResult"; 86 const char kWriteResultHistogramLabel[] = 87 "ServiceWorker.Database.WriteResult"; 88 89 bool RemovePrefix(const std::string& str, 90 const std::string& prefix, 91 std::string* out) { 92 if (!StartsWithASCII(str, prefix, true)) 93 return false; 94 if (out) 95 *out = str.substr(prefix.size()); 96 return true; 97 } 98 99 std::string CreateRegistrationKey(int64 registration_id, 100 const GURL& origin) { 101 return base::StringPrintf("%s%s%c%s", 102 kRegKeyPrefix, 103 origin.spec().c_str(), 104 kKeySeparator, 105 base::Int64ToString(registration_id).c_str()); 106 } 107 108 std::string CreateResourceRecordKeyPrefix(int64 version_id) { 109 return base::StringPrintf("%s%s%c", 110 kResKeyPrefix, 111 base::Int64ToString(version_id).c_str(), 112 kKeySeparator); 113 } 114 115 std::string CreateResourceRecordKey(int64 version_id, 116 int64 resource_id) { 117 return CreateResourceRecordKeyPrefix(version_id).append( 118 base::Int64ToString(resource_id)); 119 } 120 121 std::string CreateUniqueOriginKey(const GURL& origin) { 122 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str()); 123 } 124 125 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) { 126 return base::StringPrintf( 127 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str()); 128 } 129 130 void PutRegistrationDataToBatch( 131 const ServiceWorkerDatabase::RegistrationData& input, 132 leveldb::WriteBatch* batch) { 133 DCHECK(batch); 134 135 // Convert RegistrationData to ServiceWorkerRegistrationData. 136 ServiceWorkerRegistrationData data; 137 data.set_registration_id(input.registration_id); 138 data.set_scope_url(input.scope.spec()); 139 data.set_script_url(input.script.spec()); 140 data.set_version_id(input.version_id); 141 data.set_is_active(input.is_active); 142 data.set_has_fetch_handler(input.has_fetch_handler); 143 data.set_last_update_check_time(input.last_update_check.ToInternalValue()); 144 145 std::string value; 146 bool success = data.SerializeToString(&value); 147 DCHECK(success); 148 GURL origin = input.scope.GetOrigin(); 149 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value); 150 } 151 152 void PutResourceRecordToBatch( 153 const ServiceWorkerDatabase::ResourceRecord& input, 154 int64 version_id, 155 leveldb::WriteBatch* batch) { 156 DCHECK(batch); 157 158 // Convert ResourceRecord to ServiceWorkerResourceRecord. 159 ServiceWorkerResourceRecord record; 160 record.set_resource_id(input.resource_id); 161 record.set_url(input.url.spec()); 162 163 std::string value; 164 bool success = record.SerializeToString(&value); 165 DCHECK(success); 166 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value); 167 } 168 169 void PutUniqueOriginToBatch(const GURL& origin, 170 leveldb::WriteBatch* batch) { 171 // Value should be empty. 172 batch->Put(CreateUniqueOriginKey(origin), ""); 173 } 174 175 void PutPurgeableResourceIdToBatch(int64 resource_id, 176 leveldb::WriteBatch* batch) { 177 // Value should be empty. 178 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); 179 } 180 181 ServiceWorkerDatabase::Status ParseId( 182 const std::string& serialized, 183 int64* out) { 184 DCHECK(out); 185 int64 id; 186 if (!base::StringToInt64(serialized, &id) || id < 0) 187 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 188 *out = id; 189 return ServiceWorkerDatabase::STATUS_OK; 190 } 191 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) { 215 DCHECK(out); 216 ServiceWorkerRegistrationData data; 217 if (!data.ParseFromString(serialized)) 218 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 219 220 GURL scope_url(data.scope_url()); 221 GURL script_url(data.script_url()); 222 if (!scope_url.is_valid() || 223 !script_url.is_valid() || 224 scope_url.GetOrigin() != script_url.GetOrigin()) { 225 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 226 } 227 228 // Convert ServiceWorkerRegistrationData to RegistrationData. 229 out->registration_id = data.registration_id(); 230 out->scope = scope_url; 231 out->script = script_url; 232 out->version_id = data.version_id(); 233 out->is_active = data.is_active(); 234 out->has_fetch_handler = data.has_fetch_handler(); 235 out->last_update_check = 236 base::Time::FromInternalValue(data.last_update_check_time()); 237 return ServiceWorkerDatabase::STATUS_OK; 238 } 239 240 ServiceWorkerDatabase::Status ParseResourceRecord( 241 const std::string& serialized, 242 ServiceWorkerDatabase::ResourceRecord* out) { 243 DCHECK(out); 244 ServiceWorkerResourceRecord record; 245 if (!record.ParseFromString(serialized)) 246 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 247 248 GURL url(record.url()); 249 if (!url.is_valid()) 250 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 251 252 // Convert ServiceWorkerResourceRecord to ResourceRecord. 253 out->resource_id = record.resource_id(); 254 out->url = url; 255 return ServiceWorkerDatabase::STATUS_OK; 256 } 257 258 ServiceWorkerDatabase::Status LevelDBStatusToStatus( 259 const leveldb::Status& status) { 260 if (status.ok()) 261 return ServiceWorkerDatabase::STATUS_OK; 262 else if (status.IsNotFound()) 263 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; 264 else if (status.IsIOError()) 265 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR; 266 else if (status.IsCorruption()) 267 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; 268 else 269 return ServiceWorkerDatabase::STATUS_ERROR_FAILED; 270 } 271 272 const char* StatusToString(ServiceWorkerDatabase::Status status) { 273 switch (status) { 274 case ServiceWorkerDatabase::STATUS_OK: 275 return "Database OK"; 276 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: 277 return "Database not found"; 278 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR: 279 return "Database IO error"; 280 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED: 281 return "Database corrupted"; 282 case ServiceWorkerDatabase::STATUS_ERROR_FAILED: 283 return "Database operation failed"; 284 case ServiceWorkerDatabase::STATUS_ERROR_MAX: 285 NOTREACHED(); 286 return "Database unknown error"; 287 } 288 NOTREACHED(); 289 return "Database unknown error"; 290 } 291 292 } // namespace 293 294 ServiceWorkerDatabase::RegistrationData::RegistrationData() 295 : registration_id(kInvalidServiceWorkerRegistrationId), 296 version_id(kInvalidServiceWorkerVersionId), 297 is_active(false), 298 has_fetch_handler(false) { 299 } 300 301 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { 302 } 303 304 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) 305 : path_(path), 306 next_avail_registration_id_(0), 307 next_avail_resource_id_(0), 308 next_avail_version_id_(0), 309 state_(UNINITIALIZED) { 310 sequence_checker_.DetachFromSequence(); 311 } 312 313 ServiceWorkerDatabase::~ServiceWorkerDatabase() { 314 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 315 db_.reset(); 316 } 317 318 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetNextAvailableIds( 319 int64* next_avail_registration_id, 320 int64* next_avail_version_id, 321 int64* next_avail_resource_id) { 322 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 323 DCHECK(next_avail_registration_id); 324 DCHECK(next_avail_version_id); 325 DCHECK(next_avail_resource_id); 326 327 Status status = LazyOpen(false); 328 if (IsNewOrNonexistentDatabase(status)) { 329 *next_avail_registration_id = 0; 330 *next_avail_version_id = 0; 331 *next_avail_resource_id = 0; 332 return STATUS_OK; 333 } 334 if (status != STATUS_OK) 335 return status; 336 337 status = ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_); 338 if (status != STATUS_OK) 339 return status; 340 status = ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_); 341 if (status != STATUS_OK) 342 return status; 343 status = ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_); 344 if (status != STATUS_OK) 345 return status; 346 347 *next_avail_registration_id = next_avail_registration_id_; 348 *next_avail_version_id = next_avail_version_id_; 349 *next_avail_resource_id = next_avail_resource_id_; 350 return STATUS_OK; 351 } 352 353 ServiceWorkerDatabase::Status 354 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) { 355 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 356 DCHECK(origins->empty()); 357 358 Status status = LazyOpen(false); 359 if (IsNewOrNonexistentDatabase(status)) 360 return STATUS_OK; 361 if (status != STATUS_OK) 362 return status; 363 364 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 365 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { 366 status = LevelDBStatusToStatus(itr->status()); 367 if (status != STATUS_OK) { 368 HandleReadResult(FROM_HERE, status); 369 origins->clear(); 370 return status; 371 } 372 373 std::string origin; 374 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) 375 break; 376 origins->insert(GURL(origin)); 377 } 378 379 HandleReadResult(FROM_HERE, status); 380 return status; 381 } 382 383 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( 384 const GURL& origin, 385 std::vector<RegistrationData>* registrations) { 386 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 387 DCHECK(registrations->empty()); 388 389 Status status = LazyOpen(false); 390 if (IsNewOrNonexistentDatabase(status)) 391 return STATUS_OK; 392 if (status != STATUS_OK) 393 return status; 394 395 // Create a key prefix for registrations. 396 std::string prefix = base::StringPrintf( 397 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); 398 399 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 400 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 401 status = LevelDBStatusToStatus(itr->status()); 402 if (status != STATUS_OK) { 403 HandleReadResult(FROM_HERE, status); 404 registrations->clear(); 405 return status; 406 } 407 408 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) 409 break; 410 411 RegistrationData registration; 412 status = ParseRegistrationData(itr->value().ToString(), ®istration); 413 if (status != STATUS_OK) { 414 HandleReadResult(FROM_HERE, status); 415 registrations->clear(); 416 return status; 417 } 418 registrations->push_back(registration); 419 } 420 421 HandleReadResult(FROM_HERE, status); 422 return status; 423 } 424 425 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( 426 std::vector<RegistrationData>* registrations) { 427 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 428 DCHECK(registrations->empty()); 429 430 Status status = LazyOpen(false); 431 if (IsNewOrNonexistentDatabase(status)) 432 return STATUS_OK; 433 if (status != STATUS_OK) 434 return status; 435 436 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 437 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { 438 status = LevelDBStatusToStatus(itr->status()); 439 if (status != STATUS_OK) { 440 HandleReadResult(FROM_HERE, status); 441 registrations->clear(); 442 return status; 443 } 444 445 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) 446 break; 447 448 RegistrationData registration; 449 status = ParseRegistrationData(itr->value().ToString(), ®istration); 450 if (status != STATUS_OK) { 451 HandleReadResult(FROM_HERE, status); 452 registrations->clear(); 453 return status; 454 } 455 registrations->push_back(registration); 456 } 457 458 HandleReadResult(FROM_HERE, status); 459 return status; 460 } 461 462 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( 463 int64 registration_id, 464 const GURL& origin, 465 RegistrationData* registration, 466 std::vector<ResourceRecord>* resources) { 467 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 468 DCHECK(registration); 469 DCHECK(resources); 470 471 Status status = LazyOpen(false); 472 if (IsNewOrNonexistentDatabase(status) || status != STATUS_OK) 473 return status; 474 475 RegistrationData value; 476 status = ReadRegistrationData(registration_id, origin, &value); 477 if (status != STATUS_OK) 478 return status; 479 480 status = ReadResourceRecords(value.version_id, resources); 481 if (status != STATUS_OK) 482 return status; 483 484 *registration = value; 485 return STATUS_OK; 486 } 487 488 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration( 489 const RegistrationData& registration, 490 const std::vector<ResourceRecord>& resources, 491 std::vector<int64>* newly_purgeable_resources) { 492 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 493 Status status = LazyOpen(true); 494 if (status != STATUS_OK) 495 return status; 496 497 leveldb::WriteBatch batch; 498 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch); 499 BumpNextVersionIdIfNeeded(registration.version_id, &batch); 500 501 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch); 502 PutRegistrationDataToBatch(registration, &batch); 503 504 // Used for avoiding multiple writes for the same resource id or url. 505 std::set<int64> pushed_resources; 506 std::set<GURL> pushed_urls; 507 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin(); 508 itr != resources.end(); ++itr) { 509 if (!itr->url.is_valid()) 510 return STATUS_ERROR_FAILED; 511 512 // Duplicated resource id or url should not exist. 513 DCHECK(pushed_resources.insert(itr->resource_id).second); 514 DCHECK(pushed_urls.insert(itr->url).second); 515 516 PutResourceRecordToBatch(*itr, registration.version_id, &batch); 517 518 // Delete a resource from the uncommitted list. 519 batch.Delete(CreateResourceIdKey( 520 kUncommittedResIdKeyPrefix, itr->resource_id)); 521 } 522 523 // Retrieve a previous version to sweep purgeable resources. 524 RegistrationData old_registration; 525 status = ReadRegistrationData(registration.registration_id, 526 registration.scope.GetOrigin(), 527 &old_registration); 528 if (status != STATUS_OK && status != STATUS_ERROR_NOT_FOUND) 529 return status; 530 if (status == STATUS_OK) { 531 DCHECK_LT(old_registration.version_id, registration.version_id); 532 status = DeleteResourceRecords( 533 old_registration.version_id, newly_purgeable_resources, &batch); 534 if (status != STATUS_OK) 535 return status; 536 537 // Currently resource sharing across versions and registrations is not 538 // supported, so resource ids should not be overlapped between 539 // |registration| and |old_registration|. 540 std::set<int64> deleted_resources(newly_purgeable_resources->begin(), 541 newly_purgeable_resources->end()); 542 DCHECK(base::STLSetIntersection<std::set<int64> >( 543 pushed_resources, deleted_resources).empty()); 544 } 545 546 return WriteBatch(&batch); 547 } 548 549 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateVersionToActive( 550 int64 registration_id, 551 const GURL& origin) { 552 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 553 ServiceWorkerDatabase::Status status = LazyOpen(false); 554 if (IsNewOrNonexistentDatabase(status)) 555 return STATUS_ERROR_NOT_FOUND; 556 if (status != STATUS_OK) 557 return status; 558 if (!origin.is_valid()) 559 return STATUS_ERROR_FAILED; 560 561 RegistrationData registration; 562 status = ReadRegistrationData(registration_id, origin, ®istration); 563 if (status != STATUS_OK) 564 return status; 565 566 registration.is_active = true; 567 568 leveldb::WriteBatch batch; 569 PutRegistrationDataToBatch(registration, &batch); 570 return WriteBatch(&batch); 571 } 572 573 ServiceWorkerDatabase::Status ServiceWorkerDatabase::UpdateLastCheckTime( 574 int64 registration_id, 575 const GURL& origin, 576 const base::Time& time) { 577 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 578 ServiceWorkerDatabase::Status status = LazyOpen(false); 579 if (IsNewOrNonexistentDatabase(status)) 580 return STATUS_ERROR_NOT_FOUND; 581 if (status != STATUS_OK) 582 return status; 583 if (!origin.is_valid()) 584 return STATUS_ERROR_FAILED; 585 586 RegistrationData registration; 587 status = ReadRegistrationData(registration_id, origin, ®istration); 588 if (status != STATUS_OK) 589 return status; 590 591 registration.last_update_check = time; 592 593 leveldb::WriteBatch batch; 594 PutRegistrationDataToBatch(registration, &batch); 595 return WriteBatch(&batch); 596 } 597 598 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteRegistration( 599 int64 registration_id, 600 const GURL& origin, 601 std::vector<int64>* newly_purgeable_resources) { 602 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 603 Status status = LazyOpen(false); 604 if (IsNewOrNonexistentDatabase(status)) 605 return STATUS_OK; 606 if (status != STATUS_OK) 607 return status; 608 if (!origin.is_valid()) 609 return STATUS_ERROR_FAILED; 610 611 leveldb::WriteBatch batch; 612 613 // Remove |origin| from unique origins if a registration specified by 614 // |registration_id| is the only one for |origin|. 615 // TODO(nhiroki): Check the uniqueness by more efficient way. 616 std::vector<RegistrationData> registrations; 617 status = GetRegistrationsForOrigin(origin, ®istrations); 618 if (status != STATUS_OK) 619 return status; 620 621 if (registrations.size() == 1 && 622 registrations[0].registration_id == registration_id) { 623 batch.Delete(CreateUniqueOriginKey(origin)); 624 } 625 626 // Delete a registration specified by |registration_id|. 627 batch.Delete(CreateRegistrationKey(registration_id, origin)); 628 629 // Delete resource records associated with the registration. 630 for (std::vector<RegistrationData>::const_iterator itr = 631 registrations.begin(); itr != registrations.end(); ++itr) { 632 if (itr->registration_id == registration_id) { 633 status = DeleteResourceRecords( 634 itr->version_id, newly_purgeable_resources, &batch); 635 if (status != STATUS_OK) 636 return status; 637 break; 638 } 639 } 640 641 return WriteBatch(&batch); 642 } 643 644 ServiceWorkerDatabase::Status 645 ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) { 646 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids); 647 } 648 649 ServiceWorkerDatabase::Status 650 ServiceWorkerDatabase::WriteUncommittedResourceIds(const std::set<int64>& ids) { 651 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids); 652 } 653 654 ServiceWorkerDatabase::Status 655 ServiceWorkerDatabase::ClearUncommittedResourceIds(const std::set<int64>& ids) { 656 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids); 657 } 658 659 ServiceWorkerDatabase::Status 660 ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) { 661 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids); 662 } 663 664 ServiceWorkerDatabase::Status 665 ServiceWorkerDatabase::WritePurgeableResourceIds(const std::set<int64>& ids) { 666 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids); 667 } 668 669 ServiceWorkerDatabase::Status 670 ServiceWorkerDatabase::ClearPurgeableResourceIds(const std::set<int64>& ids) { 671 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids); 672 } 673 674 ServiceWorkerDatabase::Status 675 ServiceWorkerDatabase::PurgeUncommittedResourceIds( 676 const std::set<int64>& ids) { 677 leveldb::WriteBatch batch; 678 Status status = DeleteResourceIdsInBatch( 679 kUncommittedResIdKeyPrefix, ids, &batch); 680 if (status != STATUS_OK) 681 return status; 682 status = WriteResourceIdsInBatch(kPurgeableResIdKeyPrefix, ids, &batch); 683 if (status != STATUS_OK) 684 return status; 685 return WriteBatch(&batch); 686 } 687 688 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteAllDataForOrigin( 689 const GURL& origin, 690 std::vector<int64>* newly_purgeable_resources) { 691 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 692 Status status = LazyOpen(false); 693 if (IsNewOrNonexistentDatabase(status)) 694 return STATUS_OK; 695 if (status != STATUS_OK) 696 return status; 697 if (!origin.is_valid()) 698 return STATUS_ERROR_FAILED; 699 700 leveldb::WriteBatch batch; 701 702 // Delete from the unique origin list. 703 batch.Delete(CreateUniqueOriginKey(origin)); 704 705 std::vector<RegistrationData> registrations; 706 status = GetRegistrationsForOrigin(origin, ®istrations); 707 if (status != STATUS_OK) 708 return status; 709 710 // Delete registrations and resource records. 711 for (std::vector<RegistrationData>::const_iterator itr = 712 registrations.begin(); itr != registrations.end(); ++itr) { 713 batch.Delete(CreateRegistrationKey(itr->registration_id, origin)); 714 status = DeleteResourceRecords( 715 itr->version_id, newly_purgeable_resources, &batch); 716 if (status != STATUS_OK) 717 return status; 718 } 719 720 return WriteBatch(&batch); 721 } 722 723 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() { 724 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 725 Disable(FROM_HERE, STATUS_OK); 726 return LevelDBStatusToStatus( 727 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options())); 728 } 729 730 ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen( 731 bool create_if_missing) { 732 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 733 734 // Do not try to open a database if we tried and failed once. 735 if (state_ == DISABLED) 736 return STATUS_ERROR_FAILED; 737 if (IsOpen()) 738 return STATUS_OK; 739 740 // When |path_| is empty, open a database in-memory. 741 bool use_in_memory_db = path_.empty(); 742 743 if (!create_if_missing) { 744 // Avoid opening a database if it does not exist at the |path_|. 745 if (use_in_memory_db || 746 !base::PathExists(path_) || 747 base::IsDirectoryEmpty(path_)) { 748 return STATUS_ERROR_NOT_FOUND; 749 } 750 } 751 752 leveldb::Options options; 753 options.create_if_missing = create_if_missing; 754 if (use_in_memory_db) { 755 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); 756 options.env = env_.get(); 757 } 758 759 leveldb::DB* db = NULL; 760 Status status = LevelDBStatusToStatus( 761 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db)); 762 HandleOpenResult(FROM_HERE, status); 763 if (status != STATUS_OK) { 764 DCHECK(!db); 765 // TODO(nhiroki): Should we retry to open the database? 766 return status; 767 } 768 db_.reset(db); 769 770 int64 db_version; 771 status = ReadDatabaseVersion(&db_version); 772 if (status != STATUS_OK) 773 return status; 774 DCHECK_LE(0, db_version); 775 if (db_version > 0) 776 state_ = INITIALIZED; 777 return STATUS_OK; 778 } 779 780 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase( 781 ServiceWorkerDatabase::Status status) { 782 if (status == STATUS_ERROR_NOT_FOUND) 783 return true; 784 if (status == STATUS_OK && state_ == UNINITIALIZED) 785 return true; 786 return false; 787 } 788 789 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( 790 const char* id_key, 791 int64* next_avail_id) { 792 DCHECK(id_key); 793 DCHECK(next_avail_id); 794 795 std::string value; 796 Status status = LevelDBStatusToStatus( 797 db_->Get(leveldb::ReadOptions(), id_key, &value)); 798 if (status == STATUS_ERROR_NOT_FOUND) { 799 // Nobody has gotten the next resource id for |id_key|. 800 *next_avail_id = 0; 801 HandleReadResult(FROM_HERE, STATUS_OK); 802 return STATUS_OK; 803 } else if (status != STATUS_OK) { 804 HandleReadResult(FROM_HERE, status); 805 return status; 806 } 807 808 status = ParseId(value, next_avail_id); 809 HandleReadResult(FROM_HERE, status); 810 return status; 811 } 812 813 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( 814 int64 registration_id, 815 const GURL& origin, 816 RegistrationData* registration) { 817 DCHECK(registration); 818 819 const std::string key = CreateRegistrationKey(registration_id, origin); 820 std::string value; 821 Status status = LevelDBStatusToStatus( 822 db_->Get(leveldb::ReadOptions(), key, &value)); 823 if (status != STATUS_OK) { 824 HandleReadResult( 825 FROM_HERE, 826 status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status); 827 return status; 828 } 829 830 status = ParseRegistrationData(value, registration); 831 HandleReadResult(FROM_HERE, status); 832 return status; 833 } 834 835 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( 836 int64 version_id, 837 std::vector<ResourceRecord>* resources) { 838 DCHECK(resources->empty()); 839 840 Status status = STATUS_OK; 841 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); 842 843 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 844 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 845 Status status = LevelDBStatusToStatus(itr->status()); 846 if (status != STATUS_OK) { 847 HandleReadResult(FROM_HERE, status); 848 resources->clear(); 849 return status; 850 } 851 852 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) 853 break; 854 855 ResourceRecord resource; 856 status = ParseResourceRecord(itr->value().ToString(), &resource); 857 if (status != STATUS_OK) { 858 HandleReadResult(FROM_HERE, status); 859 resources->clear(); 860 return status; 861 } 862 resources->push_back(resource); 863 } 864 865 HandleReadResult(FROM_HERE, status); 866 return status; 867 } 868 869 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( 870 int64 version_id, 871 std::vector<int64>* newly_purgeable_resources, 872 leveldb::WriteBatch* batch) { 873 DCHECK(batch); 874 875 Status status = STATUS_OK; 876 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); 877 878 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 879 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 880 status = LevelDBStatusToStatus(itr->status()); 881 if (status != STATUS_OK) { 882 HandleReadResult(FROM_HERE, status); 883 return status; 884 } 885 886 const std::string key = itr->key().ToString(); 887 std::string unprefixed; 888 if (!RemovePrefix(key, prefix, &unprefixed)) 889 break; 890 891 int64 resource_id; 892 status = ParseId(unprefixed, &resource_id); 893 if (status != STATUS_OK) { 894 HandleReadResult(FROM_HERE, status); 895 return status; 896 } 897 898 // Remove a resource record. 899 batch->Delete(key); 900 901 // Currently resource sharing across versions and registrations is not 902 // supported, so we can purge this without caring about it. 903 PutPurgeableResourceIdToBatch(resource_id, batch); 904 newly_purgeable_resources->push_back(resource_id); 905 } 906 907 HandleReadResult(FROM_HERE, status); 908 return status; 909 } 910 911 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( 912 const char* id_key_prefix, 913 std::set<int64>* ids) { 914 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 915 DCHECK(id_key_prefix); 916 DCHECK(ids->empty()); 917 918 Status status = LazyOpen(false); 919 if (IsNewOrNonexistentDatabase(status)) 920 return STATUS_OK; 921 if (status != STATUS_OK) 922 return status; 923 924 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 925 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { 926 status = LevelDBStatusToStatus(itr->status()); 927 if (status != STATUS_OK) { 928 HandleReadResult(FROM_HERE, status); 929 ids->clear(); 930 return status; 931 } 932 933 std::string unprefixed; 934 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) 935 break; 936 937 int64 resource_id; 938 status = ParseId(unprefixed, &resource_id); 939 if (status != STATUS_OK) { 940 HandleReadResult(FROM_HERE, status); 941 ids->clear(); 942 return status; 943 } 944 ids->insert(resource_id); 945 } 946 947 HandleReadResult(FROM_HERE, status); 948 return status; 949 } 950 951 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( 952 const char* id_key_prefix, 953 const std::set<int64>& ids) { 954 leveldb::WriteBatch batch; 955 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); 956 if (status != STATUS_OK) 957 return status; 958 return WriteBatch(&batch); 959 } 960 961 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIdsInBatch( 962 const char* id_key_prefix, 963 const std::set<int64>& ids, 964 leveldb::WriteBatch* batch) { 965 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 966 DCHECK(id_key_prefix); 967 968 Status status = LazyOpen(true); 969 if (status != STATUS_OK) 970 return status; 971 972 for (std::set<int64>::const_iterator itr = ids.begin(); 973 itr != ids.end(); ++itr) { 974 // Value should be empty. 975 batch->Put(CreateResourceIdKey(id_key_prefix, *itr), ""); 976 } 977 return STATUS_OK; 978 } 979 980 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIds( 981 const char* id_key_prefix, 982 const std::set<int64>& ids) { 983 leveldb::WriteBatch batch; 984 Status status = DeleteResourceIdsInBatch(id_key_prefix, ids, &batch); 985 if (status != STATUS_OK) 986 return status; 987 return WriteBatch(&batch); 988 } 989 990 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch( 991 const char* id_key_prefix, 992 const std::set<int64>& ids, 993 leveldb::WriteBatch* batch) { 994 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 995 DCHECK(id_key_prefix); 996 997 Status status = LazyOpen(false); 998 if (IsNewOrNonexistentDatabase(status)) 999 return STATUS_OK; 1000 if (status != STATUS_OK) 1001 return status; 1002 1003 for (std::set<int64>::const_iterator itr = ids.begin(); 1004 itr != ids.end(); ++itr) { 1005 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr)); 1006 } 1007 return STATUS_OK; 1008 } 1009 1010 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( 1011 int64* db_version) { 1012 std::string value; 1013 Status status = LevelDBStatusToStatus( 1014 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value)); 1015 if (status == STATUS_ERROR_NOT_FOUND) { 1016 // The database hasn't been initialized yet. 1017 *db_version = 0; 1018 HandleReadResult(FROM_HERE, STATUS_OK); 1019 return STATUS_OK; 1020 } 1021 1022 if (status != STATUS_OK) { 1023 HandleReadResult(FROM_HERE, status); 1024 return status; 1025 } 1026 1027 status = ParseDatabaseVersion(value, db_version); 1028 HandleReadResult(FROM_HERE, status); 1029 return status; 1030 } 1031 1032 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( 1033 leveldb::WriteBatch* batch) { 1034 DCHECK(batch); 1035 DCHECK_NE(DISABLED, state_); 1036 1037 if (state_ == UNINITIALIZED) { 1038 // Write the database schema version. 1039 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); 1040 state_ = INITIALIZED; 1041 } 1042 1043 Status status = LevelDBStatusToStatus( 1044 db_->Write(leveldb::WriteOptions(), batch)); 1045 HandleWriteResult(FROM_HERE, status); 1046 return status; 1047 } 1048 1049 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( 1050 int64 used_id, leveldb::WriteBatch* batch) { 1051 DCHECK(batch); 1052 if (next_avail_registration_id_ <= used_id) { 1053 next_avail_registration_id_ = used_id + 1; 1054 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); 1055 } 1056 } 1057 1058 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( 1059 int64 used_id, leveldb::WriteBatch* batch) { 1060 DCHECK(batch); 1061 if (next_avail_version_id_ <= used_id) { 1062 next_avail_version_id_ = used_id + 1; 1063 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); 1064 } 1065 } 1066 1067 bool ServiceWorkerDatabase::IsOpen() { 1068 return db_ != NULL; 1069 } 1070 1071 void ServiceWorkerDatabase::Disable( 1072 const tracked_objects::Location& from_here, 1073 Status status) { 1074 if (status != STATUS_OK) { 1075 DLOG(ERROR) << "Failed at: " << from_here.ToString() 1076 << " with error: " << StatusToString(status); 1077 DLOG(ERROR) << "ServiceWorkerDatabase is disabled."; 1078 } 1079 state_ = DISABLED; 1080 db_.reset(); 1081 } 1082 1083 void ServiceWorkerDatabase::HandleOpenResult( 1084 const tracked_objects::Location& from_here, 1085 Status status) { 1086 if (status != ServiceWorkerDatabase::STATUS_OK) 1087 Disable(from_here, status); 1088 UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel, 1089 status, 1090 ServiceWorkerDatabase::STATUS_ERROR_MAX); 1091 } 1092 1093 void ServiceWorkerDatabase::HandleReadResult( 1094 const tracked_objects::Location& from_here, 1095 Status status) { 1096 if (status != ServiceWorkerDatabase::STATUS_OK) 1097 Disable(from_here, status); 1098 UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel, 1099 status, 1100 ServiceWorkerDatabase::STATUS_ERROR_MAX); 1101 } 1102 1103 void ServiceWorkerDatabase::HandleWriteResult( 1104 const tracked_objects::Location& from_here, 1105 Status status) { 1106 if (status != ServiceWorkerDatabase::STATUS_OK) 1107 Disable(from_here, status); 1108 UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel, 1109 status, 1110 ServiceWorkerDatabase::STATUS_ERROR_MAX); 1111 } 1112 1113 } // namespace content 1114