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"
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"
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");
51 // Helpers for clearing data from the AppCacheDatabase.
52 bool DeleteGroupAndRelatedRecords(AppCacheDatabase
* database
,
54 std::vector
<int64
>* deletable_response_ids
) {
55 AppCacheDatabase::CacheRecord cache_record
;
57 if (database
->FindCacheForGroup(group_id
, &cache_record
)) {
58 database
->FindResponseIdsForCacheAsVector(cache_record
.cache_id
,
59 deletable_response_ids
);
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
);
68 NOTREACHED() << "A existing group without a cache is unexpected";
69 success
= database
->DeleteGroup(group_id
);
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
)
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
)
94 std::set
<GURL
> origins
;
95 database
->FindOriginsWithGroups(&origins
);
97 return; // nothing to delete
99 sql::Connection
* connection
= database
->db_connection();
101 NOTREACHED() << "Missing database connection.";
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
))
110 if (special_storage_policy
->IsStorageProtected(*origin
))
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";
122 std::vector
<int64
> deletable_response_ids
;
123 bool success
= DeleteGroupAndRelatedRecords(database
,
125 &deletable_response_ids
);
126 success
= success
&& transaction
.Commit();
134 // DatabaseTask -----------------------------------------
136 class AppCacheStorageImpl::DatabaseTask
137 : public base::RefCountedThreadSafe
<DatabaseTask
> {
139 explicit DatabaseTask(AppCacheStorageImpl
* 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.
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();
171 friend class base::RefCountedThreadSafe
<DatabaseTask
>;
172 virtual ~DatabaseTask() {}
174 AppCacheStorageImpl
* storage_
;
175 AppCacheDatabase
* database_
;
176 DelegateReferenceVector delegates_
;
179 void CallRun(base::TimeTicks schedule_time
);
180 void CallRunCompleted(base::TimeTicks schedule_time
);
183 scoped_refptr
<base::SingleThreadTaskRunner
> io_thread_
;
186 void AppCacheStorageImpl::DatabaseTask::Schedule() {
188 DCHECK(io_thread_
->BelongsToCurrentThread());
189 if (!storage_
->database_
)
192 if (storage_
->db_thread_
->PostTask(
194 base::Bind(&DatabaseTask::CallRun
, this, base::TimeTicks::Now()))) {
195 storage_
->scheduled_database_tasks_
.push_back(this);
197 NOTREACHED() << "Thread for database tasks is not running.";
201 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
202 DCHECK(io_thread_
->BelongsToCurrentThread());
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();
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(
224 base::Bind(&DatabaseTask::OnFatalError
, this));
227 io_thread_
->PostTask(
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
);
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();
243 AppCacheHistograms::AddCompletionRunTimeSample(
244 base::TimeTicks::Now() - run_time
);
249 void AppCacheStorageImpl::DatabaseTask::OnFatalError() {
251 DCHECK(io_thread_
->BelongsToCurrentThread());
253 storage_
->DeleteAndStartOver();
259 class AppCacheStorageImpl::InitTask
: public DatabaseTask
{
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_
) {
267 storage
->cache_directory_
.Append(kAppCacheDatabaseName
);
268 disk_cache_directory_
=
269 storage
->cache_directory_
.Append(kDiskCacheDirectoryName
);
275 void RunCompleted() override
;
278 ~InitTask() override
{}
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.
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(
321 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses
,
322 storage_
->weak_factory_
.GetWeakPtr()),
326 if (storage_
->service()->quota_client())
327 storage_
->service()->quota_client()->NotifyAppCacheReady();
330 // DisableDatabaseTask -------
332 class AppCacheStorageImpl::DisableDatabaseTask
: public DatabaseTask
{
334 explicit DisableDatabaseTask(AppCacheStorageImpl
* storage
)
335 : DatabaseTask(storage
) {}
338 void Run() override
{ database_
->Disable(); }
341 ~DisableDatabaseTask() override
{}
344 // GetAllInfoTask -------
346 class AppCacheStorageImpl::GetAllInfoTask
: public DatabaseTask
{
348 explicit GetAllInfoTask(AppCacheStorageImpl
* storage
)
349 : DatabaseTask(storage
),
350 info_collection_(new AppCacheInfoCollection()) {
355 void RunCompleted() override
;
358 ~GetAllInfoTask() override
{}
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
);
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
{
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(
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
);
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
);
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
);
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
);
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
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
{
507 CacheLoadTask(int64 cache_id
, AppCacheStorageImpl
* storage
)
508 : StoreOrLoadTask(storage
), cache_id_(cache_id
),
513 void RunCompleted() override
;
516 ~CacheLoadTask() override
{}
523 void AppCacheStorageImpl::CacheLoadTask::Run() {
524 tracked_objects::ScopedTracker
tracking_profile(
525 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::CacheLoadTask"));
527 database_
->FindCache(cache_id_
, &cache_record_
) &&
528 database_
->FindGroup(cache_record_
.group_id
, &group_record_
) &&
529 FindRelatedCacheRecords(cache_id_
);
532 database_
->LazyUpdateLastAccessTime(group_record_
.group_id
,
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
{
552 GroupLoadTask(GURL manifest_url
, AppCacheStorageImpl
* storage
)
553 : StoreOrLoadTask(storage
), manifest_url_(manifest_url
),
558 void RunCompleted() override
;
561 ~GroupLoadTask() override
{}
568 void AppCacheStorageImpl::GroupLoadTask::Run() {
569 tracked_objects::ScopedTracker
tracking_profile(
570 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::GroupLoadTask"));
572 database_
->FindGroupForManifestUrl(manifest_url_
, &group_record_
) &&
573 database_
->FindCacheForGroup(group_record_
.group_id
, &cache_record_
) &&
574 FindRelatedCacheRecords(cache_record_
.cache_id
);
577 database_
->LazyUpdateLastAccessTime(group_record_
.group_id
,
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()) {
587 storage_
->LazilyCommitLastAccessTimes();
588 DCHECK(group_record_
.manifest_url
== manifest_url_
);
589 CreateCacheAndGroupFromRecords(&cache
, &group
);
591 group
= storage_
->working_set_
.GetGroup(manifest_url_
);
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
{
605 StoreGroupAndCacheTask(AppCacheStorageImpl
* storage
, AppCacheGroup
* group
,
606 AppCache
* newest_cache
);
608 void GetQuotaThenSchedule();
609 void OnQuotaCallback(storage::QuotaStatusCode status
,
615 void RunCompleted() override
;
616 void CancelCompletion() override
;
619 ~StoreGroupAndCacheTask() override
{}
622 scoped_refptr
<AppCacheGroup
> group_
;
623 scoped_refptr
<AppCache
> cache_
;
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(
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()) {
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
;
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
,
680 if (status
== storage::kQuotaStatusOk
)
681 space_available_
= std::max(static_cast<int64
>(0), quota
- usage
);
683 space_available_
= 0;
684 storage_
->pending_quota_queries_
.erase(this);
689 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
691 sql::Connection
* connection
= database_
->db_connection();
695 sql::Transaction
transaction(connection
);
696 if (!transaction
.Begin())
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
);
704 group_record_
.creation_time
= base::Time::Now();
705 group_record_
.last_access_time
= base::Time::Now();
706 success_
= database_
->InsertGroup(&group_record_
);
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
,
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
);
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
);
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
750 NOTREACHED() << "A existing group without a cache is unexpected";
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_
);
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();
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;
780 success_
= transaction
.Commit();
784 // Check limits based on the space availbable given to us via the
786 int64 delta
= new_origin_usage_
- old_origin_usage
;
787 if (delta
> space_available_
) {
788 would_exceed_quota_
= true;
793 success_
= transaction
.Commit();
796 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
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_
);
810 OnGroupAndNewestCacheStored(
811 group_
.get(), cache_
.get(), success_
, would_exceed_quota_
));
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();
827 // FindMainResponseTask -------
829 // Helpers for FindMainResponseTask::Run()
831 class SortByCachePreference
832 : public std::binary_function
<
833 AppCacheDatabase::EntryRecord
,
834 AppCacheDatabase::EntryRecord
,
837 SortByCachePreference(int64 preferred_id
, const std::set
<int64
>& in_use_ids
)
838 : preferred_id_(preferred_id
), in_use_ids_(in_use_ids
) {
841 const AppCacheDatabase::EntryRecord
& lhs
,
842 const AppCacheDatabase::EntryRecord
& rhs
) {
843 return compute_value(lhs
) > compute_value(rhs
);
846 int compute_value(const AppCacheDatabase::EntryRecord
& entry
) {
847 if (entry
.cache_id
== preferred_id_
)
849 else if (in_use_ids_
.find(entry
.cache_id
) != in_use_ids_
.end())
854 const std::set
<int64
>& in_use_ids_
;
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
{
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()));
875 GetOnlineWhiteListForCache(cache_id
, &result
.first
->second
);
876 return AppCache::FindNamespace(result
.first
->second
, url
) != NULL
;
880 void GetOnlineWhiteListForCache(
881 int64 cache_id
, AppCacheNamespaceVector
* namespaces
) {
882 DCHECK(namespaces
&& namespaces
->empty());
883 typedef std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
885 WhiteListVector records
;
886 if (!database_
->FindOnlineWhiteListForCache(cache_id
, &records
))
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
));
898 typedef std::map
<int64
, AppCacheNamespaceVector
> WhiteListMap
;
899 WhiteListMap namespaces_map_
;
900 AppCacheDatabase
* database_
;
905 class AppCacheStorageImpl::FindMainResponseTask
: public DatabaseTask
{
907 FindMainResponseTask(AppCacheStorageImpl
* storage
,
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) {
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
)
922 cache_ids_in_use_
.insert(cache
->cache_id());
929 void RunCompleted() override
;
932 ~FindMainResponseTask() override
{}
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
);
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_
;
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
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() &&
993 // We didn't find anything.
994 DCHECK(cache_id_
== kAppCacheNoCacheId
&& manifest_url_
.is_empty() &&
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
)) {
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.
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())) {
1035 NetworkNamespaceHelper
network_namespace_helper(database_
);
1036 if (FindNamespaceHelper(preferred_cache_id
,
1038 &network_namespace_helper
) ||
1039 FindNamespaceHelper(preferred_cache_id
,
1041 &network_namespace_helper
)) {
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
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_
))
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
))
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
));
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.
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
,
1097 AppCacheDatabase::GroupRecord group_record
;
1098 if ((entry_record
.flags
& AppCacheEntry::FOREIGN
) ||
1099 !database_
->FindGroupForCache(entry_record
.cache_id
, &group_record
)) {
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
);
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
{
1127 MarkEntryAsForeignTask(
1128 AppCacheStorageImpl
* storage
, const GURL
& url
, int64 cache_id
)
1129 : DatabaseTask(storage
), cache_id_(cache_id
), entry_url_(url
) {}
1132 void Run() override
;
1133 void RunCompleted() override
;
1136 ~MarkEntryAsForeignTask() override
{}
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
{
1157 MakeGroupObsoleteTask(AppCacheStorageImpl
* storage
,
1158 AppCacheGroup
* group
,
1162 void Run() override
;
1163 void RunCompleted() override
;
1164 void CancelCompletion() override
;
1167 ~MakeGroupObsoleteTask() override
{}
1170 scoped_refptr
<AppCacheGroup
> group_
;
1175 int64 new_origin_usage_
;
1176 std::vector
<int64
> newly_deletable_response_ids_
;
1179 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
1180 AppCacheStorageImpl
* storage
,
1181 AppCacheGroup
* group
,
1183 : DatabaseTask(storage
),
1185 group_id_(group
->group_id()),
1186 origin_(group
->manifest_url().GetOrigin()),
1188 response_code_(response_code
),
1189 new_origin_usage_(-1) {}
1191 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
1193 sql::Connection
* connection
= database_
->db_connection();
1197 sql::Transaction
transaction(connection
);
1198 if (!transaction
.Begin())
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_
);
1209 DCHECK_EQ(group_record
.origin
, origin_
);
1210 success_
= DeleteGroupAndRelatedRecords(database_
,
1212 &newly_deletable_response_ids_
);
1214 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1215 success_
= success_
&& transaction
.Commit();
1218 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
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());
1232 delegates_
, OnGroupMadeObsolete(group_
.get(), success_
, response_code_
));
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();
1243 // GetDeletableResponseIdsTask -------
1245 class AppCacheStorageImpl::GetDeletableResponseIdsTask
: public DatabaseTask
{
1247 GetDeletableResponseIdsTask(AppCacheStorageImpl
* storage
, int64 max_rowid
)
1248 : DatabaseTask(storage
), max_rowid_(max_rowid
) {}
1251 void Run() override
;
1252 void RunCompleted() override
;
1255 ~GetDeletableResponseIdsTask() override
{}
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
{
1278 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1279 : DatabaseTask(storage
) {}
1282 void Run() override
;
1284 std::vector
<int64
> response_ids_
;
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
{
1300 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1301 : DatabaseTask(storage
) {}
1304 void Run() override
;
1306 std::vector
<int64
> response_ids_
;
1309 ~DeleteDeletableResponseIdsTask() override
{}
1312 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1313 database_
->DeleteDeletableResponseIds(response_ids_
);
1316 // LazyUpdateLastAccessTimeTask -------
1318 class AppCacheStorageImpl::LazyUpdateLastAccessTimeTask
1319 : public DatabaseTask
{
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());
1329 void Run() override
;
1330 void RunCompleted() override
;
1333 ~LazyUpdateLastAccessTimeTask() override
{}
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
{
1356 CommitLastAccessTimesTask(AppCacheStorageImpl
* storage
)
1357 : DatabaseTask(storage
) {}
1360 void Run() override
{
1361 tracked_objects::ScopedTracker
tracking_profile(
1362 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1363 "AppCacheStorageImpl::CommitLastAccessTimesTask"));
1364 database_
->CommitLazyLastAccessTimes();
1368 ~CommitLastAccessTimesTask() override
{}
1371 // UpdateEvictionTimes -------
1373 class AppCacheStorageImpl::UpdateEvictionTimesTask
1374 : public DatabaseTask
{
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()) {
1384 void Run() override
;
1387 ~UpdateEvictionTimesTask() override
{}
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),
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
));
1426 !db_thread_
->PostTask(
1428 base::Bind(&ClearSessionOnlyOrigins
,
1430 make_scoped_refptr(service_
->special_storage_policy()),
1431 service()->force_keep_session_state()))) {
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
;
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));
1458 void AppCacheStorageImpl::Disable() {
1461 VLOG(1) << "Disabling appcache storage.";
1462 is_disabled_
= true;
1463 ClearUsageMapAndNotify();
1464 working_set()->Disable();
1466 disk_cache_
->Disable();
1467 scoped_refptr
<DisableDatabaseTask
> task(new DisableDatabaseTask(this));
1471 void AppCacheStorageImpl::GetAllInfo(Delegate
* delegate
) {
1473 scoped_refptr
<GetAllInfoTask
> task(new GetAllInfoTask(this));
1474 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1478 void AppCacheStorageImpl::LoadCache(int64 id
, Delegate
* delegate
) {
1481 delegate
->OnCacheLoaded(NULL
, id
);
1485 AppCache
* cache
= working_set_
.GetCache(id
);
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();
1496 scoped_refptr
<CacheLoadTask
> task(GetPendingCacheLoadTask(id
));
1498 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1501 task
= new CacheLoadTask(id
, this);
1502 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1504 pending_cache_loads_
[id
] = task
.get();
1507 void AppCacheStorageImpl::LoadOrCreateGroup(
1508 const GURL
& manifest_url
, Delegate
* delegate
) {
1511 delegate
->OnGroupLoaded(NULL
, manifest_url
);
1515 AppCacheGroup
* group
= working_set_
.GetGroup(manifest_url
);
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();
1525 scoped_refptr
<GroupLoadTask
> task(GetPendingGroupLoadTask(manifest_url
));
1527 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
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
);
1539 task
= new GroupLoadTask(manifest_url
, this);
1540 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
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
) {
1570 const GURL
* url_ptr
= &url
;
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
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
)) {
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
)) {
1606 if (IsInitTaskComplete() && usage_map_
.find(origin
) == usage_map_
.end()) {
1607 // No need to query the database, return async'ly but without going thru
1609 scoped_refptr
<AppCacheGroup
> no_group
;
1610 scoped_refptr
<AppCache
> no_cache
;
1612 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1613 weak_factory_
.GetWeakPtr(), url
, AppCacheEntry(), no_group
,
1615 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
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
,
1623 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
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
)
1633 AppCacheEntry
* entry
= cache
->GetEntry(url
);
1634 if (!entry
|| entry
->IsForeign())
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
))));
1645 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
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
) {
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;
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
);
1702 AppCacheEntry
* entry
= cache
->GetEntry(entry_url
);
1705 entry
->add_types(AppCacheEntry::FOREIGN
);
1707 scoped_refptr
<MarkEntryAsForeignTask
> task(
1708 new MarkEntryAsForeignTask(this, entry_url
, cache_id
));
1710 pending_foreign_markings_
.push_back(std::make_pair(entry_url
, cache_id
));
1713 void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup
* group
,
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
));
1723 void AppCacheStorageImpl::StoreEvictionTimes(AppCacheGroup
* group
) {
1724 scoped_refptr
<UpdateEvictionTimesTask
> task(
1725 new UpdateEvictionTimesTask(this, group
));
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
,
1746 void AppCacheStorageImpl::DoomResponses(
1747 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1748 if (response_ids
.empty())
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
;
1765 void AppCacheStorageImpl::DeleteResponses(
1766 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1767 if (response_ids
.empty())
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_
));
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()),
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;
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;
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_
);
1842 if (deletable_response_ids_
.empty()) {
1843 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1844 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
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
;
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
;
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
);
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();
1892 AppCacheDiskCache
* AppCacheStorageImpl::disk_cache() {
1893 DCHECK(IsInitTaskComplete());
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)));
1907 rv
= disk_cache_
->InitWithDiskBackend(
1908 cache_directory_
.Append(kDiskCacheDirectoryName
),
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.
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(
1945 base::Bind(&base::DoNothing
),
1946 base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2
,
1947 weak_factory_
.GetWeakPtr()));
1951 void AppCacheStorageImpl::DeleteAndStartOverPart2() {
1952 db_thread_
->PostTaskAndReply(
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())
1967 const base::TimeDelta kDelay
= base::TimeDelta::FromMinutes(5);
1968 lazy_commit_timer_
.Start(
1970 base::Bind(&AppCacheStorageImpl::OnLazyCommitTimer
,
1971 weak_factory_
.GetWeakPtr()));
1974 void AppCacheStorageImpl::OnLazyCommitTimer() {
1975 lazy_commit_timer_
.Stop();
1978 scoped_refptr
<DatabaseTask
> task(new CommitLastAccessTimesTask(this));
1982 } // namespace content