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