1 // Copyright (c) 2012 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 "webkit/browser/appcache/appcache_storage_impl.h" 6 7 #include <algorithm> 8 #include <functional> 9 #include <set> 10 #include <vector> 11 12 #include "base/bind.h" 13 #include "base/bind_helpers.h" 14 #include "base/file_util.h" 15 #include "base/logging.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/stl_util.h" 18 #include "base/strings/string_util.h" 19 #include "net/base/cache_type.h" 20 #include "net/base/net_errors.h" 21 #include "sql/connection.h" 22 #include "sql/transaction.h" 23 #include "webkit/browser/appcache/appcache.h" 24 #include "webkit/browser/appcache/appcache_database.h" 25 #include "webkit/browser/appcache/appcache_entry.h" 26 #include "webkit/browser/appcache/appcache_group.h" 27 #include "webkit/browser/appcache/appcache_histograms.h" 28 #include "webkit/browser/appcache/appcache_quota_client.h" 29 #include "webkit/browser/appcache/appcache_response.h" 30 #include "webkit/browser/appcache/appcache_service_impl.h" 31 #include "webkit/browser/quota/quota_client.h" 32 #include "webkit/browser/quota/quota_manager.h" 33 #include "webkit/browser/quota/quota_manager_proxy.h" 34 #include "webkit/browser/quota/special_storage_policy.h" 35 36 namespace appcache { 37 38 // Hard coded default when not using quota management. 39 static const int kDefaultQuota = 5 * 1024 * 1024; 40 41 static const int kMaxDiskCacheSize = 250 * 1024 * 1024; 42 static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024; 43 static const base::FilePath::CharType kDiskCacheDirectoryName[] = 44 FILE_PATH_LITERAL("Cache"); 45 46 namespace { 47 48 // Helpers for clearing data from the AppCacheDatabase. 49 bool DeleteGroupAndRelatedRecords(AppCacheDatabase* database, 50 int64 group_id, 51 std::vector<int64>* deletable_response_ids) { 52 AppCacheDatabase::CacheRecord cache_record; 53 bool success = false; 54 if (database->FindCacheForGroup(group_id, &cache_record)) { 55 database->FindResponseIdsForCacheAsVector(cache_record.cache_id, 56 deletable_response_ids); 57 success = 58 database->DeleteGroup(group_id) && 59 database->DeleteCache(cache_record.cache_id) && 60 database->DeleteEntriesForCache(cache_record.cache_id) && 61 database->DeleteNamespacesForCache(cache_record.cache_id) && 62 database->DeleteOnlineWhiteListForCache(cache_record.cache_id) && 63 database->InsertDeletableResponseIds(*deletable_response_ids); 64 } else { 65 NOTREACHED() << "A existing group without a cache is unexpected"; 66 success = database->DeleteGroup(group_id); 67 } 68 return success; 69 } 70 71 // Destroys |database|. If there is appcache data to be deleted 72 // (|force_keep_session_state| is false), deletes session-only appcache data. 73 void ClearSessionOnlyOrigins( 74 AppCacheDatabase* database, 75 scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy, 76 bool force_keep_session_state) { 77 scoped_ptr<AppCacheDatabase> database_to_delete(database); 78 79 // If saving session state, only delete the database. 80 if (force_keep_session_state) 81 return; 82 83 bool has_session_only_appcaches = 84 special_storage_policy.get() && 85 special_storage_policy->HasSessionOnlyOrigins(); 86 87 // Clearning only session-only databases, and there are none. 88 if (!has_session_only_appcaches) 89 return; 90 91 std::set<GURL> origins; 92 database->FindOriginsWithGroups(&origins); 93 if (origins.empty()) 94 return; // nothing to delete 95 96 sql::Connection* connection = database->db_connection(); 97 if (!connection) { 98 NOTREACHED() << "Missing database connection."; 99 return; 100 } 101 102 std::set<GURL>::const_iterator origin; 103 for (origin = origins.begin(); origin != origins.end(); ++origin) { 104 if (!special_storage_policy->IsStorageSessionOnly(*origin)) 105 continue; 106 if (special_storage_policy.get() && 107 special_storage_policy->IsStorageProtected(*origin)) 108 continue; 109 110 std::vector<AppCacheDatabase::GroupRecord> groups; 111 database->FindGroupsForOrigin(*origin, &groups); 112 std::vector<AppCacheDatabase::GroupRecord>::const_iterator group; 113 for (group = groups.begin(); group != groups.end(); ++group) { 114 sql::Transaction transaction(connection); 115 if (!transaction.Begin()) { 116 NOTREACHED() << "Failed to start transaction"; 117 return; 118 } 119 std::vector<int64> deletable_response_ids; 120 bool success = DeleteGroupAndRelatedRecords(database, 121 group->group_id, 122 &deletable_response_ids); 123 success = success && transaction.Commit(); 124 DCHECK(success); 125 } // for each group 126 } // for each origin 127 } 128 129 } // namespace 130 131 // DatabaseTask ----------------------------------------- 132 133 class AppCacheStorageImpl::DatabaseTask 134 : public base::RefCountedThreadSafe<DatabaseTask> { 135 public: 136 explicit DatabaseTask(AppCacheStorageImpl* storage) 137 : storage_(storage), database_(storage->database_), 138 io_thread_(base::MessageLoopProxy::current()) { 139 DCHECK(io_thread_.get()); 140 } 141 142 void AddDelegate(DelegateReference* delegate_reference) { 143 delegates_.push_back(make_scoped_refptr(delegate_reference)); 144 } 145 146 // Schedules a task to be Run() on the DB thread. Tasks 147 // are run in the order in which they are scheduled. 148 void Schedule(); 149 150 // Called on the DB thread. 151 virtual void Run() = 0; 152 153 // Called on the IO thread after Run() has completed. 154 virtual void RunCompleted() {} 155 156 // Once scheduled a task cannot be cancelled, but the 157 // call to RunCompleted may be. This method should only be 158 // called on the IO thread. This is used by AppCacheStorageImpl 159 // to cancel the completion calls when AppCacheStorageImpl is 160 // destructed. This method may be overriden to release or delete 161 // additional data associated with the task that is not DB thread 162 // safe. If overriden, this base class method must be called from 163 // within the override. 164 virtual void CancelCompletion(); 165 166 protected: 167 friend class base::RefCountedThreadSafe<DatabaseTask>; 168 virtual ~DatabaseTask() {} 169 170 AppCacheStorageImpl* storage_; 171 AppCacheDatabase* database_; 172 DelegateReferenceVector delegates_; 173 174 private: 175 void CallRun(base::TimeTicks schedule_time); 176 void CallRunCompleted(base::TimeTicks schedule_time); 177 void OnFatalError(); 178 179 scoped_refptr<base::MessageLoopProxy> io_thread_; 180 }; 181 182 void AppCacheStorageImpl::DatabaseTask::Schedule() { 183 DCHECK(storage_); 184 DCHECK(io_thread_->BelongsToCurrentThread()); 185 if (!storage_->database_) 186 return; 187 188 if (storage_->db_thread_->PostTask( 189 FROM_HERE, 190 base::Bind(&DatabaseTask::CallRun, this, base::TimeTicks::Now()))) { 191 storage_->scheduled_database_tasks_.push_back(this); 192 } else { 193 NOTREACHED() << "Thread for database tasks is not running."; 194 } 195 } 196 197 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() { 198 DCHECK(io_thread_->BelongsToCurrentThread()); 199 delegates_.clear(); 200 storage_ = NULL; 201 } 202 203 void AppCacheStorageImpl::DatabaseTask::CallRun( 204 base::TimeTicks schedule_time) { 205 AppCacheHistograms::AddTaskQueueTimeSample( 206 base::TimeTicks::Now() - schedule_time); 207 if (!database_->is_disabled()) { 208 base::TimeTicks run_time = base::TimeTicks::Now(); 209 Run(); 210 AppCacheHistograms::AddTaskRunTimeSample( 211 base::TimeTicks::Now() - run_time); 212 213 if (database_->was_corruption_detected()) { 214 AppCacheHistograms::CountCorruptionDetected(); 215 database_->Disable(); 216 } 217 if (database_->is_disabled()) { 218 io_thread_->PostTask( 219 FROM_HERE, 220 base::Bind(&DatabaseTask::OnFatalError, this)); 221 } 222 } 223 io_thread_->PostTask( 224 FROM_HERE, 225 base::Bind(&DatabaseTask::CallRunCompleted, this, 226 base::TimeTicks::Now())); 227 } 228 229 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted( 230 base::TimeTicks schedule_time) { 231 AppCacheHistograms::AddCompletionQueueTimeSample( 232 base::TimeTicks::Now() - schedule_time); 233 if (storage_) { 234 DCHECK(io_thread_->BelongsToCurrentThread()); 235 DCHECK(storage_->scheduled_database_tasks_.front() == this); 236 storage_->scheduled_database_tasks_.pop_front(); 237 base::TimeTicks run_time = base::TimeTicks::Now(); 238 RunCompleted(); 239 AppCacheHistograms::AddCompletionRunTimeSample( 240 base::TimeTicks::Now() - run_time); 241 delegates_.clear(); 242 } 243 } 244 245 void AppCacheStorageImpl::DatabaseTask::OnFatalError() { 246 if (storage_) { 247 DCHECK(io_thread_->BelongsToCurrentThread()); 248 storage_->Disable(); 249 storage_->DeleteAndStartOver(); 250 } 251 } 252 253 // InitTask ------- 254 255 class AppCacheStorageImpl::InitTask : public DatabaseTask { 256 public: 257 explicit InitTask(AppCacheStorageImpl* storage) 258 : DatabaseTask(storage), last_group_id_(0), 259 last_cache_id_(0), last_response_id_(0), 260 last_deletable_response_rowid_(0) { 261 if (!storage->is_incognito_) { 262 db_file_path_ = 263 storage->cache_directory_.Append(kAppCacheDatabaseName); 264 disk_cache_directory_ = 265 storage->cache_directory_.Append(kDiskCacheDirectoryName); 266 } 267 } 268 269 // DatabaseTask: 270 virtual void Run() OVERRIDE; 271 virtual void RunCompleted() OVERRIDE; 272 273 protected: 274 virtual ~InitTask() {} 275 276 private: 277 base::FilePath db_file_path_; 278 base::FilePath disk_cache_directory_; 279 int64 last_group_id_; 280 int64 last_cache_id_; 281 int64 last_response_id_; 282 int64 last_deletable_response_rowid_; 283 std::map<GURL, int64> usage_map_; 284 }; 285 286 void AppCacheStorageImpl::InitTask::Run() { 287 // If there is no sql database, ensure there is no disk cache either. 288 if (!db_file_path_.empty() && 289 !base::PathExists(db_file_path_) && 290 base::DirectoryExists(disk_cache_directory_)) { 291 base::DeleteFile(disk_cache_directory_, true); 292 if (base::DirectoryExists(disk_cache_directory_)) { 293 database_->Disable(); // This triggers OnFatalError handling. 294 return; 295 } 296 } 297 298 database_->FindLastStorageIds( 299 &last_group_id_, &last_cache_id_, &last_response_id_, 300 &last_deletable_response_rowid_); 301 database_->GetAllOriginUsage(&usage_map_); 302 } 303 304 void AppCacheStorageImpl::InitTask::RunCompleted() { 305 storage_->last_group_id_ = last_group_id_; 306 storage_->last_cache_id_ = last_cache_id_; 307 storage_->last_response_id_ = last_response_id_; 308 storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_; 309 310 if (!storage_->is_disabled()) { 311 storage_->usage_map_.swap(usage_map_); 312 const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5); 313 base::MessageLoop::current()->PostDelayedTask( 314 FROM_HERE, 315 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses, 316 storage_->weak_factory_.GetWeakPtr()), 317 kDelay); 318 } 319 320 if (storage_->service()->quota_client()) 321 storage_->service()->quota_client()->NotifyAppCacheReady(); 322 } 323 324 // DisableDatabaseTask ------- 325 326 class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask { 327 public: 328 explicit DisableDatabaseTask(AppCacheStorageImpl* storage) 329 : DatabaseTask(storage) {} 330 331 // DatabaseTask: 332 virtual void Run() OVERRIDE { database_->Disable(); } 333 334 protected: 335 virtual ~DisableDatabaseTask() {} 336 }; 337 338 // GetAllInfoTask ------- 339 340 class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask { 341 public: 342 explicit GetAllInfoTask(AppCacheStorageImpl* storage) 343 : DatabaseTask(storage), 344 info_collection_(new AppCacheInfoCollection()) { 345 } 346 347 // DatabaseTask: 348 virtual void Run() OVERRIDE; 349 virtual void RunCompleted() OVERRIDE; 350 351 protected: 352 virtual ~GetAllInfoTask() {} 353 354 private: 355 scoped_refptr<AppCacheInfoCollection> info_collection_; 356 }; 357 358 void AppCacheStorageImpl::GetAllInfoTask::Run() { 359 std::set<GURL> origins; 360 database_->FindOriginsWithGroups(&origins); 361 for (std::set<GURL>::const_iterator origin = origins.begin(); 362 origin != origins.end(); ++origin) { 363 AppCacheInfoVector& infos = 364 info_collection_->infos_by_origin[*origin]; 365 std::vector<AppCacheDatabase::GroupRecord> groups; 366 database_->FindGroupsForOrigin(*origin, &groups); 367 for (std::vector<AppCacheDatabase::GroupRecord>::const_iterator 368 group = groups.begin(); 369 group != groups.end(); ++group) { 370 AppCacheDatabase::CacheRecord cache_record; 371 database_->FindCacheForGroup(group->group_id, &cache_record); 372 AppCacheInfo info; 373 info.manifest_url = group->manifest_url; 374 info.creation_time = group->creation_time; 375 info.size = cache_record.cache_size; 376 info.last_access_time = group->last_access_time; 377 info.last_update_time = cache_record.update_time; 378 info.cache_id = cache_record.cache_id; 379 info.group_id = group->group_id; 380 info.is_complete = true; 381 infos.push_back(info); 382 } 383 } 384 } 385 386 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() { 387 DCHECK(delegates_.size() == 1); 388 FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get())); 389 } 390 391 // StoreOrLoadTask ------- 392 393 class AppCacheStorageImpl::StoreOrLoadTask : public DatabaseTask { 394 protected: 395 explicit StoreOrLoadTask(AppCacheStorageImpl* storage) 396 : DatabaseTask(storage) {} 397 virtual ~StoreOrLoadTask() {} 398 399 bool FindRelatedCacheRecords(int64 cache_id); 400 void CreateCacheAndGroupFromRecords( 401 scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group); 402 403 AppCacheDatabase::GroupRecord group_record_; 404 AppCacheDatabase::CacheRecord cache_record_; 405 std::vector<AppCacheDatabase::EntryRecord> entry_records_; 406 std::vector<AppCacheDatabase::NamespaceRecord> 407 intercept_namespace_records_; 408 std::vector<AppCacheDatabase::NamespaceRecord> 409 fallback_namespace_records_; 410 std::vector<AppCacheDatabase::OnlineWhiteListRecord> 411 online_whitelist_records_; 412 }; 413 414 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords( 415 int64 cache_id) { 416 return database_->FindEntriesForCache(cache_id, &entry_records_) && 417 database_->FindNamespacesForCache( 418 cache_id, &intercept_namespace_records_, 419 &fallback_namespace_records_) && 420 database_->FindOnlineWhiteListForCache( 421 cache_id, &online_whitelist_records_); 422 } 423 424 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords( 425 scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group) { 426 DCHECK(storage_ && cache && group); 427 428 (*cache) = storage_->working_set_.GetCache(cache_record_.cache_id); 429 if (cache->get()) { 430 (*group) = cache->get()->owning_group(); 431 DCHECK(group->get()); 432 DCHECK_EQ(group_record_.group_id, group->get()->group_id()); 433 434 // TODO(michaeln): histogram is fishing for clues to crbug/95101 435 if (!cache->get()->GetEntry(group_record_.manifest_url)) { 436 AppCacheHistograms::AddMissingManifestDetectedAtCallsite( 437 AppCacheHistograms::CALLSITE_0); 438 } 439 440 storage_->NotifyStorageAccessed(group_record_.origin); 441 return; 442 } 443 444 (*cache) = new AppCache(storage_, cache_record_.cache_id); 445 cache->get()->InitializeWithDatabaseRecords( 446 cache_record_, entry_records_, 447 intercept_namespace_records_, 448 fallback_namespace_records_, 449 online_whitelist_records_); 450 cache->get()->set_complete(true); 451 452 (*group) = storage_->working_set_.GetGroup(group_record_.manifest_url); 453 if (group->get()) { 454 DCHECK(group_record_.group_id == group->get()->group_id()); 455 group->get()->AddCache(cache->get()); 456 457 // TODO(michaeln): histogram is fishing for clues to crbug/95101 458 if (!cache->get()->GetEntry(group_record_.manifest_url)) { 459 AppCacheHistograms::AddMissingManifestDetectedAtCallsite( 460 AppCacheHistograms::CALLSITE_1); 461 } 462 } else { 463 (*group) = new AppCacheGroup( 464 storage_, group_record_.manifest_url, 465 group_record_.group_id); 466 group->get()->set_creation_time(group_record_.creation_time); 467 group->get()->AddCache(cache->get()); 468 469 // TODO(michaeln): histogram is fishing for clues to crbug/95101 470 if (!cache->get()->GetEntry(group_record_.manifest_url)) { 471 AppCacheHistograms::AddMissingManifestDetectedAtCallsite( 472 AppCacheHistograms::CALLSITE_2); 473 } 474 } 475 DCHECK(group->get()->newest_complete_cache() == cache->get()); 476 477 // We have to update foriegn entries if MarkEntryAsForeignTasks 478 // are in flight. 479 std::vector<GURL> urls; 480 storage_->GetPendingForeignMarkingsForCache(cache->get()->cache_id(), &urls); 481 for (std::vector<GURL>::iterator iter = urls.begin(); 482 iter != urls.end(); ++iter) { 483 DCHECK(cache->get()->GetEntry(*iter)); 484 cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN); 485 } 486 487 storage_->NotifyStorageAccessed(group_record_.origin); 488 489 // TODO(michaeln): Maybe verify that the responses we expect to exist 490 // do actually exist in the disk_cache (and if not then what?) 491 } 492 493 // CacheLoadTask ------- 494 495 class AppCacheStorageImpl::CacheLoadTask : public StoreOrLoadTask { 496 public: 497 CacheLoadTask(int64 cache_id, AppCacheStorageImpl* storage) 498 : StoreOrLoadTask(storage), cache_id_(cache_id), 499 success_(false) {} 500 501 // DatabaseTask: 502 virtual void Run() OVERRIDE; 503 virtual void RunCompleted() OVERRIDE; 504 505 protected: 506 virtual ~CacheLoadTask() {} 507 508 private: 509 int64 cache_id_; 510 bool success_; 511 }; 512 513 void AppCacheStorageImpl::CacheLoadTask::Run() { 514 success_ = 515 database_->FindCache(cache_id_, &cache_record_) && 516 database_->FindGroup(cache_record_.group_id, &group_record_) && 517 FindRelatedCacheRecords(cache_id_); 518 519 if (success_) 520 database_->UpdateGroupLastAccessTime(group_record_.group_id, 521 base::Time::Now()); 522 } 523 524 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() { 525 storage_->pending_cache_loads_.erase(cache_id_); 526 scoped_refptr<AppCache> cache; 527 scoped_refptr<AppCacheGroup> group; 528 if (success_ && !storage_->is_disabled()) { 529 DCHECK(cache_record_.cache_id == cache_id_); 530 CreateCacheAndGroupFromRecords(&cache, &group); 531 } 532 FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_)); 533 } 534 535 // GroupLoadTask ------- 536 537 class AppCacheStorageImpl::GroupLoadTask : public StoreOrLoadTask { 538 public: 539 GroupLoadTask(GURL manifest_url, AppCacheStorageImpl* storage) 540 : StoreOrLoadTask(storage), manifest_url_(manifest_url), 541 success_(false) {} 542 543 // DatabaseTask: 544 virtual void Run() OVERRIDE; 545 virtual void RunCompleted() OVERRIDE; 546 547 protected: 548 virtual ~GroupLoadTask() {} 549 550 private: 551 GURL manifest_url_; 552 bool success_; 553 }; 554 555 void AppCacheStorageImpl::GroupLoadTask::Run() { 556 success_ = 557 database_->FindGroupForManifestUrl(manifest_url_, &group_record_) && 558 database_->FindCacheForGroup(group_record_.group_id, &cache_record_) && 559 FindRelatedCacheRecords(cache_record_.cache_id); 560 561 if (success_) 562 database_->UpdateGroupLastAccessTime(group_record_.group_id, 563 base::Time::Now()); 564 } 565 566 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() { 567 storage_->pending_group_loads_.erase(manifest_url_); 568 scoped_refptr<AppCacheGroup> group; 569 scoped_refptr<AppCache> cache; 570 if (!storage_->is_disabled()) { 571 if (success_) { 572 DCHECK(group_record_.manifest_url == manifest_url_); 573 CreateCacheAndGroupFromRecords(&cache, &group); 574 } else { 575 group = storage_->working_set_.GetGroup(manifest_url_); 576 if (!group.get()) { 577 group = 578 new AppCacheGroup(storage_, manifest_url_, storage_->NewGroupId()); 579 } 580 } 581 } 582 FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_)); 583 } 584 585 // StoreGroupAndCacheTask ------- 586 587 class AppCacheStorageImpl::StoreGroupAndCacheTask : public StoreOrLoadTask { 588 public: 589 StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group, 590 AppCache* newest_cache); 591 592 void GetQuotaThenSchedule(); 593 void OnQuotaCallback( 594 quota::QuotaStatusCode status, int64 usage, int64 quota); 595 596 // DatabaseTask: 597 virtual void Run() OVERRIDE; 598 virtual void RunCompleted() OVERRIDE; 599 virtual void CancelCompletion() OVERRIDE; 600 601 protected: 602 virtual ~StoreGroupAndCacheTask() {} 603 604 private: 605 scoped_refptr<AppCacheGroup> group_; 606 scoped_refptr<AppCache> cache_; 607 bool success_; 608 bool would_exceed_quota_; 609 int64 space_available_; 610 int64 new_origin_usage_; 611 std::vector<int64> newly_deletable_response_ids_; 612 }; 613 614 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask( 615 AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache) 616 : StoreOrLoadTask(storage), group_(group), cache_(newest_cache), 617 success_(false), would_exceed_quota_(false), 618 space_available_(-1), new_origin_usage_(-1) { 619 group_record_.group_id = group->group_id(); 620 group_record_.manifest_url = group->manifest_url(); 621 group_record_.origin = group_record_.manifest_url.GetOrigin(); 622 newest_cache->ToDatabaseRecords( 623 group, 624 &cache_record_, &entry_records_, 625 &intercept_namespace_records_, 626 &fallback_namespace_records_, 627 &online_whitelist_records_); 628 } 629 630 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() { 631 quota::QuotaManager* quota_manager = NULL; 632 if (storage_->service()->quota_manager_proxy()) { 633 quota_manager = 634 storage_->service()->quota_manager_proxy()->quota_manager(); 635 } 636 637 if (!quota_manager) { 638 if (storage_->service()->special_storage_policy() && 639 storage_->service()->special_storage_policy()->IsStorageUnlimited( 640 group_record_.origin)) 641 space_available_ = kint64max; 642 Schedule(); 643 return; 644 } 645 646 // We have to ask the quota manager for the value. 647 storage_->pending_quota_queries_.insert(this); 648 quota_manager->GetUsageAndQuota( 649 group_record_.origin, quota::kStorageTypeTemporary, 650 base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback, this)); 651 } 652 653 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback( 654 quota::QuotaStatusCode status, int64 usage, int64 quota) { 655 if (storage_) { 656 if (status == quota::kQuotaStatusOk) 657 space_available_ = std::max(static_cast<int64>(0), quota - usage); 658 else 659 space_available_ = 0; 660 storage_->pending_quota_queries_.erase(this); 661 Schedule(); 662 } 663 } 664 665 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() { 666 DCHECK(!success_); 667 sql::Connection* connection = database_->db_connection(); 668 if (!connection) 669 return; 670 671 sql::Transaction transaction(connection); 672 if (!transaction.Begin()) 673 return; 674 675 int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin); 676 677 AppCacheDatabase::GroupRecord existing_group; 678 success_ = database_->FindGroup(group_record_.group_id, &existing_group); 679 if (!success_) { 680 group_record_.creation_time = base::Time::Now(); 681 group_record_.last_access_time = base::Time::Now(); 682 success_ = database_->InsertGroup(&group_record_); 683 } else { 684 DCHECK(group_record_.group_id == existing_group.group_id); 685 DCHECK(group_record_.manifest_url == existing_group.manifest_url); 686 DCHECK(group_record_.origin == existing_group.origin); 687 688 database_->UpdateGroupLastAccessTime(group_record_.group_id, 689 base::Time::Now()); 690 691 AppCacheDatabase::CacheRecord cache; 692 if (database_->FindCacheForGroup(group_record_.group_id, &cache)) { 693 // Get the set of response ids in the old cache. 694 std::set<int64> existing_response_ids; 695 database_->FindResponseIdsForCacheAsSet(cache.cache_id, 696 &existing_response_ids); 697 698 // Remove those that remain in the new cache. 699 std::vector<AppCacheDatabase::EntryRecord>::const_iterator entry_iter = 700 entry_records_.begin(); 701 while (entry_iter != entry_records_.end()) { 702 existing_response_ids.erase(entry_iter->response_id); 703 ++entry_iter; 704 } 705 706 // The rest are deletable. 707 std::set<int64>::const_iterator id_iter = existing_response_ids.begin(); 708 while (id_iter != existing_response_ids.end()) { 709 newly_deletable_response_ids_.push_back(*id_iter); 710 ++id_iter; 711 } 712 713 success_ = 714 database_->DeleteCache(cache.cache_id) && 715 database_->DeleteEntriesForCache(cache.cache_id) && 716 database_->DeleteNamespacesForCache(cache.cache_id) && 717 database_->DeleteOnlineWhiteListForCache(cache.cache_id) && 718 database_->InsertDeletableResponseIds(newly_deletable_response_ids_); 719 // TODO(michaeln): store group_id too with deletable ids 720 } else { 721 NOTREACHED() << "A existing group without a cache is unexpected"; 722 } 723 } 724 725 success_ = 726 success_ && 727 database_->InsertCache(&cache_record_) && 728 database_->InsertEntryRecords(entry_records_) && 729 database_->InsertNamespaceRecords(intercept_namespace_records_) && 730 database_->InsertNamespaceRecords(fallback_namespace_records_) && 731 database_->InsertOnlineWhiteListRecords(online_whitelist_records_); 732 733 if (!success_) 734 return; 735 736 new_origin_usage_ = database_->GetOriginUsage(group_record_.origin); 737 738 // Only check quota when the new usage exceeds the old usage. 739 if (new_origin_usage_ <= old_origin_usage) { 740 success_ = transaction.Commit(); 741 return; 742 } 743 744 // Use a simple hard-coded value when not using quota management. 745 if (space_available_ == -1) { 746 if (new_origin_usage_ > kDefaultQuota) { 747 would_exceed_quota_ = true; 748 success_ = false; 749 return; 750 } 751 success_ = transaction.Commit(); 752 return; 753 } 754 755 // Check limits based on the space availbable given to us via the 756 // quota system. 757 int64 delta = new_origin_usage_ - old_origin_usage; 758 if (delta > space_available_) { 759 would_exceed_quota_ = true; 760 success_ = false; 761 return; 762 } 763 764 success_ = transaction.Commit(); 765 } 766 767 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() { 768 if (success_) { 769 storage_->UpdateUsageMapAndNotify( 770 group_->manifest_url().GetOrigin(), new_origin_usage_); 771 if (cache_.get() != group_->newest_complete_cache()) { 772 cache_->set_complete(true); 773 group_->AddCache(cache_.get()); 774 } 775 if (group_->creation_time().is_null()) 776 group_->set_creation_time(group_record_.creation_time); 777 group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); 778 } 779 FOR_EACH_DELEGATE( 780 delegates_, 781 OnGroupAndNewestCacheStored( 782 group_.get(), cache_.get(), success_, would_exceed_quota_)); 783 group_ = NULL; 784 cache_ = NULL; 785 786 // TODO(michaeln): if (would_exceed_quota_) what if the current usage 787 // also exceeds the quota? http://crbug.com/83968 788 } 789 790 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() { 791 // Overriden to safely drop our reference to the group and cache 792 // which are not thread safe refcounted. 793 DatabaseTask::CancelCompletion(); 794 group_ = NULL; 795 cache_ = NULL; 796 } 797 798 // FindMainResponseTask ------- 799 800 // Helpers for FindMainResponseTask::Run() 801 namespace { 802 class SortByCachePreference 803 : public std::binary_function< 804 AppCacheDatabase::EntryRecord, 805 AppCacheDatabase::EntryRecord, 806 bool> { 807 public: 808 SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids) 809 : preferred_id_(preferred_id), in_use_ids_(in_use_ids) { 810 } 811 bool operator()( 812 const AppCacheDatabase::EntryRecord& lhs, 813 const AppCacheDatabase::EntryRecord& rhs) { 814 return compute_value(lhs) > compute_value(rhs); 815 } 816 private: 817 int compute_value(const AppCacheDatabase::EntryRecord& entry) { 818 if (entry.cache_id == preferred_id_) 819 return 100; 820 else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end()) 821 return 50; 822 return 0; 823 } 824 int64 preferred_id_; 825 const std::set<int64>& in_use_ids_; 826 }; 827 828 bool SortByLength( 829 const AppCacheDatabase::NamespaceRecord& lhs, 830 const AppCacheDatabase::NamespaceRecord& rhs) { 831 return lhs.namespace_.namespace_url.spec().length() > 832 rhs.namespace_.namespace_url.spec().length(); 833 } 834 835 class NetworkNamespaceHelper { 836 public: 837 explicit NetworkNamespaceHelper(AppCacheDatabase* database) 838 : database_(database) { 839 } 840 841 bool IsInNetworkNamespace(const GURL& url, int64 cache_id) { 842 typedef std::pair<WhiteListMap::iterator, bool> InsertResult; 843 InsertResult result = namespaces_map_.insert( 844 WhiteListMap::value_type(cache_id, NamespaceVector())); 845 if (result.second) 846 GetOnlineWhiteListForCache(cache_id, &result.first->second); 847 return AppCache::FindNamespace(result.first->second, url) != NULL; 848 } 849 850 private: 851 void GetOnlineWhiteListForCache( 852 int64 cache_id, NamespaceVector* namespaces) { 853 DCHECK(namespaces && namespaces->empty()); 854 typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord> 855 WhiteListVector; 856 WhiteListVector records; 857 if (!database_->FindOnlineWhiteListForCache(cache_id, &records)) 858 return; 859 WhiteListVector::const_iterator iter = records.begin(); 860 while (iter != records.end()) { 861 namespaces->push_back( 862 Namespace(APPCACHE_NETWORK_NAMESPACE, iter->namespace_url, GURL(), 863 iter->is_pattern)); 864 ++iter; 865 } 866 } 867 868 // Key is cache id 869 typedef std::map<int64, NamespaceVector> WhiteListMap; 870 WhiteListMap namespaces_map_; 871 AppCacheDatabase* database_; 872 }; 873 874 } // namespace 875 876 class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask { 877 public: 878 FindMainResponseTask(AppCacheStorageImpl* storage, 879 const GURL& url, 880 const GURL& preferred_manifest_url, 881 const AppCacheWorkingSet::GroupMap* groups_in_use) 882 : DatabaseTask(storage), url_(url), 883 preferred_manifest_url_(preferred_manifest_url), 884 cache_id_(kAppCacheNoCacheId), group_id_(0) { 885 if (groups_in_use) { 886 for (AppCacheWorkingSet::GroupMap::const_iterator it = 887 groups_in_use->begin(); 888 it != groups_in_use->end(); ++it) { 889 AppCacheGroup* group = it->second; 890 AppCache* cache = group->newest_complete_cache(); 891 if (group->is_obsolete() || !cache) 892 continue; 893 cache_ids_in_use_.insert(cache->cache_id()); 894 } 895 } 896 } 897 898 // DatabaseTask: 899 virtual void Run() OVERRIDE; 900 virtual void RunCompleted() OVERRIDE; 901 902 protected: 903 virtual ~FindMainResponseTask() {} 904 905 private: 906 typedef std::vector<AppCacheDatabase::NamespaceRecord*> 907 NamespaceRecordPtrVector; 908 909 bool FindExactMatch(int64 preferred_id); 910 bool FindNamespaceMatch(int64 preferred_id); 911 bool FindNamespaceHelper( 912 int64 preferred_cache_id, 913 AppCacheDatabase::NamespaceRecordVector* namespaces, 914 NetworkNamespaceHelper* network_namespace_helper); 915 bool FindFirstValidNamespace(const NamespaceRecordPtrVector& namespaces); 916 917 GURL url_; 918 GURL preferred_manifest_url_; 919 std::set<int64> cache_ids_in_use_; 920 AppCacheEntry entry_; 921 AppCacheEntry fallback_entry_; 922 GURL namespace_entry_url_; 923 int64 cache_id_; 924 int64 group_id_; 925 GURL manifest_url_; 926 }; 927 928 void AppCacheStorageImpl::FindMainResponseTask::Run() { 929 // NOTE: The heuristics around choosing amoungst multiple candidates 930 // is underspecified, and just plain not fully understood. This needs 931 // to be refined. 932 933 // The 'preferred_manifest_url' is the url of the manifest associated 934 // with the page that opened or embedded the page being loaded now. 935 // We have a strong preference to use resources from that cache. 936 // We also have a lesser bias to use resources from caches that are currently 937 // being used by other unrelated pages. 938 // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases 939 // - when navigating a frame whose current contents are from an appcache 940 // - when clicking an href in a frame that is appcached 941 int64 preferred_cache_id = kAppCacheNoCacheId; 942 if (!preferred_manifest_url_.is_empty()) { 943 AppCacheDatabase::GroupRecord preferred_group; 944 AppCacheDatabase::CacheRecord preferred_cache; 945 if (database_->FindGroupForManifestUrl( 946 preferred_manifest_url_, &preferred_group) && 947 database_->FindCacheForGroup( 948 preferred_group.group_id, &preferred_cache)) { 949 preferred_cache_id = preferred_cache.cache_id; 950 } 951 } 952 953 if (FindExactMatch(preferred_cache_id) || 954 FindNamespaceMatch(preferred_cache_id)) { 955 // We found something. 956 DCHECK(cache_id_ != kAppCacheNoCacheId && !manifest_url_.is_empty() && 957 group_id_ != 0); 958 return; 959 } 960 961 // We didn't find anything. 962 DCHECK(cache_id_ == kAppCacheNoCacheId && manifest_url_.is_empty() && 963 group_id_ == 0); 964 } 965 966 bool AppCacheStorageImpl:: 967 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) { 968 std::vector<AppCacheDatabase::EntryRecord> entries; 969 if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) { 970 // Sort them in order of preference, from the preferred_cache first, 971 // followed by hits from caches that are 'in use', then the rest. 972 std::sort(entries.begin(), entries.end(), 973 SortByCachePreference(preferred_cache_id, cache_ids_in_use_)); 974 975 // Take the first with a valid, non-foreign entry. 976 std::vector<AppCacheDatabase::EntryRecord>::iterator iter; 977 for (iter = entries.begin(); iter < entries.end(); ++iter) { 978 AppCacheDatabase::GroupRecord group_record; 979 if ((iter->flags & AppCacheEntry::FOREIGN) || 980 !database_->FindGroupForCache(iter->cache_id, &group_record)) { 981 continue; 982 } 983 manifest_url_ = group_record.manifest_url; 984 group_id_ = group_record.group_id; 985 entry_ = AppCacheEntry(iter->flags, iter->response_id); 986 cache_id_ = iter->cache_id; 987 return true; // We found an exact match. 988 } 989 } 990 return false; 991 } 992 993 bool AppCacheStorageImpl:: 994 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id) { 995 AppCacheDatabase::NamespaceRecordVector all_intercepts; 996 AppCacheDatabase::NamespaceRecordVector all_fallbacks; 997 if (!database_->FindNamespacesForOrigin( 998 url_.GetOrigin(), &all_intercepts, &all_fallbacks) 999 || (all_intercepts.empty() && all_fallbacks.empty())) { 1000 return false; 1001 } 1002 1003 NetworkNamespaceHelper network_namespace_helper(database_); 1004 if (FindNamespaceHelper(preferred_cache_id, 1005 &all_intercepts, 1006 &network_namespace_helper) || 1007 FindNamespaceHelper(preferred_cache_id, 1008 &all_fallbacks, 1009 &network_namespace_helper)) { 1010 return true; 1011 } 1012 return false; 1013 } 1014 1015 bool AppCacheStorageImpl:: 1016 FindMainResponseTask::FindNamespaceHelper( 1017 int64 preferred_cache_id, 1018 AppCacheDatabase::NamespaceRecordVector* namespaces, 1019 NetworkNamespaceHelper* network_namespace_helper) { 1020 // Sort them by length, longer matches within the same cache/bucket take 1021 // precedence. 1022 std::sort(namespaces->begin(), namespaces->end(), SortByLength); 1023 1024 NamespaceRecordPtrVector preferred_namespaces; 1025 NamespaceRecordPtrVector inuse_namespaces; 1026 NamespaceRecordPtrVector other_namespaces; 1027 std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter; 1028 for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) { 1029 // Skip those that aren't a match. 1030 if (!iter->namespace_.IsMatch(url_)) 1031 continue; 1032 1033 // Skip namespaces where the requested url falls into a network 1034 // namespace of its containing appcache. 1035 if (network_namespace_helper->IsInNetworkNamespace(url_, iter->cache_id)) 1036 continue; 1037 1038 // Bin them into one of our three buckets. 1039 if (iter->cache_id == preferred_cache_id) 1040 preferred_namespaces.push_back(&(*iter)); 1041 else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end()) 1042 inuse_namespaces.push_back(&(*iter)); 1043 else 1044 other_namespaces.push_back(&(*iter)); 1045 } 1046 1047 if (FindFirstValidNamespace(preferred_namespaces) || 1048 FindFirstValidNamespace(inuse_namespaces) || 1049 FindFirstValidNamespace(other_namespaces)) 1050 return true; // We found one. 1051 1052 // We didn't find anything. 1053 return false; 1054 } 1055 1056 bool AppCacheStorageImpl:: 1057 FindMainResponseTask::FindFirstValidNamespace( 1058 const NamespaceRecordPtrVector& namespaces) { 1059 // Take the first with a valid, non-foreign entry. 1060 NamespaceRecordPtrVector::const_iterator iter; 1061 for (iter = namespaces.begin(); iter < namespaces.end(); ++iter) { 1062 AppCacheDatabase::EntryRecord entry_record; 1063 if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url, 1064 &entry_record)) { 1065 AppCacheDatabase::GroupRecord group_record; 1066 if ((entry_record.flags & AppCacheEntry::FOREIGN) || 1067 !database_->FindGroupForCache(entry_record.cache_id, &group_record)) { 1068 continue; 1069 } 1070 manifest_url_ = group_record.manifest_url; 1071 group_id_ = group_record.group_id; 1072 cache_id_ = (*iter)->cache_id; 1073 namespace_entry_url_ = (*iter)->namespace_.target_url; 1074 if ((*iter)->namespace_.type == APPCACHE_FALLBACK_NAMESPACE) 1075 fallback_entry_ = AppCacheEntry(entry_record.flags, 1076 entry_record.response_id); 1077 else 1078 entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id); 1079 return true; // We found one. 1080 } 1081 } 1082 return false; // We didn't find a match. 1083 } 1084 1085 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() { 1086 storage_->CallOnMainResponseFound( 1087 &delegates_, url_, entry_, namespace_entry_url_, fallback_entry_, 1088 cache_id_, group_id_, manifest_url_); 1089 } 1090 1091 // MarkEntryAsForeignTask ------- 1092 1093 class AppCacheStorageImpl::MarkEntryAsForeignTask : public DatabaseTask { 1094 public: 1095 MarkEntryAsForeignTask( 1096 AppCacheStorageImpl* storage, const GURL& url, int64 cache_id) 1097 : DatabaseTask(storage), cache_id_(cache_id), entry_url_(url) {} 1098 1099 // DatabaseTask: 1100 virtual void Run() OVERRIDE; 1101 virtual void RunCompleted() OVERRIDE; 1102 1103 protected: 1104 virtual ~MarkEntryAsForeignTask() {} 1105 1106 private: 1107 int64 cache_id_; 1108 GURL entry_url_; 1109 }; 1110 1111 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() { 1112 database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN); 1113 } 1114 1115 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() { 1116 DCHECK(storage_->pending_foreign_markings_.front().first == entry_url_ && 1117 storage_->pending_foreign_markings_.front().second == cache_id_); 1118 storage_->pending_foreign_markings_.pop_front(); 1119 } 1120 1121 // MakeGroupObsoleteTask ------- 1122 1123 class AppCacheStorageImpl::MakeGroupObsoleteTask : public DatabaseTask { 1124 public: 1125 MakeGroupObsoleteTask(AppCacheStorageImpl* storage, 1126 AppCacheGroup* group, 1127 int response_code); 1128 1129 // DatabaseTask: 1130 virtual void Run() OVERRIDE; 1131 virtual void RunCompleted() OVERRIDE; 1132 virtual void CancelCompletion() OVERRIDE; 1133 1134 protected: 1135 virtual ~MakeGroupObsoleteTask() {} 1136 1137 private: 1138 scoped_refptr<AppCacheGroup> group_; 1139 int64 group_id_; 1140 GURL origin_; 1141 bool success_; 1142 int response_code_; 1143 int64 new_origin_usage_; 1144 std::vector<int64> newly_deletable_response_ids_; 1145 }; 1146 1147 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask( 1148 AppCacheStorageImpl* storage, 1149 AppCacheGroup* group, 1150 int response_code) 1151 : DatabaseTask(storage), 1152 group_(group), 1153 group_id_(group->group_id()), 1154 origin_(group->manifest_url().GetOrigin()), 1155 success_(false), 1156 response_code_(response_code), 1157 new_origin_usage_(-1) {} 1158 1159 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() { 1160 DCHECK(!success_); 1161 sql::Connection* connection = database_->db_connection(); 1162 if (!connection) 1163 return; 1164 1165 sql::Transaction transaction(connection); 1166 if (!transaction.Begin()) 1167 return; 1168 1169 AppCacheDatabase::GroupRecord group_record; 1170 if (!database_->FindGroup(group_id_, &group_record)) { 1171 // This group doesn't exists in the database, nothing todo here. 1172 new_origin_usage_ = database_->GetOriginUsage(origin_); 1173 success_ = true; 1174 return; 1175 } 1176 1177 DCHECK_EQ(group_record.origin, origin_); 1178 success_ = DeleteGroupAndRelatedRecords(database_, 1179 group_id_, 1180 &newly_deletable_response_ids_); 1181 1182 new_origin_usage_ = database_->GetOriginUsage(origin_); 1183 success_ = success_ && transaction.Commit(); 1184 } 1185 1186 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() { 1187 if (success_) { 1188 group_->set_obsolete(true); 1189 if (!storage_->is_disabled()) { 1190 storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_); 1191 group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); 1192 1193 // Also remove from the working set, caches for an 'obsolete' group 1194 // may linger in use, but the group itself cannot be looked up by 1195 // 'manifest_url' in the working set any longer. 1196 storage_->working_set()->RemoveGroup(group_.get()); 1197 } 1198 } 1199 FOR_EACH_DELEGATE( 1200 delegates_, OnGroupMadeObsolete(group_.get(), success_, response_code_)); 1201 group_ = NULL; 1202 } 1203 1204 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() { 1205 // Overriden to safely drop our reference to the group 1206 // which is not thread safe refcounted. 1207 DatabaseTask::CancelCompletion(); 1208 group_ = NULL; 1209 } 1210 1211 // GetDeletableResponseIdsTask ------- 1212 1213 class AppCacheStorageImpl::GetDeletableResponseIdsTask : public DatabaseTask { 1214 public: 1215 GetDeletableResponseIdsTask(AppCacheStorageImpl* storage, int64 max_rowid) 1216 : DatabaseTask(storage), max_rowid_(max_rowid) {} 1217 1218 // DatabaseTask: 1219 virtual void Run() OVERRIDE; 1220 virtual void RunCompleted() OVERRIDE; 1221 1222 protected: 1223 virtual ~GetDeletableResponseIdsTask() {} 1224 1225 private: 1226 int64 max_rowid_; 1227 std::vector<int64> response_ids_; 1228 }; 1229 1230 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() { 1231 const int kSqlLimit = 1000; 1232 database_->GetDeletableResponseIds(&response_ids_, max_rowid_, kSqlLimit); 1233 // TODO(michaeln): retrieve group_ids too 1234 } 1235 1236 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() { 1237 if (!response_ids_.empty()) 1238 storage_->StartDeletingResponses(response_ids_); 1239 } 1240 1241 // InsertDeletableResponseIdsTask ------- 1242 1243 class AppCacheStorageImpl::InsertDeletableResponseIdsTask 1244 : public DatabaseTask { 1245 public: 1246 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl* storage) 1247 : DatabaseTask(storage) {} 1248 1249 // DatabaseTask: 1250 virtual void Run() OVERRIDE; 1251 1252 std::vector<int64> response_ids_; 1253 1254 protected: 1255 virtual ~InsertDeletableResponseIdsTask() {} 1256 }; 1257 1258 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() { 1259 database_->InsertDeletableResponseIds(response_ids_); 1260 // TODO(michaeln): store group_ids too 1261 } 1262 1263 // DeleteDeletableResponseIdsTask ------- 1264 1265 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask 1266 : public DatabaseTask { 1267 public: 1268 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl* storage) 1269 : DatabaseTask(storage) {} 1270 1271 // DatabaseTask: 1272 virtual void Run() OVERRIDE; 1273 1274 std::vector<int64> response_ids_; 1275 1276 protected: 1277 virtual ~DeleteDeletableResponseIdsTask() {} 1278 }; 1279 1280 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() { 1281 database_->DeleteDeletableResponseIds(response_ids_); 1282 } 1283 1284 // UpdateGroupLastAccessTimeTask ------- 1285 1286 class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask 1287 : public DatabaseTask { 1288 public: 1289 UpdateGroupLastAccessTimeTask( 1290 AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time) 1291 : DatabaseTask(storage), group_id_(group->group_id()), 1292 last_access_time_(time) { 1293 storage->NotifyStorageAccessed(group->manifest_url().GetOrigin()); 1294 } 1295 1296 // DatabaseTask: 1297 virtual void Run() OVERRIDE; 1298 1299 protected: 1300 virtual ~UpdateGroupLastAccessTimeTask() {} 1301 1302 private: 1303 int64 group_id_; 1304 base::Time last_access_time_; 1305 }; 1306 1307 void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() { 1308 database_->UpdateGroupLastAccessTime(group_id_, last_access_time_); 1309 } 1310 1311 1312 // AppCacheStorageImpl --------------------------------------------------- 1313 1314 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl* service) 1315 : AppCacheStorage(service), 1316 is_incognito_(false), 1317 is_response_deletion_scheduled_(false), 1318 did_start_deleting_responses_(false), 1319 last_deletable_response_rowid_(0), 1320 database_(NULL), 1321 is_disabled_(false), 1322 weak_factory_(this) { 1323 } 1324 1325 AppCacheStorageImpl::~AppCacheStorageImpl() { 1326 std::for_each(pending_quota_queries_.begin(), 1327 pending_quota_queries_.end(), 1328 std::mem_fun(&DatabaseTask::CancelCompletion)); 1329 std::for_each(scheduled_database_tasks_.begin(), 1330 scheduled_database_tasks_.end(), 1331 std::mem_fun(&DatabaseTask::CancelCompletion)); 1332 1333 if (database_ && 1334 !db_thread_->PostTask( 1335 FROM_HERE, 1336 base::Bind(&ClearSessionOnlyOrigins, database_, 1337 make_scoped_refptr(service_->special_storage_policy()), 1338 service()->force_keep_session_state()))) { 1339 delete database_; 1340 } 1341 database_ = NULL; // So no further database tasks can be scheduled. 1342 } 1343 1344 void AppCacheStorageImpl::Initialize(const base::FilePath& cache_directory, 1345 base::MessageLoopProxy* db_thread, 1346 base::MessageLoopProxy* cache_thread) { 1347 DCHECK(db_thread); 1348 1349 cache_directory_ = cache_directory; 1350 is_incognito_ = cache_directory_.empty(); 1351 1352 base::FilePath db_file_path; 1353 if (!is_incognito_) 1354 db_file_path = cache_directory_.Append(kAppCacheDatabaseName); 1355 database_ = new AppCacheDatabase(db_file_path); 1356 1357 db_thread_ = db_thread; 1358 cache_thread_ = cache_thread; 1359 1360 scoped_refptr<InitTask> task(new InitTask(this)); 1361 task->Schedule(); 1362 } 1363 1364 void AppCacheStorageImpl::Disable() { 1365 if (is_disabled_) 1366 return; 1367 VLOG(1) << "Disabling appcache storage."; 1368 is_disabled_ = true; 1369 ClearUsageMapAndNotify(); 1370 working_set()->Disable(); 1371 if (disk_cache_) 1372 disk_cache_->Disable(); 1373 scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this)); 1374 task->Schedule(); 1375 } 1376 1377 void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) { 1378 DCHECK(delegate); 1379 scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this)); 1380 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1381 task->Schedule(); 1382 } 1383 1384 void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) { 1385 DCHECK(delegate); 1386 if (is_disabled_) { 1387 delegate->OnCacheLoaded(NULL, id); 1388 return; 1389 } 1390 1391 AppCache* cache = working_set_.GetCache(id); 1392 if (cache) { 1393 delegate->OnCacheLoaded(cache, id); 1394 if (cache->owning_group()) { 1395 scoped_refptr<DatabaseTask> update_task( 1396 new UpdateGroupLastAccessTimeTask( 1397 this, cache->owning_group(), base::Time::Now())); 1398 update_task->Schedule(); 1399 } 1400 return; 1401 } 1402 scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id)); 1403 if (task.get()) { 1404 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1405 return; 1406 } 1407 task = new CacheLoadTask(id, this); 1408 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1409 task->Schedule(); 1410 pending_cache_loads_[id] = task.get(); 1411 } 1412 1413 void AppCacheStorageImpl::LoadOrCreateGroup( 1414 const GURL& manifest_url, Delegate* delegate) { 1415 DCHECK(delegate); 1416 if (is_disabled_) { 1417 delegate->OnGroupLoaded(NULL, manifest_url); 1418 return; 1419 } 1420 1421 AppCacheGroup* group = working_set_.GetGroup(manifest_url); 1422 if (group) { 1423 delegate->OnGroupLoaded(group, manifest_url); 1424 scoped_refptr<DatabaseTask> update_task( 1425 new UpdateGroupLastAccessTimeTask( 1426 this, group, base::Time::Now())); 1427 update_task->Schedule(); 1428 return; 1429 } 1430 1431 scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url)); 1432 if (task.get()) { 1433 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1434 return; 1435 } 1436 1437 if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) { 1438 // No need to query the database, return a new group immediately. 1439 scoped_refptr<AppCacheGroup> group(new AppCacheGroup( 1440 this, manifest_url, NewGroupId())); 1441 delegate->OnGroupLoaded(group.get(), manifest_url); 1442 return; 1443 } 1444 1445 task = new GroupLoadTask(manifest_url, this); 1446 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1447 task->Schedule(); 1448 pending_group_loads_[manifest_url] = task.get(); 1449 } 1450 1451 void AppCacheStorageImpl::StoreGroupAndNewestCache( 1452 AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) { 1453 // TODO(michaeln): distinguish between a simple update of an existing 1454 // cache that just adds new master entry(s), and the insertion of a 1455 // whole new cache. The StoreGroupAndCacheTask as written will handle 1456 // the simple update case in a very heavy weight way (delete all and 1457 // the reinsert all over again). 1458 DCHECK(group && delegate && newest_cache); 1459 scoped_refptr<StoreGroupAndCacheTask> task( 1460 new StoreGroupAndCacheTask(this, group, newest_cache)); 1461 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1462 task->GetQuotaThenSchedule(); 1463 1464 // TODO(michaeln): histogram is fishing for clues to crbug/95101 1465 if (!newest_cache->GetEntry(group->manifest_url())) { 1466 AppCacheHistograms::AddMissingManifestDetectedAtCallsite( 1467 AppCacheHistograms::CALLSITE_3); 1468 } 1469 } 1470 1471 void AppCacheStorageImpl::FindResponseForMainRequest( 1472 const GURL& url, const GURL& preferred_manifest_url, 1473 Delegate* delegate) { 1474 DCHECK(delegate); 1475 1476 const GURL* url_ptr = &url; 1477 GURL url_no_ref; 1478 if (url.has_ref()) { 1479 GURL::Replacements replacements; 1480 replacements.ClearRef(); 1481 url_no_ref = url.ReplaceComponents(replacements); 1482 url_ptr = &url_no_ref; 1483 } 1484 1485 const GURL origin = url.GetOrigin(); 1486 1487 // First look in our working set for a direct hit without having to query 1488 // the database. 1489 const AppCacheWorkingSet::GroupMap* groups_in_use = 1490 working_set()->GetGroupsInOrigin(origin); 1491 if (groups_in_use) { 1492 if (!preferred_manifest_url.is_empty()) { 1493 AppCacheWorkingSet::GroupMap::const_iterator found = 1494 groups_in_use->find(preferred_manifest_url); 1495 if (found != groups_in_use->end() && 1496 FindResponseForMainRequestInGroup( 1497 found->second, *url_ptr, delegate)) { 1498 return; 1499 } 1500 } else { 1501 for (AppCacheWorkingSet::GroupMap::const_iterator it = 1502 groups_in_use->begin(); 1503 it != groups_in_use->end(); ++it) { 1504 if (FindResponseForMainRequestInGroup( 1505 it->second, *url_ptr, delegate)) { 1506 return; 1507 } 1508 } 1509 } 1510 } 1511 1512 if (IsInitTaskComplete() && usage_map_.find(origin) == usage_map_.end()) { 1513 // No need to query the database, return async'ly but without going thru 1514 // the DB thread. 1515 scoped_refptr<AppCacheGroup> no_group; 1516 scoped_refptr<AppCache> no_cache; 1517 ScheduleSimpleTask( 1518 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, 1519 weak_factory_.GetWeakPtr(), url, AppCacheEntry(), no_group, 1520 no_cache, 1521 make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); 1522 return; 1523 } 1524 1525 // We have to query the database, schedule a database task to do so. 1526 scoped_refptr<FindMainResponseTask> task( 1527 new FindMainResponseTask(this, *url_ptr, preferred_manifest_url, 1528 groups_in_use)); 1529 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1530 task->Schedule(); 1531 } 1532 1533 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup( 1534 AppCacheGroup* group, const GURL& url, Delegate* delegate) { 1535 AppCache* cache = group->newest_complete_cache(); 1536 if (group->is_obsolete() || !cache) 1537 return false; 1538 1539 AppCacheEntry* entry = cache->GetEntry(url); 1540 if (!entry || entry->IsForeign()) 1541 return false; 1542 1543 ScheduleSimpleTask( 1544 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, 1545 weak_factory_.GetWeakPtr(), url, *entry, 1546 make_scoped_refptr(group), make_scoped_refptr(cache), 1547 make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); 1548 return true; 1549 } 1550 1551 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse( 1552 const GURL& url, 1553 const AppCacheEntry& found_entry, 1554 scoped_refptr<AppCacheGroup> group, 1555 scoped_refptr<AppCache> cache, 1556 scoped_refptr<DelegateReference> delegate_ref) { 1557 if (delegate_ref->delegate) { 1558 DelegateReferenceVector delegates(1, delegate_ref); 1559 CallOnMainResponseFound( 1560 &delegates, url, found_entry, 1561 GURL(), AppCacheEntry(), 1562 cache.get() ? cache->cache_id() : kAppCacheNoCacheId, 1563 group.get() ? group->group_id() : kAppCacheNoCacheId, 1564 group.get() ? group->manifest_url() : GURL()); 1565 } 1566 } 1567 1568 void AppCacheStorageImpl::CallOnMainResponseFound( 1569 DelegateReferenceVector* delegates, 1570 const GURL& url, const AppCacheEntry& entry, 1571 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry, 1572 int64 cache_id, int64 group_id, const GURL& manifest_url) { 1573 FOR_EACH_DELEGATE( 1574 (*delegates), 1575 OnMainResponseFound(url, entry, 1576 namespace_entry_url, fallback_entry, 1577 cache_id, group_id, manifest_url)); 1578 } 1579 1580 void AppCacheStorageImpl::FindResponseForSubRequest( 1581 AppCache* cache, const GURL& url, 1582 AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry, 1583 bool* found_network_namespace) { 1584 DCHECK(cache && cache->is_complete()); 1585 1586 // When a group is forcibly deleted, all subresource loads for pages 1587 // using caches in the group will result in a synthesized network errors. 1588 // Forcible deletion is not a function that is covered by the HTML5 spec. 1589 if (cache->owning_group()->is_being_deleted()) { 1590 *found_entry = AppCacheEntry(); 1591 *found_fallback_entry = AppCacheEntry(); 1592 *found_network_namespace = false; 1593 return; 1594 } 1595 1596 GURL fallback_namespace_not_used; 1597 GURL intercept_namespace_not_used; 1598 cache->FindResponseForRequest( 1599 url, found_entry, &intercept_namespace_not_used, 1600 found_fallback_entry, &fallback_namespace_not_used, 1601 found_network_namespace); 1602 } 1603 1604 void AppCacheStorageImpl::MarkEntryAsForeign( 1605 const GURL& entry_url, int64 cache_id) { 1606 AppCache* cache = working_set_.GetCache(cache_id); 1607 if (cache) { 1608 AppCacheEntry* entry = cache->GetEntry(entry_url); 1609 DCHECK(entry); 1610 if (entry) 1611 entry->add_types(AppCacheEntry::FOREIGN); 1612 } 1613 scoped_refptr<MarkEntryAsForeignTask> task( 1614 new MarkEntryAsForeignTask(this, entry_url, cache_id)); 1615 task->Schedule(); 1616 pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id)); 1617 } 1618 1619 void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup* group, 1620 Delegate* delegate, 1621 int response_code) { 1622 DCHECK(group && delegate); 1623 scoped_refptr<MakeGroupObsoleteTask> task( 1624 new MakeGroupObsoleteTask(this, group, response_code)); 1625 task->AddDelegate(GetOrCreateDelegateReference(delegate)); 1626 task->Schedule(); 1627 } 1628 1629 AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader( 1630 const GURL& manifest_url, int64 group_id, int64 response_id) { 1631 return new AppCacheResponseReader(response_id, group_id, disk_cache()); 1632 } 1633 1634 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter( 1635 const GURL& manifest_url, int64 group_id) { 1636 return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache()); 1637 } 1638 1639 void AppCacheStorageImpl::DoomResponses( 1640 const GURL& manifest_url, const std::vector<int64>& response_ids) { 1641 if (response_ids.empty()) 1642 return; 1643 1644 // Start deleting them from the disk cache lazily. 1645 StartDeletingResponses(response_ids); 1646 1647 // Also schedule a database task to record these ids in the 1648 // deletable responses table. 1649 // TODO(michaeln): There is a race here. If the browser crashes 1650 // prior to committing these rows to the database and prior to us 1651 // having deleted them from the disk cache, we'll never delete them. 1652 scoped_refptr<InsertDeletableResponseIdsTask> task( 1653 new InsertDeletableResponseIdsTask(this)); 1654 task->response_ids_ = response_ids; 1655 task->Schedule(); 1656 } 1657 1658 void AppCacheStorageImpl::DeleteResponses( 1659 const GURL& manifest_url, const std::vector<int64>& response_ids) { 1660 if (response_ids.empty()) 1661 return; 1662 StartDeletingResponses(response_ids); 1663 } 1664 1665 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() { 1666 // Only if we haven't already begun. 1667 if (!did_start_deleting_responses_) { 1668 scoped_refptr<GetDeletableResponseIdsTask> task( 1669 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); 1670 task->Schedule(); 1671 } 1672 } 1673 1674 void AppCacheStorageImpl::StartDeletingResponses( 1675 const std::vector<int64>& response_ids) { 1676 DCHECK(!response_ids.empty()); 1677 did_start_deleting_responses_ = true; 1678 deletable_response_ids_.insert( 1679 deletable_response_ids_.end(), 1680 response_ids.begin(), response_ids.end()); 1681 if (!is_response_deletion_scheduled_) 1682 ScheduleDeleteOneResponse(); 1683 } 1684 1685 void AppCacheStorageImpl::ScheduleDeleteOneResponse() { 1686 DCHECK(!is_response_deletion_scheduled_); 1687 const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(10); 1688 base::MessageLoop::current()->PostDelayedTask( 1689 FROM_HERE, 1690 base::Bind(&AppCacheStorageImpl::DeleteOneResponse, 1691 weak_factory_.GetWeakPtr()), 1692 kDelay); 1693 is_response_deletion_scheduled_ = true; 1694 } 1695 1696 void AppCacheStorageImpl::DeleteOneResponse() { 1697 DCHECK(is_response_deletion_scheduled_); 1698 DCHECK(!deletable_response_ids_.empty()); 1699 1700 if (!disk_cache()) { 1701 DCHECK(is_disabled_); 1702 deletable_response_ids_.clear(); 1703 deleted_response_ids_.clear(); 1704 is_response_deletion_scheduled_ = false; 1705 return; 1706 } 1707 1708 // TODO(michaeln): add group_id to DoomEntry args 1709 int64 id = deletable_response_ids_.front(); 1710 int rv = disk_cache_->DoomEntry( 1711 id, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse, 1712 base::Unretained(this))); 1713 if (rv != net::ERR_IO_PENDING) 1714 OnDeletedOneResponse(rv); 1715 } 1716 1717 void AppCacheStorageImpl::OnDeletedOneResponse(int rv) { 1718 is_response_deletion_scheduled_ = false; 1719 if (is_disabled_) 1720 return; 1721 1722 int64 id = deletable_response_ids_.front(); 1723 deletable_response_ids_.pop_front(); 1724 if (rv != net::ERR_ABORTED) 1725 deleted_response_ids_.push_back(id); 1726 1727 const size_t kBatchSize = 50U; 1728 if (deleted_response_ids_.size() >= kBatchSize || 1729 deletable_response_ids_.empty()) { 1730 scoped_refptr<DeleteDeletableResponseIdsTask> task( 1731 new DeleteDeletableResponseIdsTask(this)); 1732 task->response_ids_.swap(deleted_response_ids_); 1733 task->Schedule(); 1734 } 1735 1736 if (deletable_response_ids_.empty()) { 1737 scoped_refptr<GetDeletableResponseIdsTask> task( 1738 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); 1739 task->Schedule(); 1740 return; 1741 } 1742 1743 ScheduleDeleteOneResponse(); 1744 } 1745 1746 AppCacheStorageImpl::CacheLoadTask* 1747 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) { 1748 PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id); 1749 if (found != pending_cache_loads_.end()) 1750 return found->second; 1751 return NULL; 1752 } 1753 1754 AppCacheStorageImpl::GroupLoadTask* 1755 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL& manifest_url) { 1756 PendingGroupLoads::iterator found = pending_group_loads_.find(manifest_url); 1757 if (found != pending_group_loads_.end()) 1758 return found->second; 1759 return NULL; 1760 } 1761 1762 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache( 1763 int64 cache_id, std::vector<GURL>* urls) { 1764 PendingForeignMarkings::iterator iter = pending_foreign_markings_.begin(); 1765 while (iter != pending_foreign_markings_.end()) { 1766 if (iter->second == cache_id) 1767 urls->push_back(iter->first); 1768 ++iter; 1769 } 1770 } 1771 1772 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure& task) { 1773 pending_simple_tasks_.push_back(task); 1774 base::MessageLoop::current()->PostTask( 1775 FROM_HERE, 1776 base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask, 1777 weak_factory_.GetWeakPtr())); 1778 } 1779 1780 void AppCacheStorageImpl::RunOnePendingSimpleTask() { 1781 DCHECK(!pending_simple_tasks_.empty()); 1782 base::Closure task = pending_simple_tasks_.front(); 1783 pending_simple_tasks_.pop_front(); 1784 task.Run(); 1785 } 1786 1787 AppCacheDiskCache* AppCacheStorageImpl::disk_cache() { 1788 DCHECK(IsInitTaskComplete()); 1789 1790 if (is_disabled_) 1791 return NULL; 1792 1793 if (!disk_cache_) { 1794 int rv = net::OK; 1795 disk_cache_.reset(new AppCacheDiskCache); 1796 if (is_incognito_) { 1797 rv = disk_cache_->InitWithMemBackend( 1798 kMaxMemDiskCacheSize, 1799 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized, 1800 base::Unretained(this))); 1801 } else { 1802 rv = disk_cache_->InitWithDiskBackend( 1803 cache_directory_.Append(kDiskCacheDirectoryName), 1804 kMaxDiskCacheSize, 1805 false, 1806 cache_thread_.get(), 1807 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized, 1808 base::Unretained(this))); 1809 } 1810 1811 if (rv != net::ERR_IO_PENDING) 1812 OnDiskCacheInitialized(rv); 1813 } 1814 return disk_cache_.get(); 1815 } 1816 1817 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) { 1818 if (rv != net::OK) { 1819 LOG(ERROR) << "Failed to open the appcache diskcache."; 1820 AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR); 1821 1822 // We're unable to open the disk cache, this is a fatal error that we can't 1823 // really recover from. We handle it by temporarily disabling the appcache 1824 // deleting the directory on disk and reinitializing the appcache system. 1825 Disable(); 1826 if (rv != net::ERR_ABORTED) 1827 DeleteAndStartOver(); 1828 } 1829 } 1830 1831 void AppCacheStorageImpl::DeleteAndStartOver() { 1832 DCHECK(is_disabled_); 1833 if (!is_incognito_) { 1834 VLOG(1) << "Deleting existing appcache data and starting over."; 1835 // We can have tasks in flight to close file handles on both the db 1836 // and cache threads, we need to allow those tasks to cycle thru 1837 // prior to deleting the files and calling reinit. 1838 cache_thread_->PostTaskAndReply( 1839 FROM_HERE, 1840 base::Bind(&base::DoNothing), 1841 base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2, 1842 weak_factory_.GetWeakPtr())); 1843 } 1844 } 1845 1846 void AppCacheStorageImpl::DeleteAndStartOverPart2() { 1847 db_thread_->PostTaskAndReply( 1848 FROM_HERE, 1849 base::Bind(base::IgnoreResult(&base::DeleteFile), 1850 cache_directory_, true), 1851 base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize, 1852 weak_factory_.GetWeakPtr())); 1853 } 1854 1855 void AppCacheStorageImpl::CallScheduleReinitialize() { 1856 service_->ScheduleReinitialize(); 1857 // note: 'this' may be deleted at this point. 1858 } 1859 1860 } // namespace appcache 1861