Update V8 to version 4.6.55.
[chromium-blink-merge.git] / content / browser / appcache / appcache_storage_impl.cc
blob7f1ee9a3fcbfa269f15096c9de6f0a616d251f06
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.
5 #include "content/browser/appcache/appcache_storage_impl.h"
7 #include <algorithm>
8 #include <functional>
9 #include <set>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/files/file_util.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/profiler/scoped_tracker.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "content/browser/appcache/appcache.h"
23 #include "content/browser/appcache/appcache_database.h"
24 #include "content/browser/appcache/appcache_entry.h"
25 #include "content/browser/appcache/appcache_group.h"
26 #include "content/browser/appcache/appcache_histograms.h"
27 #include "content/browser/appcache/appcache_quota_client.h"
28 #include "content/browser/appcache/appcache_response.h"
29 #include "content/browser/appcache/appcache_service_impl.h"
30 #include "net/base/cache_type.h"
31 #include "net/base/net_errors.h"
32 #include "sql/connection.h"
33 #include "sql/transaction.h"
34 #include "storage/browser/quota/quota_client.h"
35 #include "storage/browser/quota/quota_manager.h"
36 #include "storage/browser/quota/quota_manager_proxy.h"
37 #include "storage/browser/quota/special_storage_policy.h"
39 namespace content {
41 // Hard coded default when not using quota management.
42 static const int kDefaultQuota = 5 * 1024 * 1024;
44 static const int kMaxDiskCacheSize = 250 * 1024 * 1024;
45 static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
46 static const base::FilePath::CharType kDiskCacheDirectoryName[] =
47 FILE_PATH_LITERAL("Cache");
49 namespace {
51 // Helpers for clearing data from the AppCacheDatabase.
52 bool DeleteGroupAndRelatedRecords(AppCacheDatabase* database,
53 int64 group_id,
54 std::vector<int64>* deletable_response_ids) {
55 AppCacheDatabase::CacheRecord cache_record;
56 bool success = false;
57 if (database->FindCacheForGroup(group_id, &cache_record)) {
58 database->FindResponseIdsForCacheAsVector(cache_record.cache_id,
59 deletable_response_ids);
60 success =
61 database->DeleteGroup(group_id) &&
62 database->DeleteCache(cache_record.cache_id) &&
63 database->DeleteEntriesForCache(cache_record.cache_id) &&
64 database->DeleteNamespacesForCache(cache_record.cache_id) &&
65 database->DeleteOnlineWhiteListForCache(cache_record.cache_id) &&
66 database->InsertDeletableResponseIds(*deletable_response_ids);
67 } else {
68 NOTREACHED() << "A existing group without a cache is unexpected";
69 success = database->DeleteGroup(group_id);
71 return success;
74 // Destroys |database|. If there is appcache data to be deleted
75 // (|force_keep_session_state| is false), deletes session-only appcache data.
76 void ClearSessionOnlyOrigins(
77 AppCacheDatabase* database,
78 scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
79 bool force_keep_session_state) {
80 scoped_ptr<AppCacheDatabase> database_to_delete(database);
82 // If saving session state, only delete the database.
83 if (force_keep_session_state)
84 return;
86 bool has_session_only_appcaches =
87 special_storage_policy.get() &&
88 special_storage_policy->HasSessionOnlyOrigins();
90 // Clearning only session-only databases, and there are none.
91 if (!has_session_only_appcaches)
92 return;
94 std::set<GURL> origins;
95 database->FindOriginsWithGroups(&origins);
96 if (origins.empty())
97 return; // nothing to delete
99 sql::Connection* connection = database->db_connection();
100 if (!connection) {
101 NOTREACHED() << "Missing database connection.";
102 return;
105 std::set<GURL>::const_iterator origin;
106 DCHECK(special_storage_policy.get());
107 for (origin = origins.begin(); origin != origins.end(); ++origin) {
108 if (!special_storage_policy->IsStorageSessionOnly(*origin))
109 continue;
110 if (special_storage_policy->IsStorageProtected(*origin))
111 continue;
113 std::vector<AppCacheDatabase::GroupRecord> groups;
114 database->FindGroupsForOrigin(*origin, &groups);
115 std::vector<AppCacheDatabase::GroupRecord>::const_iterator group;
116 for (group = groups.begin(); group != groups.end(); ++group) {
117 sql::Transaction transaction(connection);
118 if (!transaction.Begin()) {
119 NOTREACHED() << "Failed to start transaction";
120 return;
122 std::vector<int64> deletable_response_ids;
123 bool success = DeleteGroupAndRelatedRecords(database,
124 group->group_id,
125 &deletable_response_ids);
126 success = success && transaction.Commit();
127 DCHECK(success);
128 } // for each group
129 } // for each origin
132 } // namespace
134 // DatabaseTask -----------------------------------------
136 class AppCacheStorageImpl::DatabaseTask
137 : public base::RefCountedThreadSafe<DatabaseTask> {
138 public:
139 explicit DatabaseTask(AppCacheStorageImpl* storage)
140 : storage_(storage),
141 database_(storage->database_),
142 io_thread_(base::ThreadTaskRunnerHandle::Get()) {
143 DCHECK(io_thread_.get());
146 void AddDelegate(DelegateReference* delegate_reference) {
147 delegates_.push_back(make_scoped_refptr(delegate_reference));
150 // Schedules a task to be Run() on the DB thread. Tasks
151 // are run in the order in which they are scheduled.
152 void Schedule();
154 // Called on the DB thread.
155 virtual void Run() = 0;
157 // Called on the IO thread after Run() has completed.
158 virtual void RunCompleted() {}
160 // Once scheduled a task cannot be cancelled, but the
161 // call to RunCompleted may be. This method should only be
162 // called on the IO thread. This is used by AppCacheStorageImpl
163 // to cancel the completion calls when AppCacheStorageImpl is
164 // destructed. This method may be overriden to release or delete
165 // additional data associated with the task that is not DB thread
166 // safe. If overriden, this base class method must be called from
167 // within the override.
168 virtual void CancelCompletion();
170 protected:
171 friend class base::RefCountedThreadSafe<DatabaseTask>;
172 virtual ~DatabaseTask() {}
174 AppCacheStorageImpl* storage_;
175 AppCacheDatabase* database_;
176 DelegateReferenceVector delegates_;
178 private:
179 void CallRun(base::TimeTicks schedule_time);
180 void CallRunCompleted(base::TimeTicks schedule_time);
181 void OnFatalError();
183 scoped_refptr<base::SingleThreadTaskRunner> io_thread_;
186 void AppCacheStorageImpl::DatabaseTask::Schedule() {
187 DCHECK(storage_);
188 DCHECK(io_thread_->BelongsToCurrentThread());
189 if (!storage_->database_)
190 return;
192 if (storage_->db_thread_->PostTask(
193 FROM_HERE,
194 base::Bind(&DatabaseTask::CallRun, this, base::TimeTicks::Now()))) {
195 storage_->scheduled_database_tasks_.push_back(this);
196 } else {
197 NOTREACHED() << "Thread for database tasks is not running.";
201 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
202 DCHECK(io_thread_->BelongsToCurrentThread());
203 delegates_.clear();
204 storage_ = NULL;
207 void AppCacheStorageImpl::DatabaseTask::CallRun(
208 base::TimeTicks schedule_time) {
209 AppCacheHistograms::AddTaskQueueTimeSample(
210 base::TimeTicks::Now() - schedule_time);
211 if (!database_->is_disabled()) {
212 base::TimeTicks run_time = base::TimeTicks::Now();
213 Run();
214 AppCacheHistograms::AddTaskRunTimeSample(
215 base::TimeTicks::Now() - run_time);
217 if (database_->was_corruption_detected()) {
218 AppCacheHistograms::CountCorruptionDetected();
219 database_->Disable();
221 if (database_->is_disabled()) {
222 io_thread_->PostTask(
223 FROM_HERE,
224 base::Bind(&DatabaseTask::OnFatalError, this));
227 io_thread_->PostTask(
228 FROM_HERE,
229 base::Bind(&DatabaseTask::CallRunCompleted, this,
230 base::TimeTicks::Now()));
233 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
234 base::TimeTicks schedule_time) {
235 AppCacheHistograms::AddCompletionQueueTimeSample(
236 base::TimeTicks::Now() - schedule_time);
237 if (storage_) {
238 DCHECK(io_thread_->BelongsToCurrentThread());
239 DCHECK(storage_->scheduled_database_tasks_.front() == this);
240 storage_->scheduled_database_tasks_.pop_front();
241 base::TimeTicks run_time = base::TimeTicks::Now();
242 RunCompleted();
243 AppCacheHistograms::AddCompletionRunTimeSample(
244 base::TimeTicks::Now() - run_time);
245 delegates_.clear();
249 void AppCacheStorageImpl::DatabaseTask::OnFatalError() {
250 if (storage_) {
251 DCHECK(io_thread_->BelongsToCurrentThread());
252 storage_->Disable();
253 storage_->DeleteAndStartOver();
257 // InitTask -------
259 class AppCacheStorageImpl::InitTask : public DatabaseTask {
260 public:
261 explicit InitTask(AppCacheStorageImpl* storage)
262 : DatabaseTask(storage), last_group_id_(0),
263 last_cache_id_(0), last_response_id_(0),
264 last_deletable_response_rowid_(0) {
265 if (!storage->is_incognito_) {
266 db_file_path_ =
267 storage->cache_directory_.Append(kAppCacheDatabaseName);
268 disk_cache_directory_ =
269 storage->cache_directory_.Append(kDiskCacheDirectoryName);
273 // DatabaseTask:
274 void Run() override;
275 void RunCompleted() override;
277 protected:
278 ~InitTask() override {}
280 private:
281 base::FilePath db_file_path_;
282 base::FilePath disk_cache_directory_;
283 int64 last_group_id_;
284 int64 last_cache_id_;
285 int64 last_response_id_;
286 int64 last_deletable_response_rowid_;
287 std::map<GURL, int64> usage_map_;
290 void AppCacheStorageImpl::InitTask::Run() {
291 tracked_objects::ScopedTracker tracking_profile(
292 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::InitTask"));
293 // If there is no sql database, ensure there is no disk cache either.
294 if (!db_file_path_.empty() &&
295 !base::PathExists(db_file_path_) &&
296 base::DirectoryExists(disk_cache_directory_)) {
297 base::DeleteFile(disk_cache_directory_, true);
298 if (base::DirectoryExists(disk_cache_directory_)) {
299 database_->Disable(); // This triggers OnFatalError handling.
300 return;
304 database_->FindLastStorageIds(
305 &last_group_id_, &last_cache_id_, &last_response_id_,
306 &last_deletable_response_rowid_);
307 database_->GetAllOriginUsage(&usage_map_);
310 void AppCacheStorageImpl::InitTask::RunCompleted() {
311 storage_->last_group_id_ = last_group_id_;
312 storage_->last_cache_id_ = last_cache_id_;
313 storage_->last_response_id_ = last_response_id_;
314 storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_;
316 if (!storage_->is_disabled()) {
317 storage_->usage_map_.swap(usage_map_);
318 const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5);
319 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
320 FROM_HERE,
321 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses,
322 storage_->weak_factory_.GetWeakPtr()),
323 kDelay);
326 if (storage_->service()->quota_client())
327 storage_->service()->quota_client()->NotifyAppCacheReady();
330 // DisableDatabaseTask -------
332 class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask {
333 public:
334 explicit DisableDatabaseTask(AppCacheStorageImpl* storage)
335 : DatabaseTask(storage) {}
337 // DatabaseTask:
338 void Run() override { database_->Disable(); }
340 protected:
341 ~DisableDatabaseTask() override {}
344 // GetAllInfoTask -------
346 class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask {
347 public:
348 explicit GetAllInfoTask(AppCacheStorageImpl* storage)
349 : DatabaseTask(storage),
350 info_collection_(new AppCacheInfoCollection()) {
353 // DatabaseTask:
354 void Run() override;
355 void RunCompleted() override;
357 protected:
358 ~GetAllInfoTask() override {}
360 private:
361 scoped_refptr<AppCacheInfoCollection> info_collection_;
364 void AppCacheStorageImpl::GetAllInfoTask::Run() {
365 std::set<GURL> origins;
366 database_->FindOriginsWithGroups(&origins);
367 for (std::set<GURL>::const_iterator origin = origins.begin();
368 origin != origins.end(); ++origin) {
369 AppCacheInfoVector& infos =
370 info_collection_->infos_by_origin[*origin];
371 std::vector<AppCacheDatabase::GroupRecord> groups;
372 database_->FindGroupsForOrigin(*origin, &groups);
373 for (std::vector<AppCacheDatabase::GroupRecord>::const_iterator
374 group = groups.begin();
375 group != groups.end(); ++group) {
376 AppCacheDatabase::CacheRecord cache_record;
377 database_->FindCacheForGroup(group->group_id, &cache_record);
378 AppCacheInfo info;
379 info.manifest_url = group->manifest_url;
380 info.creation_time = group->creation_time;
381 info.size = cache_record.cache_size;
382 info.last_access_time = group->last_access_time;
383 info.last_update_time = cache_record.update_time;
384 info.cache_id = cache_record.cache_id;
385 info.group_id = group->group_id;
386 info.is_complete = true;
387 infos.push_back(info);
392 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
393 DCHECK_EQ(1U, delegates_.size());
394 FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get()));
397 // StoreOrLoadTask -------
399 class AppCacheStorageImpl::StoreOrLoadTask : public DatabaseTask {
400 protected:
401 explicit StoreOrLoadTask(AppCacheStorageImpl* storage)
402 : DatabaseTask(storage) {}
403 ~StoreOrLoadTask() override {}
405 bool FindRelatedCacheRecords(int64 cache_id);
406 void CreateCacheAndGroupFromRecords(
407 scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group);
409 AppCacheDatabase::GroupRecord group_record_;
410 AppCacheDatabase::CacheRecord cache_record_;
411 std::vector<AppCacheDatabase::EntryRecord> entry_records_;
412 std::vector<AppCacheDatabase::NamespaceRecord>
413 intercept_namespace_records_;
414 std::vector<AppCacheDatabase::NamespaceRecord>
415 fallback_namespace_records_;
416 std::vector<AppCacheDatabase::OnlineWhiteListRecord>
417 online_whitelist_records_;
420 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
421 int64 cache_id) {
422 return database_->FindEntriesForCache(cache_id, &entry_records_) &&
423 database_->FindNamespacesForCache(
424 cache_id, &intercept_namespace_records_,
425 &fallback_namespace_records_) &&
426 database_->FindOnlineWhiteListForCache(
427 cache_id, &online_whitelist_records_);
430 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
431 scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group) {
432 DCHECK(storage_ && cache && group);
434 (*cache) = storage_->working_set_.GetCache(cache_record_.cache_id);
435 if (cache->get()) {
436 (*group) = cache->get()->owning_group();
437 DCHECK(group->get());
438 DCHECK_EQ(group_record_.group_id, group->get()->group_id());
440 // TODO(michaeln): histogram is fishing for clues to crbug/95101
441 if (!cache->get()->GetEntry(group_record_.manifest_url)) {
442 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
443 AppCacheHistograms::CALLSITE_0);
446 storage_->NotifyStorageAccessed(group_record_.origin);
447 return;
450 (*cache) = new AppCache(storage_, cache_record_.cache_id);
451 cache->get()->InitializeWithDatabaseRecords(
452 cache_record_, entry_records_,
453 intercept_namespace_records_,
454 fallback_namespace_records_,
455 online_whitelist_records_);
456 cache->get()->set_complete(true);
458 (*group) = storage_->working_set_.GetGroup(group_record_.manifest_url);
459 if (group->get()) {
460 DCHECK(group_record_.group_id == group->get()->group_id());
461 group->get()->AddCache(cache->get());
463 // TODO(michaeln): histogram is fishing for clues to crbug/95101
464 if (!cache->get()->GetEntry(group_record_.manifest_url)) {
465 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
466 AppCacheHistograms::CALLSITE_1);
468 } else {
469 (*group) = new AppCacheGroup(
470 storage_, group_record_.manifest_url,
471 group_record_.group_id);
472 group->get()->set_creation_time(group_record_.creation_time);
473 group->get()->set_last_full_update_check_time(
474 group_record_.last_full_update_check_time);
475 group->get()->set_first_evictable_error_time(
476 group_record_.first_evictable_error_time);
477 group->get()->AddCache(cache->get());
479 // TODO(michaeln): histogram is fishing for clues to crbug/95101
480 if (!cache->get()->GetEntry(group_record_.manifest_url)) {
481 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
482 AppCacheHistograms::CALLSITE_2);
485 DCHECK(group->get()->newest_complete_cache() == cache->get());
487 // We have to update foriegn entries if MarkEntryAsForeignTasks
488 // are in flight.
489 std::vector<GURL> urls;
490 storage_->GetPendingForeignMarkingsForCache(cache->get()->cache_id(), &urls);
491 for (std::vector<GURL>::iterator iter = urls.begin();
492 iter != urls.end(); ++iter) {
493 DCHECK(cache->get()->GetEntry(*iter));
494 cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN);
497 storage_->NotifyStorageAccessed(group_record_.origin);
499 // TODO(michaeln): Maybe verify that the responses we expect to exist
500 // do actually exist in the disk_cache (and if not then what?)
503 // CacheLoadTask -------
505 class AppCacheStorageImpl::CacheLoadTask : public StoreOrLoadTask {
506 public:
507 CacheLoadTask(int64 cache_id, AppCacheStorageImpl* storage)
508 : StoreOrLoadTask(storage), cache_id_(cache_id),
509 success_(false) {}
511 // DatabaseTask:
512 void Run() override;
513 void RunCompleted() override;
515 protected:
516 ~CacheLoadTask() override {}
518 private:
519 int64 cache_id_;
520 bool success_;
523 void AppCacheStorageImpl::CacheLoadTask::Run() {
524 tracked_objects::ScopedTracker tracking_profile(
525 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::CacheLoadTask"));
526 success_ =
527 database_->FindCache(cache_id_, &cache_record_) &&
528 database_->FindGroup(cache_record_.group_id, &group_record_) &&
529 FindRelatedCacheRecords(cache_id_);
531 if (success_)
532 database_->LazyUpdateLastAccessTime(group_record_.group_id,
533 base::Time::Now());
536 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
537 storage_->pending_cache_loads_.erase(cache_id_);
538 scoped_refptr<AppCache> cache;
539 scoped_refptr<AppCacheGroup> group;
540 if (success_ && !storage_->is_disabled()) {
541 storage_->LazilyCommitLastAccessTimes();
542 DCHECK(cache_record_.cache_id == cache_id_);
543 CreateCacheAndGroupFromRecords(&cache, &group);
545 FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_));
548 // GroupLoadTask -------
550 class AppCacheStorageImpl::GroupLoadTask : public StoreOrLoadTask {
551 public:
552 GroupLoadTask(GURL manifest_url, AppCacheStorageImpl* storage)
553 : StoreOrLoadTask(storage), manifest_url_(manifest_url),
554 success_(false) {}
556 // DatabaseTask:
557 void Run() override;
558 void RunCompleted() override;
560 protected:
561 ~GroupLoadTask() override {}
563 private:
564 GURL manifest_url_;
565 bool success_;
568 void AppCacheStorageImpl::GroupLoadTask::Run() {
569 tracked_objects::ScopedTracker tracking_profile(
570 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::GroupLoadTask"));
571 success_ =
572 database_->FindGroupForManifestUrl(manifest_url_, &group_record_) &&
573 database_->FindCacheForGroup(group_record_.group_id, &cache_record_) &&
574 FindRelatedCacheRecords(cache_record_.cache_id);
576 if (success_)
577 database_->LazyUpdateLastAccessTime(group_record_.group_id,
578 base::Time::Now());
581 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
582 storage_->pending_group_loads_.erase(manifest_url_);
583 scoped_refptr<AppCacheGroup> group;
584 scoped_refptr<AppCache> cache;
585 if (!storage_->is_disabled()) {
586 if (success_) {
587 storage_->LazilyCommitLastAccessTimes();
588 DCHECK(group_record_.manifest_url == manifest_url_);
589 CreateCacheAndGroupFromRecords(&cache, &group);
590 } else {
591 group = storage_->working_set_.GetGroup(manifest_url_);
592 if (!group.get()) {
593 group =
594 new AppCacheGroup(storage_, manifest_url_, storage_->NewGroupId());
598 FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_));
601 // StoreGroupAndCacheTask -------
603 class AppCacheStorageImpl::StoreGroupAndCacheTask : public StoreOrLoadTask {
604 public:
605 StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group,
606 AppCache* newest_cache);
608 void GetQuotaThenSchedule();
609 void OnQuotaCallback(storage::QuotaStatusCode status,
610 int64 usage,
611 int64 quota);
613 // DatabaseTask:
614 void Run() override;
615 void RunCompleted() override;
616 void CancelCompletion() override;
618 protected:
619 ~StoreGroupAndCacheTask() override {}
621 private:
622 scoped_refptr<AppCacheGroup> group_;
623 scoped_refptr<AppCache> cache_;
624 bool success_;
625 bool would_exceed_quota_;
626 int64 space_available_;
627 int64 new_origin_usage_;
628 std::vector<int64> newly_deletable_response_ids_;
631 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
632 AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache)
633 : StoreOrLoadTask(storage), group_(group), cache_(newest_cache),
634 success_(false), would_exceed_quota_(false),
635 space_available_(-1), new_origin_usage_(-1) {
636 group_record_.group_id = group->group_id();
637 group_record_.manifest_url = group->manifest_url();
638 group_record_.origin = group_record_.manifest_url.GetOrigin();
639 group_record_.last_full_update_check_time =
640 group->last_full_update_check_time();
641 group_record_.first_evictable_error_time =
642 group->first_evictable_error_time();
643 newest_cache->ToDatabaseRecords(
644 group,
645 &cache_record_, &entry_records_,
646 &intercept_namespace_records_,
647 &fallback_namespace_records_,
648 &online_whitelist_records_);
651 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
652 storage::QuotaManager* quota_manager = NULL;
653 if (storage_->service()->quota_manager_proxy()) {
654 quota_manager =
655 storage_->service()->quota_manager_proxy()->quota_manager();
658 if (!quota_manager) {
659 if (storage_->service()->special_storage_policy() &&
660 storage_->service()->special_storage_policy()->IsStorageUnlimited(
661 group_record_.origin))
662 space_available_ = kint64max;
663 Schedule();
664 return;
667 // We have to ask the quota manager for the value.
668 storage_->pending_quota_queries_.insert(this);
669 quota_manager->GetUsageAndQuota(
670 group_record_.origin,
671 storage::kStorageTypeTemporary,
672 base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback, this));
675 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
676 storage::QuotaStatusCode status,
677 int64 usage,
678 int64 quota) {
679 if (storage_) {
680 if (status == storage::kQuotaStatusOk)
681 space_available_ = std::max(static_cast<int64>(0), quota - usage);
682 else
683 space_available_ = 0;
684 storage_->pending_quota_queries_.erase(this);
685 Schedule();
689 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
690 DCHECK(!success_);
691 sql::Connection* connection = database_->db_connection();
692 if (!connection)
693 return;
695 sql::Transaction transaction(connection);
696 if (!transaction.Begin())
697 return;
699 int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin);
701 AppCacheDatabase::GroupRecord existing_group;
702 success_ = database_->FindGroup(group_record_.group_id, &existing_group);
703 if (!success_) {
704 group_record_.creation_time = base::Time::Now();
705 group_record_.last_access_time = base::Time::Now();
706 success_ = database_->InsertGroup(&group_record_);
707 } else {
708 DCHECK(group_record_.group_id == existing_group.group_id);
709 DCHECK(group_record_.manifest_url == existing_group.manifest_url);
710 DCHECK(group_record_.origin == existing_group.origin);
712 database_->UpdateLastAccessTime(group_record_.group_id,
713 base::Time::Now());
715 database_->UpdateEvictionTimes(
716 group_record_.group_id,
717 group_record_.last_full_update_check_time,
718 group_record_.first_evictable_error_time);
720 AppCacheDatabase::CacheRecord cache;
721 if (database_->FindCacheForGroup(group_record_.group_id, &cache)) {
722 // Get the set of response ids in the old cache.
723 std::set<int64> existing_response_ids;
724 database_->FindResponseIdsForCacheAsSet(cache.cache_id,
725 &existing_response_ids);
727 // Remove those that remain in the new cache.
728 std::vector<AppCacheDatabase::EntryRecord>::const_iterator entry_iter =
729 entry_records_.begin();
730 while (entry_iter != entry_records_.end()) {
731 existing_response_ids.erase(entry_iter->response_id);
732 ++entry_iter;
735 // The rest are deletable.
736 std::set<int64>::const_iterator id_iter = existing_response_ids.begin();
737 while (id_iter != existing_response_ids.end()) {
738 newly_deletable_response_ids_.push_back(*id_iter);
739 ++id_iter;
742 success_ =
743 database_->DeleteCache(cache.cache_id) &&
744 database_->DeleteEntriesForCache(cache.cache_id) &&
745 database_->DeleteNamespacesForCache(cache.cache_id) &&
746 database_->DeleteOnlineWhiteListForCache(cache.cache_id) &&
747 database_->InsertDeletableResponseIds(newly_deletable_response_ids_);
748 // TODO(michaeln): store group_id too with deletable ids
749 } else {
750 NOTREACHED() << "A existing group without a cache is unexpected";
754 success_ =
755 success_ &&
756 database_->InsertCache(&cache_record_) &&
757 database_->InsertEntryRecords(entry_records_) &&
758 database_->InsertNamespaceRecords(intercept_namespace_records_) &&
759 database_->InsertNamespaceRecords(fallback_namespace_records_) &&
760 database_->InsertOnlineWhiteListRecords(online_whitelist_records_);
762 if (!success_)
763 return;
765 new_origin_usage_ = database_->GetOriginUsage(group_record_.origin);
767 // Only check quota when the new usage exceeds the old usage.
768 if (new_origin_usage_ <= old_origin_usage) {
769 success_ = transaction.Commit();
770 return;
773 // Use a simple hard-coded value when not using quota management.
774 if (space_available_ == -1) {
775 if (new_origin_usage_ > kDefaultQuota) {
776 would_exceed_quota_ = true;
777 success_ = false;
778 return;
780 success_ = transaction.Commit();
781 return;
784 // Check limits based on the space availbable given to us via the
785 // quota system.
786 int64 delta = new_origin_usage_ - old_origin_usage;
787 if (delta > space_available_) {
788 would_exceed_quota_ = true;
789 success_ = false;
790 return;
793 success_ = transaction.Commit();
796 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
797 if (success_) {
798 storage_->UpdateUsageMapAndNotify(
799 group_->manifest_url().GetOrigin(), new_origin_usage_);
800 if (cache_.get() != group_->newest_complete_cache()) {
801 cache_->set_complete(true);
802 group_->AddCache(cache_.get());
804 if (group_->creation_time().is_null())
805 group_->set_creation_time(group_record_.creation_time);
806 group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
808 FOR_EACH_DELEGATE(
809 delegates_,
810 OnGroupAndNewestCacheStored(
811 group_.get(), cache_.get(), success_, would_exceed_quota_));
812 group_ = NULL;
813 cache_ = NULL;
815 // TODO(michaeln): if (would_exceed_quota_) what if the current usage
816 // also exceeds the quota? http://crbug.com/83968
819 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
820 // Overriden to safely drop our reference to the group and cache
821 // which are not thread safe refcounted.
822 DatabaseTask::CancelCompletion();
823 group_ = NULL;
824 cache_ = NULL;
827 // FindMainResponseTask -------
829 // Helpers for FindMainResponseTask::Run()
830 namespace {
831 class SortByCachePreference
832 : public std::binary_function<
833 AppCacheDatabase::EntryRecord,
834 AppCacheDatabase::EntryRecord,
835 bool> {
836 public:
837 SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids)
838 : preferred_id_(preferred_id), in_use_ids_(in_use_ids) {
840 bool operator()(
841 const AppCacheDatabase::EntryRecord& lhs,
842 const AppCacheDatabase::EntryRecord& rhs) {
843 return compute_value(lhs) > compute_value(rhs);
845 private:
846 int compute_value(const AppCacheDatabase::EntryRecord& entry) {
847 if (entry.cache_id == preferred_id_)
848 return 100;
849 else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end())
850 return 50;
851 return 0;
853 int64 preferred_id_;
854 const std::set<int64>& in_use_ids_;
857 bool SortByLength(
858 const AppCacheDatabase::NamespaceRecord& lhs,
859 const AppCacheDatabase::NamespaceRecord& rhs) {
860 return lhs.namespace_.namespace_url.spec().length() >
861 rhs.namespace_.namespace_url.spec().length();
864 class NetworkNamespaceHelper {
865 public:
866 explicit NetworkNamespaceHelper(AppCacheDatabase* database)
867 : database_(database) {
870 bool IsInNetworkNamespace(const GURL& url, int64 cache_id) {
871 typedef std::pair<WhiteListMap::iterator, bool> InsertResult;
872 InsertResult result = namespaces_map_.insert(
873 WhiteListMap::value_type(cache_id, AppCacheNamespaceVector()));
874 if (result.second)
875 GetOnlineWhiteListForCache(cache_id, &result.first->second);
876 return AppCache::FindNamespace(result.first->second, url) != NULL;
879 private:
880 void GetOnlineWhiteListForCache(
881 int64 cache_id, AppCacheNamespaceVector* namespaces) {
882 DCHECK(namespaces && namespaces->empty());
883 typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord>
884 WhiteListVector;
885 WhiteListVector records;
886 if (!database_->FindOnlineWhiteListForCache(cache_id, &records))
887 return;
888 WhiteListVector::const_iterator iter = records.begin();
889 while (iter != records.end()) {
890 namespaces->push_back(
891 AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, iter->namespace_url,
892 GURL(), iter->is_pattern));
893 ++iter;
897 // Key is cache id
898 typedef std::map<int64, AppCacheNamespaceVector> WhiteListMap;
899 WhiteListMap namespaces_map_;
900 AppCacheDatabase* database_;
903 } // namespace
905 class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask {
906 public:
907 FindMainResponseTask(AppCacheStorageImpl* storage,
908 const GURL& url,
909 const GURL& preferred_manifest_url,
910 const AppCacheWorkingSet::GroupMap* groups_in_use)
911 : DatabaseTask(storage), url_(url),
912 preferred_manifest_url_(preferred_manifest_url),
913 cache_id_(kAppCacheNoCacheId), group_id_(0) {
914 if (groups_in_use) {
915 for (AppCacheWorkingSet::GroupMap::const_iterator it =
916 groups_in_use->begin();
917 it != groups_in_use->end(); ++it) {
918 AppCacheGroup* group = it->second;
919 AppCache* cache = group->newest_complete_cache();
920 if (group->is_obsolete() || !cache)
921 continue;
922 cache_ids_in_use_.insert(cache->cache_id());
927 // DatabaseTask:
928 void Run() override;
929 void RunCompleted() override;
931 protected:
932 ~FindMainResponseTask() override {}
934 private:
935 typedef std::vector<AppCacheDatabase::NamespaceRecord*>
936 NamespaceRecordPtrVector;
938 bool FindExactMatch(int64 preferred_id);
939 bool FindNamespaceMatch(int64 preferred_id);
940 bool FindNamespaceHelper(
941 int64 preferred_cache_id,
942 AppCacheDatabase::NamespaceRecordVector* namespaces,
943 NetworkNamespaceHelper* network_namespace_helper);
944 bool FindFirstValidNamespace(const NamespaceRecordPtrVector& namespaces);
946 GURL url_;
947 GURL preferred_manifest_url_;
948 std::set<int64> cache_ids_in_use_;
949 AppCacheEntry entry_;
950 AppCacheEntry fallback_entry_;
951 GURL namespace_entry_url_;
952 int64 cache_id_;
953 int64 group_id_;
954 GURL manifest_url_;
957 void AppCacheStorageImpl::FindMainResponseTask::Run() {
958 tracked_objects::ScopedTracker tracking_profile(
959 FROM_HERE_WITH_EXPLICIT_FUNCTION(
960 "AppCacheStorageImpl::FindMainResponseTask"));
961 // NOTE: The heuristics around choosing amoungst multiple candidates
962 // is underspecified, and just plain not fully understood. This needs
963 // to be refined.
965 // The 'preferred_manifest_url' is the url of the manifest associated
966 // with the page that opened or embedded the page being loaded now.
967 // We have a strong preference to use resources from that cache.
968 // We also have a lesser bias to use resources from caches that are currently
969 // being used by other unrelated pages.
970 // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
971 // - when navigating a frame whose current contents are from an appcache
972 // - when clicking an href in a frame that is appcached
973 int64 preferred_cache_id = kAppCacheNoCacheId;
974 if (!preferred_manifest_url_.is_empty()) {
975 AppCacheDatabase::GroupRecord preferred_group;
976 AppCacheDatabase::CacheRecord preferred_cache;
977 if (database_->FindGroupForManifestUrl(
978 preferred_manifest_url_, &preferred_group) &&
979 database_->FindCacheForGroup(
980 preferred_group.group_id, &preferred_cache)) {
981 preferred_cache_id = preferred_cache.cache_id;
985 if (FindExactMatch(preferred_cache_id) ||
986 FindNamespaceMatch(preferred_cache_id)) {
987 // We found something.
988 DCHECK(cache_id_ != kAppCacheNoCacheId && !manifest_url_.is_empty() &&
989 group_id_ != 0);
990 return;
993 // We didn't find anything.
994 DCHECK(cache_id_ == kAppCacheNoCacheId && manifest_url_.is_empty() &&
995 group_id_ == 0);
998 bool AppCacheStorageImpl::
999 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) {
1000 std::vector<AppCacheDatabase::EntryRecord> entries;
1001 if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) {
1002 // Sort them in order of preference, from the preferred_cache first,
1003 // followed by hits from caches that are 'in use', then the rest.
1004 std::sort(entries.begin(), entries.end(),
1005 SortByCachePreference(preferred_cache_id, cache_ids_in_use_));
1007 // Take the first with a valid, non-foreign entry.
1008 std::vector<AppCacheDatabase::EntryRecord>::iterator iter;
1009 for (iter = entries.begin(); iter < entries.end(); ++iter) {
1010 AppCacheDatabase::GroupRecord group_record;
1011 if ((iter->flags & AppCacheEntry::FOREIGN) ||
1012 !database_->FindGroupForCache(iter->cache_id, &group_record)) {
1013 continue;
1015 manifest_url_ = group_record.manifest_url;
1016 group_id_ = group_record.group_id;
1017 entry_ = AppCacheEntry(iter->flags, iter->response_id);
1018 cache_id_ = iter->cache_id;
1019 return true; // We found an exact match.
1022 return false;
1025 bool AppCacheStorageImpl::
1026 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id) {
1027 AppCacheDatabase::NamespaceRecordVector all_intercepts;
1028 AppCacheDatabase::NamespaceRecordVector all_fallbacks;
1029 if (!database_->FindNamespacesForOrigin(
1030 url_.GetOrigin(), &all_intercepts, &all_fallbacks)
1031 || (all_intercepts.empty() && all_fallbacks.empty())) {
1032 return false;
1035 NetworkNamespaceHelper network_namespace_helper(database_);
1036 if (FindNamespaceHelper(preferred_cache_id,
1037 &all_intercepts,
1038 &network_namespace_helper) ||
1039 FindNamespaceHelper(preferred_cache_id,
1040 &all_fallbacks,
1041 &network_namespace_helper)) {
1042 return true;
1044 return false;
1047 bool AppCacheStorageImpl::
1048 FindMainResponseTask::FindNamespaceHelper(
1049 int64 preferred_cache_id,
1050 AppCacheDatabase::NamespaceRecordVector* namespaces,
1051 NetworkNamespaceHelper* network_namespace_helper) {
1052 // Sort them by length, longer matches within the same cache/bucket take
1053 // precedence.
1054 std::sort(namespaces->begin(), namespaces->end(), SortByLength);
1056 NamespaceRecordPtrVector preferred_namespaces;
1057 NamespaceRecordPtrVector inuse_namespaces;
1058 NamespaceRecordPtrVector other_namespaces;
1059 std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter;
1060 for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) {
1061 // Skip those that aren't a match.
1062 if (!iter->namespace_.IsMatch(url_))
1063 continue;
1065 // Skip namespaces where the requested url falls into a network
1066 // namespace of its containing appcache.
1067 if (network_namespace_helper->IsInNetworkNamespace(url_, iter->cache_id))
1068 continue;
1070 // Bin them into one of our three buckets.
1071 if (iter->cache_id == preferred_cache_id)
1072 preferred_namespaces.push_back(&(*iter));
1073 else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end())
1074 inuse_namespaces.push_back(&(*iter));
1075 else
1076 other_namespaces.push_back(&(*iter));
1079 if (FindFirstValidNamespace(preferred_namespaces) ||
1080 FindFirstValidNamespace(inuse_namespaces) ||
1081 FindFirstValidNamespace(other_namespaces))
1082 return true; // We found one.
1084 // We didn't find anything.
1085 return false;
1088 bool AppCacheStorageImpl::
1089 FindMainResponseTask::FindFirstValidNamespace(
1090 const NamespaceRecordPtrVector& namespaces) {
1091 // Take the first with a valid, non-foreign entry.
1092 NamespaceRecordPtrVector::const_iterator iter;
1093 for (iter = namespaces.begin(); iter < namespaces.end(); ++iter) {
1094 AppCacheDatabase::EntryRecord entry_record;
1095 if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url,
1096 &entry_record)) {
1097 AppCacheDatabase::GroupRecord group_record;
1098 if ((entry_record.flags & AppCacheEntry::FOREIGN) ||
1099 !database_->FindGroupForCache(entry_record.cache_id, &group_record)) {
1100 continue;
1102 manifest_url_ = group_record.manifest_url;
1103 group_id_ = group_record.group_id;
1104 cache_id_ = (*iter)->cache_id;
1105 namespace_entry_url_ = (*iter)->namespace_.target_url;
1106 if ((*iter)->namespace_.type == APPCACHE_FALLBACK_NAMESPACE)
1107 fallback_entry_ = AppCacheEntry(entry_record.flags,
1108 entry_record.response_id);
1109 else
1110 entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id);
1111 return true; // We found one.
1114 return false; // We didn't find a match.
1117 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
1118 storage_->CallOnMainResponseFound(
1119 &delegates_, url_, entry_, namespace_entry_url_, fallback_entry_,
1120 cache_id_, group_id_, manifest_url_);
1123 // MarkEntryAsForeignTask -------
1125 class AppCacheStorageImpl::MarkEntryAsForeignTask : public DatabaseTask {
1126 public:
1127 MarkEntryAsForeignTask(
1128 AppCacheStorageImpl* storage, const GURL& url, int64 cache_id)
1129 : DatabaseTask(storage), cache_id_(cache_id), entry_url_(url) {}
1131 // DatabaseTask:
1132 void Run() override;
1133 void RunCompleted() override;
1135 protected:
1136 ~MarkEntryAsForeignTask() override {}
1138 private:
1139 int64 cache_id_;
1140 GURL entry_url_;
1143 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
1144 database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN);
1147 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
1148 DCHECK(storage_->pending_foreign_markings_.front().first == entry_url_ &&
1149 storage_->pending_foreign_markings_.front().second == cache_id_);
1150 storage_->pending_foreign_markings_.pop_front();
1153 // MakeGroupObsoleteTask -------
1155 class AppCacheStorageImpl::MakeGroupObsoleteTask : public DatabaseTask {
1156 public:
1157 MakeGroupObsoleteTask(AppCacheStorageImpl* storage,
1158 AppCacheGroup* group,
1159 int response_code);
1161 // DatabaseTask:
1162 void Run() override;
1163 void RunCompleted() override;
1164 void CancelCompletion() override;
1166 protected:
1167 ~MakeGroupObsoleteTask() override {}
1169 private:
1170 scoped_refptr<AppCacheGroup> group_;
1171 int64 group_id_;
1172 GURL origin_;
1173 bool success_;
1174 int response_code_;
1175 int64 new_origin_usage_;
1176 std::vector<int64> newly_deletable_response_ids_;
1179 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
1180 AppCacheStorageImpl* storage,
1181 AppCacheGroup* group,
1182 int response_code)
1183 : DatabaseTask(storage),
1184 group_(group),
1185 group_id_(group->group_id()),
1186 origin_(group->manifest_url().GetOrigin()),
1187 success_(false),
1188 response_code_(response_code),
1189 new_origin_usage_(-1) {}
1191 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
1192 DCHECK(!success_);
1193 sql::Connection* connection = database_->db_connection();
1194 if (!connection)
1195 return;
1197 sql::Transaction transaction(connection);
1198 if (!transaction.Begin())
1199 return;
1201 AppCacheDatabase::GroupRecord group_record;
1202 if (!database_->FindGroup(group_id_, &group_record)) {
1203 // This group doesn't exists in the database, nothing todo here.
1204 new_origin_usage_ = database_->GetOriginUsage(origin_);
1205 success_ = true;
1206 return;
1209 DCHECK_EQ(group_record.origin, origin_);
1210 success_ = DeleteGroupAndRelatedRecords(database_,
1211 group_id_,
1212 &newly_deletable_response_ids_);
1214 new_origin_usage_ = database_->GetOriginUsage(origin_);
1215 success_ = success_ && transaction.Commit();
1218 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
1219 if (success_) {
1220 group_->set_obsolete(true);
1221 if (!storage_->is_disabled()) {
1222 storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_);
1223 group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
1225 // Also remove from the working set, caches for an 'obsolete' group
1226 // may linger in use, but the group itself cannot be looked up by
1227 // 'manifest_url' in the working set any longer.
1228 storage_->working_set()->RemoveGroup(group_.get());
1231 FOR_EACH_DELEGATE(
1232 delegates_, OnGroupMadeObsolete(group_.get(), success_, response_code_));
1233 group_ = NULL;
1236 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
1237 // Overriden to safely drop our reference to the group
1238 // which is not thread safe refcounted.
1239 DatabaseTask::CancelCompletion();
1240 group_ = NULL;
1243 // GetDeletableResponseIdsTask -------
1245 class AppCacheStorageImpl::GetDeletableResponseIdsTask : public DatabaseTask {
1246 public:
1247 GetDeletableResponseIdsTask(AppCacheStorageImpl* storage, int64 max_rowid)
1248 : DatabaseTask(storage), max_rowid_(max_rowid) {}
1250 // DatabaseTask:
1251 void Run() override;
1252 void RunCompleted() override;
1254 protected:
1255 ~GetDeletableResponseIdsTask() override {}
1257 private:
1258 int64 max_rowid_;
1259 std::vector<int64> response_ids_;
1262 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
1263 const int kSqlLimit = 1000;
1264 database_->GetDeletableResponseIds(&response_ids_, max_rowid_, kSqlLimit);
1265 // TODO(michaeln): retrieve group_ids too
1268 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
1269 if (!response_ids_.empty())
1270 storage_->StartDeletingResponses(response_ids_);
1273 // InsertDeletableResponseIdsTask -------
1275 class AppCacheStorageImpl::InsertDeletableResponseIdsTask
1276 : public DatabaseTask {
1277 public:
1278 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl* storage)
1279 : DatabaseTask(storage) {}
1281 // DatabaseTask:
1282 void Run() override;
1284 std::vector<int64> response_ids_;
1286 protected:
1287 ~InsertDeletableResponseIdsTask() override {}
1290 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
1291 database_->InsertDeletableResponseIds(response_ids_);
1292 // TODO(michaeln): store group_ids too
1295 // DeleteDeletableResponseIdsTask -------
1297 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
1298 : public DatabaseTask {
1299 public:
1300 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl* storage)
1301 : DatabaseTask(storage) {}
1303 // DatabaseTask:
1304 void Run() override;
1306 std::vector<int64> response_ids_;
1308 protected:
1309 ~DeleteDeletableResponseIdsTask() override {}
1312 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1313 database_->DeleteDeletableResponseIds(response_ids_);
1316 // LazyUpdateLastAccessTimeTask -------
1318 class AppCacheStorageImpl::LazyUpdateLastAccessTimeTask
1319 : public DatabaseTask {
1320 public:
1321 LazyUpdateLastAccessTimeTask(
1322 AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time)
1323 : DatabaseTask(storage), group_id_(group->group_id()),
1324 last_access_time_(time) {
1325 storage->NotifyStorageAccessed(group->manifest_url().GetOrigin());
1328 // DatabaseTask:
1329 void Run() override;
1330 void RunCompleted() override;
1332 protected:
1333 ~LazyUpdateLastAccessTimeTask() override {}
1335 private:
1336 int64 group_id_;
1337 base::Time last_access_time_;
1340 void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::Run() {
1341 tracked_objects::ScopedTracker tracking_profile(
1342 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1343 "AppCacheStorageImpl::LazyUpdateLastAccessTimeTask"));
1344 database_->LazyUpdateLastAccessTime(group_id_, last_access_time_);
1347 void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::RunCompleted() {
1348 storage_->LazilyCommitLastAccessTimes();
1351 // CommitLastAccessTimesTask -------
1353 class AppCacheStorageImpl::CommitLastAccessTimesTask
1354 : public DatabaseTask {
1355 public:
1356 CommitLastAccessTimesTask(AppCacheStorageImpl* storage)
1357 : DatabaseTask(storage) {}
1359 // DatabaseTask:
1360 void Run() override {
1361 tracked_objects::ScopedTracker tracking_profile(
1362 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1363 "AppCacheStorageImpl::CommitLastAccessTimesTask"));
1364 database_->CommitLazyLastAccessTimes();
1367 protected:
1368 ~CommitLastAccessTimesTask() override {}
1371 // UpdateEvictionTimes -------
1373 class AppCacheStorageImpl::UpdateEvictionTimesTask
1374 : public DatabaseTask {
1375 public:
1376 UpdateEvictionTimesTask(
1377 AppCacheStorageImpl* storage, AppCacheGroup* group)
1378 : DatabaseTask(storage), group_id_(group->group_id()),
1379 last_full_update_check_time_(group->last_full_update_check_time()),
1380 first_evictable_error_time_(group->first_evictable_error_time()) {
1383 // DatabaseTask:
1384 void Run() override;
1386 protected:
1387 ~UpdateEvictionTimesTask() override {}
1389 private:
1390 int64 group_id_;
1391 base::Time last_full_update_check_time_;
1392 base::Time first_evictable_error_time_;
1395 void AppCacheStorageImpl::UpdateEvictionTimesTask::Run() {
1396 tracked_objects::ScopedTracker tracking_profile(
1397 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1398 "AppCacheStorageImpl::UpdateEvictionTimes"));
1399 database_->UpdateEvictionTimes(group_id_,
1400 last_full_update_check_time_,
1401 first_evictable_error_time_);
1404 // AppCacheStorageImpl ---------------------------------------------------
1406 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl* service)
1407 : AppCacheStorage(service),
1408 is_incognito_(false),
1409 is_response_deletion_scheduled_(false),
1410 did_start_deleting_responses_(false),
1411 last_deletable_response_rowid_(0),
1412 database_(NULL),
1413 is_disabled_(false),
1414 weak_factory_(this) {
1417 AppCacheStorageImpl::~AppCacheStorageImpl() {
1418 std::for_each(pending_quota_queries_.begin(),
1419 pending_quota_queries_.end(),
1420 std::mem_fun(&DatabaseTask::CancelCompletion));
1421 std::for_each(scheduled_database_tasks_.begin(),
1422 scheduled_database_tasks_.end(),
1423 std::mem_fun(&DatabaseTask::CancelCompletion));
1425 if (database_ &&
1426 !db_thread_->PostTask(
1427 FROM_HERE,
1428 base::Bind(&ClearSessionOnlyOrigins,
1429 database_,
1430 make_scoped_refptr(service_->special_storage_policy()),
1431 service()->force_keep_session_state()))) {
1432 delete database_;
1434 database_ = NULL; // So no further database tasks can be scheduled.
1437 void AppCacheStorageImpl::Initialize(
1438 const base::FilePath& cache_directory,
1439 const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
1440 const scoped_refptr<base::SingleThreadTaskRunner>& cache_thread) {
1441 DCHECK(db_thread.get());
1443 cache_directory_ = cache_directory;
1444 is_incognito_ = cache_directory_.empty();
1446 base::FilePath db_file_path;
1447 if (!is_incognito_)
1448 db_file_path = cache_directory_.Append(kAppCacheDatabaseName);
1449 database_ = new AppCacheDatabase(db_file_path);
1451 db_thread_ = db_thread;
1452 cache_thread_ = cache_thread;
1454 scoped_refptr<InitTask> task(new InitTask(this));
1455 task->Schedule();
1458 void AppCacheStorageImpl::Disable() {
1459 if (is_disabled_)
1460 return;
1461 VLOG(1) << "Disabling appcache storage.";
1462 is_disabled_ = true;
1463 ClearUsageMapAndNotify();
1464 working_set()->Disable();
1465 if (disk_cache_)
1466 disk_cache_->Disable();
1467 scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this));
1468 task->Schedule();
1471 void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) {
1472 DCHECK(delegate);
1473 scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this));
1474 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1475 task->Schedule();
1478 void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) {
1479 DCHECK(delegate);
1480 if (is_disabled_) {
1481 delegate->OnCacheLoaded(NULL, id);
1482 return;
1485 AppCache* cache = working_set_.GetCache(id);
1486 if (cache) {
1487 delegate->OnCacheLoaded(cache, id);
1488 if (cache->owning_group()) {
1489 scoped_refptr<DatabaseTask> update_task(
1490 new LazyUpdateLastAccessTimeTask(
1491 this, cache->owning_group(), base::Time::Now()));
1492 update_task->Schedule();
1494 return;
1496 scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id));
1497 if (task.get()) {
1498 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1499 return;
1501 task = new CacheLoadTask(id, this);
1502 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1503 task->Schedule();
1504 pending_cache_loads_[id] = task.get();
1507 void AppCacheStorageImpl::LoadOrCreateGroup(
1508 const GURL& manifest_url, Delegate* delegate) {
1509 DCHECK(delegate);
1510 if (is_disabled_) {
1511 delegate->OnGroupLoaded(NULL, manifest_url);
1512 return;
1515 AppCacheGroup* group = working_set_.GetGroup(manifest_url);
1516 if (group) {
1517 delegate->OnGroupLoaded(group, manifest_url);
1518 scoped_refptr<DatabaseTask> update_task(
1519 new LazyUpdateLastAccessTimeTask(
1520 this, group, base::Time::Now()));
1521 update_task->Schedule();
1522 return;
1525 scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url));
1526 if (task.get()) {
1527 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1528 return;
1531 if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) {
1532 // No need to query the database, return a new group immediately.
1533 scoped_refptr<AppCacheGroup> group(new AppCacheGroup(
1534 this, manifest_url, NewGroupId()));
1535 delegate->OnGroupLoaded(group.get(), manifest_url);
1536 return;
1539 task = new GroupLoadTask(manifest_url, this);
1540 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1541 task->Schedule();
1542 pending_group_loads_[manifest_url] = task.get();
1545 void AppCacheStorageImpl::StoreGroupAndNewestCache(
1546 AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) {
1547 // TODO(michaeln): distinguish between a simple update of an existing
1548 // cache that just adds new master entry(s), and the insertion of a
1549 // whole new cache. The StoreGroupAndCacheTask as written will handle
1550 // the simple update case in a very heavy weight way (delete all and
1551 // the reinsert all over again).
1552 DCHECK(group && delegate && newest_cache);
1553 scoped_refptr<StoreGroupAndCacheTask> task(
1554 new StoreGroupAndCacheTask(this, group, newest_cache));
1555 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1556 task->GetQuotaThenSchedule();
1558 // TODO(michaeln): histogram is fishing for clues to crbug/95101
1559 if (!newest_cache->GetEntry(group->manifest_url())) {
1560 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
1561 AppCacheHistograms::CALLSITE_3);
1565 void AppCacheStorageImpl::FindResponseForMainRequest(
1566 const GURL& url, const GURL& preferred_manifest_url,
1567 Delegate* delegate) {
1568 DCHECK(delegate);
1570 const GURL* url_ptr = &url;
1571 GURL url_no_ref;
1572 if (url.has_ref()) {
1573 GURL::Replacements replacements;
1574 replacements.ClearRef();
1575 url_no_ref = url.ReplaceComponents(replacements);
1576 url_ptr = &url_no_ref;
1579 const GURL origin = url.GetOrigin();
1581 // First look in our working set for a direct hit without having to query
1582 // the database.
1583 const AppCacheWorkingSet::GroupMap* groups_in_use =
1584 working_set()->GetGroupsInOrigin(origin);
1585 if (groups_in_use) {
1586 if (!preferred_manifest_url.is_empty()) {
1587 AppCacheWorkingSet::GroupMap::const_iterator found =
1588 groups_in_use->find(preferred_manifest_url);
1589 if (found != groups_in_use->end() &&
1590 FindResponseForMainRequestInGroup(
1591 found->second, *url_ptr, delegate)) {
1592 return;
1594 } else {
1595 for (AppCacheWorkingSet::GroupMap::const_iterator it =
1596 groups_in_use->begin();
1597 it != groups_in_use->end(); ++it) {
1598 if (FindResponseForMainRequestInGroup(
1599 it->second, *url_ptr, delegate)) {
1600 return;
1606 if (IsInitTaskComplete() && usage_map_.find(origin) == usage_map_.end()) {
1607 // No need to query the database, return async'ly but without going thru
1608 // the DB thread.
1609 scoped_refptr<AppCacheGroup> no_group;
1610 scoped_refptr<AppCache> no_cache;
1611 ScheduleSimpleTask(
1612 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
1613 weak_factory_.GetWeakPtr(), url, AppCacheEntry(), no_group,
1614 no_cache,
1615 make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
1616 return;
1619 // We have to query the database, schedule a database task to do so.
1620 scoped_refptr<FindMainResponseTask> task(
1621 new FindMainResponseTask(this, *url_ptr, preferred_manifest_url,
1622 groups_in_use));
1623 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1624 task->Schedule();
1627 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
1628 AppCacheGroup* group, const GURL& url, Delegate* delegate) {
1629 AppCache* cache = group->newest_complete_cache();
1630 if (group->is_obsolete() || !cache)
1631 return false;
1633 AppCacheEntry* entry = cache->GetEntry(url);
1634 if (!entry || entry->IsForeign())
1635 return false;
1637 ScheduleSimpleTask(
1638 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
1639 weak_factory_.GetWeakPtr(), url, *entry,
1640 make_scoped_refptr(group), make_scoped_refptr(cache),
1641 make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
1642 return true;
1645 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
1646 const GURL& url,
1647 const AppCacheEntry& found_entry,
1648 scoped_refptr<AppCacheGroup> group,
1649 scoped_refptr<AppCache> cache,
1650 scoped_refptr<DelegateReference> delegate_ref) {
1651 if (delegate_ref->delegate) {
1652 DelegateReferenceVector delegates(1, delegate_ref);
1653 CallOnMainResponseFound(
1654 &delegates, url, found_entry,
1655 GURL(), AppCacheEntry(),
1656 cache.get() ? cache->cache_id() : kAppCacheNoCacheId,
1657 group.get() ? group->group_id() : kAppCacheNoCacheId,
1658 group.get() ? group->manifest_url() : GURL());
1662 void AppCacheStorageImpl::CallOnMainResponseFound(
1663 DelegateReferenceVector* delegates,
1664 const GURL& url, const AppCacheEntry& entry,
1665 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
1666 int64 cache_id, int64 group_id, const GURL& manifest_url) {
1667 FOR_EACH_DELEGATE(
1668 (*delegates),
1669 OnMainResponseFound(url, entry,
1670 namespace_entry_url, fallback_entry,
1671 cache_id, group_id, manifest_url));
1674 void AppCacheStorageImpl::FindResponseForSubRequest(
1675 AppCache* cache, const GURL& url,
1676 AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
1677 bool* found_network_namespace) {
1678 DCHECK(cache && cache->is_complete());
1680 // When a group is forcibly deleted, all subresource loads for pages
1681 // using caches in the group will result in a synthesized network errors.
1682 // Forcible deletion is not a function that is covered by the HTML5 spec.
1683 if (cache->owning_group()->is_being_deleted()) {
1684 *found_entry = AppCacheEntry();
1685 *found_fallback_entry = AppCacheEntry();
1686 *found_network_namespace = false;
1687 return;
1690 GURL fallback_namespace_not_used;
1691 GURL intercept_namespace_not_used;
1692 cache->FindResponseForRequest(
1693 url, found_entry, &intercept_namespace_not_used,
1694 found_fallback_entry, &fallback_namespace_not_used,
1695 found_network_namespace);
1698 void AppCacheStorageImpl::MarkEntryAsForeign(
1699 const GURL& entry_url, int64 cache_id) {
1700 AppCache* cache = working_set_.GetCache(cache_id);
1701 if (cache) {
1702 AppCacheEntry* entry = cache->GetEntry(entry_url);
1703 DCHECK(entry);
1704 if (entry)
1705 entry->add_types(AppCacheEntry::FOREIGN);
1707 scoped_refptr<MarkEntryAsForeignTask> task(
1708 new MarkEntryAsForeignTask(this, entry_url, cache_id));
1709 task->Schedule();
1710 pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id));
1713 void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup* group,
1714 Delegate* delegate,
1715 int response_code) {
1716 DCHECK(group && delegate);
1717 scoped_refptr<MakeGroupObsoleteTask> task(
1718 new MakeGroupObsoleteTask(this, group, response_code));
1719 task->AddDelegate(GetOrCreateDelegateReference(delegate));
1720 task->Schedule();
1723 void AppCacheStorageImpl::StoreEvictionTimes(AppCacheGroup* group) {
1724 scoped_refptr<UpdateEvictionTimesTask> task(
1725 new UpdateEvictionTimesTask(this, group));
1726 task->Schedule();
1729 AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader(
1730 const GURL& manifest_url, int64 group_id, int64 response_id) {
1731 return new AppCacheResponseReader(response_id, group_id, disk_cache());
1734 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter(
1735 const GURL& manifest_url, int64 group_id) {
1736 return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
1739 AppCacheResponseMetadataWriter*
1740 AppCacheStorageImpl::CreateResponseMetadataWriter(int64 group_id,
1741 int64 response_id) {
1742 return new AppCacheResponseMetadataWriter(response_id, group_id,
1743 disk_cache());
1746 void AppCacheStorageImpl::DoomResponses(
1747 const GURL& manifest_url, const std::vector<int64>& response_ids) {
1748 if (response_ids.empty())
1749 return;
1751 // Start deleting them from the disk cache lazily.
1752 StartDeletingResponses(response_ids);
1754 // Also schedule a database task to record these ids in the
1755 // deletable responses table.
1756 // TODO(michaeln): There is a race here. If the browser crashes
1757 // prior to committing these rows to the database and prior to us
1758 // having deleted them from the disk cache, we'll never delete them.
1759 scoped_refptr<InsertDeletableResponseIdsTask> task(
1760 new InsertDeletableResponseIdsTask(this));
1761 task->response_ids_ = response_ids;
1762 task->Schedule();
1765 void AppCacheStorageImpl::DeleteResponses(
1766 const GURL& manifest_url, const std::vector<int64>& response_ids) {
1767 if (response_ids.empty())
1768 return;
1769 StartDeletingResponses(response_ids);
1772 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
1773 // Only if we haven't already begun.
1774 if (!did_start_deleting_responses_) {
1775 scoped_refptr<GetDeletableResponseIdsTask> task(
1776 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
1777 task->Schedule();
1781 void AppCacheStorageImpl::StartDeletingResponses(
1782 const std::vector<int64>& response_ids) {
1783 DCHECK(!response_ids.empty());
1784 did_start_deleting_responses_ = true;
1785 deletable_response_ids_.insert(
1786 deletable_response_ids_.end(),
1787 response_ids.begin(), response_ids.end());
1788 if (!is_response_deletion_scheduled_)
1789 ScheduleDeleteOneResponse();
1792 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
1793 DCHECK(!is_response_deletion_scheduled_);
1794 const base::TimeDelta kBriefDelay = base::TimeDelta::FromMilliseconds(10);
1795 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1796 FROM_HERE, base::Bind(&AppCacheStorageImpl::DeleteOneResponse,
1797 weak_factory_.GetWeakPtr()),
1798 kBriefDelay);
1799 is_response_deletion_scheduled_ = true;
1802 void AppCacheStorageImpl::DeleteOneResponse() {
1803 DCHECK(is_response_deletion_scheduled_);
1804 DCHECK(!deletable_response_ids_.empty());
1806 if (!disk_cache()) {
1807 DCHECK(is_disabled_);
1808 deletable_response_ids_.clear();
1809 deleted_response_ids_.clear();
1810 is_response_deletion_scheduled_ = false;
1811 return;
1814 // TODO(michaeln): add group_id to DoomEntry args
1815 int64 id = deletable_response_ids_.front();
1816 int rv = disk_cache_->DoomEntry(
1817 id, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse,
1818 base::Unretained(this)));
1819 if (rv != net::ERR_IO_PENDING)
1820 OnDeletedOneResponse(rv);
1823 void AppCacheStorageImpl::OnDeletedOneResponse(int rv) {
1824 is_response_deletion_scheduled_ = false;
1825 if (is_disabled_)
1826 return;
1828 int64 id = deletable_response_ids_.front();
1829 deletable_response_ids_.pop_front();
1830 if (rv != net::ERR_ABORTED)
1831 deleted_response_ids_.push_back(id);
1833 const size_t kBatchSize = 50U;
1834 if (deleted_response_ids_.size() >= kBatchSize ||
1835 deletable_response_ids_.empty()) {
1836 scoped_refptr<DeleteDeletableResponseIdsTask> task(
1837 new DeleteDeletableResponseIdsTask(this));
1838 task->response_ids_.swap(deleted_response_ids_);
1839 task->Schedule();
1842 if (deletable_response_ids_.empty()) {
1843 scoped_refptr<GetDeletableResponseIdsTask> task(
1844 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
1845 task->Schedule();
1846 return;
1849 ScheduleDeleteOneResponse();
1852 AppCacheStorageImpl::CacheLoadTask*
1853 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) {
1854 PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id);
1855 if (found != pending_cache_loads_.end())
1856 return found->second;
1857 return NULL;
1860 AppCacheStorageImpl::GroupLoadTask*
1861 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL& manifest_url) {
1862 PendingGroupLoads::iterator found = pending_group_loads_.find(manifest_url);
1863 if (found != pending_group_loads_.end())
1864 return found->second;
1865 return NULL;
1868 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
1869 int64 cache_id, std::vector<GURL>* urls) {
1870 PendingForeignMarkings::iterator iter = pending_foreign_markings_.begin();
1871 while (iter != pending_foreign_markings_.end()) {
1872 if (iter->second == cache_id)
1873 urls->push_back(iter->first);
1874 ++iter;
1878 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure& task) {
1879 pending_simple_tasks_.push_back(task);
1880 base::ThreadTaskRunnerHandle::Get()->PostTask(
1881 FROM_HERE, base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask,
1882 weak_factory_.GetWeakPtr()));
1885 void AppCacheStorageImpl::RunOnePendingSimpleTask() {
1886 DCHECK(!pending_simple_tasks_.empty());
1887 base::Closure task = pending_simple_tasks_.front();
1888 pending_simple_tasks_.pop_front();
1889 task.Run();
1892 AppCacheDiskCache* AppCacheStorageImpl::disk_cache() {
1893 DCHECK(IsInitTaskComplete());
1895 if (is_disabled_)
1896 return NULL;
1898 if (!disk_cache_) {
1899 int rv = net::OK;
1900 disk_cache_.reset(new AppCacheDiskCache);
1901 if (is_incognito_) {
1902 rv = disk_cache_->InitWithMemBackend(
1903 kMaxMemDiskCacheSize,
1904 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
1905 base::Unretained(this)));
1906 } else {
1907 rv = disk_cache_->InitWithDiskBackend(
1908 cache_directory_.Append(kDiskCacheDirectoryName),
1909 kMaxDiskCacheSize,
1910 false,
1911 cache_thread_.get(),
1912 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
1913 base::Unretained(this)));
1916 if (rv != net::ERR_IO_PENDING)
1917 OnDiskCacheInitialized(rv);
1919 return disk_cache_.get();
1922 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) {
1923 if (rv != net::OK) {
1924 LOG(ERROR) << "Failed to open the appcache diskcache.";
1925 AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR);
1927 // We're unable to open the disk cache, this is a fatal error that we can't
1928 // really recover from. We handle it by temporarily disabling the appcache
1929 // deleting the directory on disk and reinitializing the appcache system.
1930 Disable();
1931 if (rv != net::ERR_ABORTED)
1932 DeleteAndStartOver();
1936 void AppCacheStorageImpl::DeleteAndStartOver() {
1937 DCHECK(is_disabled_);
1938 if (!is_incognito_) {
1939 VLOG(1) << "Deleting existing appcache data and starting over.";
1940 // We can have tasks in flight to close file handles on both the db
1941 // and cache threads, we need to allow those tasks to cycle thru
1942 // prior to deleting the files and calling reinit.
1943 cache_thread_->PostTaskAndReply(
1944 FROM_HERE,
1945 base::Bind(&base::DoNothing),
1946 base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2,
1947 weak_factory_.GetWeakPtr()));
1951 void AppCacheStorageImpl::DeleteAndStartOverPart2() {
1952 db_thread_->PostTaskAndReply(
1953 FROM_HERE,
1954 base::Bind(base::IgnoreResult(&base::DeleteFile), cache_directory_, true),
1955 base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize,
1956 weak_factory_.GetWeakPtr()));
1959 void AppCacheStorageImpl::CallScheduleReinitialize() {
1960 service_->ScheduleReinitialize();
1961 // note: 'this' may be deleted at this point.
1964 void AppCacheStorageImpl::LazilyCommitLastAccessTimes() {
1965 if (lazy_commit_timer_.IsRunning())
1966 return;
1967 const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5);
1968 lazy_commit_timer_.Start(
1969 FROM_HERE, kDelay,
1970 base::Bind(&AppCacheStorageImpl::OnLazyCommitTimer,
1971 weak_factory_.GetWeakPtr()));
1974 void AppCacheStorageImpl::OnLazyCommitTimer() {
1975 lazy_commit_timer_.Stop();
1976 if (is_disabled())
1977 return;
1978 scoped_refptr<DatabaseTask> task(new CommitLastAccessTimesTask(this));
1979 task->Schedule();
1982 } // namespace content