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