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 "webkit/browser/appcache/appcache_storage_impl.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/file_util.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "net/base/cache_type.h"
20 #include "net/base/net_errors.h"
21 #include "sql/connection.h"
22 #include "sql/transaction.h"
23 #include "webkit/browser/appcache/appcache.h"
24 #include "webkit/browser/appcache/appcache_database.h"
25 #include "webkit/browser/appcache/appcache_entry.h"
26 #include "webkit/browser/appcache/appcache_group.h"
27 #include "webkit/browser/appcache/appcache_histograms.h"
28 #include "webkit/browser/appcache/appcache_quota_client.h"
29 #include "webkit/browser/appcache/appcache_response.h"
30 #include "webkit/browser/appcache/appcache_service.h"
31 #include "webkit/browser/quota/quota_client.h"
32 #include "webkit/browser/quota/quota_manager.h"
33 #include "webkit/browser/quota/special_storage_policy.h"
37 // Hard coded default when not using quota management.
38 static const int kDefaultQuota
= 5 * 1024 * 1024;
40 static const int kMaxDiskCacheSize
= 250 * 1024 * 1024;
41 static const int kMaxMemDiskCacheSize
= 10 * 1024 * 1024;
42 static const base::FilePath::CharType kDiskCacheDirectoryName
[] =
43 FILE_PATH_LITERAL("Cache");
47 // Helpers for clearing data from the AppCacheDatabase.
48 bool DeleteGroupAndRelatedRecords(AppCacheDatabase
* database
,
50 std::vector
<int64
>* deletable_response_ids
) {
51 AppCacheDatabase::CacheRecord cache_record
;
53 if (database
->FindCacheForGroup(group_id
, &cache_record
)) {
54 database
->FindResponseIdsForCacheAsVector(cache_record
.cache_id
,
55 deletable_response_ids
);
57 database
->DeleteGroup(group_id
) &&
58 database
->DeleteCache(cache_record
.cache_id
) &&
59 database
->DeleteEntriesForCache(cache_record
.cache_id
) &&
60 database
->DeleteNamespacesForCache(cache_record
.cache_id
) &&
61 database
->DeleteOnlineWhiteListForCache(cache_record
.cache_id
) &&
62 database
->InsertDeletableResponseIds(*deletable_response_ids
);
64 NOTREACHED() << "A existing group without a cache is unexpected";
65 success
= database
->DeleteGroup(group_id
);
70 // Destroys |database|. If there is appcache data to be deleted
71 // (|force_keep_session_state| is false), deletes session-only appcache data.
72 void ClearSessionOnlyOrigins(
73 AppCacheDatabase
* database
,
74 scoped_refptr
<quota::SpecialStoragePolicy
> special_storage_policy
,
75 bool force_keep_session_state
) {
76 scoped_ptr
<AppCacheDatabase
> database_to_delete(database
);
78 // If saving session state, only delete the database.
79 if (force_keep_session_state
)
82 bool has_session_only_appcaches
=
83 special_storage_policy
.get() &&
84 special_storage_policy
->HasSessionOnlyOrigins();
86 // Clearning only session-only databases, and there are none.
87 if (!has_session_only_appcaches
)
90 std::set
<GURL
> origins
;
91 database
->FindOriginsWithGroups(&origins
);
93 return; // nothing to delete
95 sql::Connection
* connection
= database
->db_connection();
97 NOTREACHED() << "Missing database connection.";
101 std::set
<GURL
>::const_iterator origin
;
102 for (origin
= origins
.begin(); origin
!= origins
.end(); ++origin
) {
103 if (!special_storage_policy
->IsStorageSessionOnly(*origin
))
105 if (special_storage_policy
.get() &&
106 special_storage_policy
->IsStorageProtected(*origin
))
109 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
110 database
->FindGroupsForOrigin(*origin
, &groups
);
111 std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator group
;
112 for (group
= groups
.begin(); group
!= groups
.end(); ++group
) {
113 sql::Transaction
transaction(connection
);
114 if (!transaction
.Begin()) {
115 NOTREACHED() << "Failed to start transaction";
118 std::vector
<int64
> deletable_response_ids
;
119 bool success
= DeleteGroupAndRelatedRecords(database
,
121 &deletable_response_ids
);
122 success
= success
&& transaction
.Commit();
130 // DatabaseTask -----------------------------------------
132 class AppCacheStorageImpl::DatabaseTask
133 : public base::RefCountedThreadSafe
<DatabaseTask
> {
135 explicit DatabaseTask(AppCacheStorageImpl
* storage
)
136 : storage_(storage
), database_(storage
->database_
),
137 io_thread_(base::MessageLoopProxy::current()) {
138 DCHECK(io_thread_
.get());
141 void AddDelegate(DelegateReference
* delegate_reference
) {
142 delegates_
.push_back(make_scoped_refptr(delegate_reference
));
145 // Schedules a task to be Run() on the DB thread. Tasks
146 // are run in the order in which they are scheduled.
149 // Called on the DB thread.
150 virtual void Run() = 0;
152 // Called on the IO thread after Run() has completed.
153 virtual void RunCompleted() {}
155 // Once scheduled a task cannot be cancelled, but the
156 // call to RunCompleted may be. This method should only be
157 // called on the IO thread. This is used by AppCacheStorageImpl
158 // to cancel the completion calls when AppCacheStorageImpl is
159 // destructed. This method may be overriden to release or delete
160 // additional data associated with the task that is not DB thread
161 // safe. If overriden, this base class method must be called from
162 // within the override.
163 virtual void CancelCompletion();
166 friend class base::RefCountedThreadSafe
<DatabaseTask
>;
167 virtual ~DatabaseTask() {}
169 AppCacheStorageImpl
* storage_
;
170 AppCacheDatabase
* database_
;
171 DelegateReferenceVector delegates_
;
174 void CallRun(base::TimeTicks schedule_time
);
175 void CallRunCompleted(base::TimeTicks schedule_time
);
176 void CallDisableStorage();
178 scoped_refptr
<base::MessageLoopProxy
> io_thread_
;
181 void AppCacheStorageImpl::DatabaseTask::Schedule() {
183 DCHECK(io_thread_
->BelongsToCurrentThread());
184 if (storage_
->db_thread_
->PostTask(
186 base::Bind(&DatabaseTask::CallRun
, this, base::TimeTicks::Now()))) {
187 storage_
->scheduled_database_tasks_
.push_back(this);
189 NOTREACHED() << "Thread for database tasks is not running. "
190 << "This is not always the DB thread.";
194 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
195 DCHECK(io_thread_
->BelongsToCurrentThread());
200 void AppCacheStorageImpl::DatabaseTask::CallRun(
201 base::TimeTicks schedule_time
) {
202 AppCacheHistograms::AddTaskQueueTimeSample(
203 base::TimeTicks::Now() - schedule_time
);
204 if (!database_
->is_disabled()) {
205 base::TimeTicks run_time
= base::TimeTicks::Now();
207 AppCacheHistograms::AddTaskRunTimeSample(
208 base::TimeTicks::Now() - run_time
);
209 if (database_
->is_disabled()) {
210 io_thread_
->PostTask(
212 base::Bind(&DatabaseTask::CallDisableStorage
, this));
215 io_thread_
->PostTask(
217 base::Bind(&DatabaseTask::CallRunCompleted
, this,
218 base::TimeTicks::Now()));
221 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
222 base::TimeTicks schedule_time
) {
223 AppCacheHistograms::AddCompletionQueueTimeSample(
224 base::TimeTicks::Now() - schedule_time
);
226 DCHECK(io_thread_
->BelongsToCurrentThread());
227 DCHECK(storage_
->scheduled_database_tasks_
.front() == this);
228 storage_
->scheduled_database_tasks_
.pop_front();
229 base::TimeTicks run_time
= base::TimeTicks::Now();
231 AppCacheHistograms::AddCompletionRunTimeSample(
232 base::TimeTicks::Now() - run_time
);
237 void AppCacheStorageImpl::DatabaseTask::CallDisableStorage() {
239 DCHECK(io_thread_
->BelongsToCurrentThread());
246 class AppCacheStorageImpl::InitTask
: public DatabaseTask
{
248 explicit InitTask(AppCacheStorageImpl
* storage
)
249 : DatabaseTask(storage
), last_group_id_(0),
250 last_cache_id_(0), last_response_id_(0),
251 last_deletable_response_rowid_(0) {}
254 virtual void Run() OVERRIDE
;
255 virtual void RunCompleted() OVERRIDE
;
258 virtual ~InitTask() {}
261 int64 last_group_id_
;
262 int64 last_cache_id_
;
263 int64 last_response_id_
;
264 int64 last_deletable_response_rowid_
;
265 std::map
<GURL
, int64
> usage_map_
;
268 void AppCacheStorageImpl::InitTask::Run() {
269 database_
->FindLastStorageIds(
270 &last_group_id_
, &last_cache_id_
, &last_response_id_
,
271 &last_deletable_response_rowid_
);
272 database_
->GetAllOriginUsage(&usage_map_
);
275 void AppCacheStorageImpl::InitTask::RunCompleted() {
276 storage_
->last_group_id_
= last_group_id_
;
277 storage_
->last_cache_id_
= last_cache_id_
;
278 storage_
->last_response_id_
= last_response_id_
;
279 storage_
->last_deletable_response_rowid_
= last_deletable_response_rowid_
;
281 if (!storage_
->is_disabled()) {
282 storage_
->usage_map_
.swap(usage_map_
);
283 const base::TimeDelta kDelay
= base::TimeDelta::FromMinutes(5);
284 base::MessageLoop::current()->PostDelayedTask(
286 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses
,
287 storage_
->weak_factory_
.GetWeakPtr()),
291 if (storage_
->service()->quota_client())
292 storage_
->service()->quota_client()->NotifyAppCacheReady();
295 // CloseConnectionTask -------
297 class AppCacheStorageImpl::CloseConnectionTask
: public DatabaseTask
{
299 explicit CloseConnectionTask(AppCacheStorageImpl
* storage
)
300 : DatabaseTask(storage
) {}
303 virtual void Run() OVERRIDE
{ database_
->CloseConnection(); }
306 virtual ~CloseConnectionTask() {}
309 // DisableDatabaseTask -------
311 class AppCacheStorageImpl::DisableDatabaseTask
: public DatabaseTask
{
313 explicit DisableDatabaseTask(AppCacheStorageImpl
* storage
)
314 : DatabaseTask(storage
) {}
317 virtual void Run() OVERRIDE
{ database_
->Disable(); }
320 virtual ~DisableDatabaseTask() {}
323 // GetAllInfoTask -------
325 class AppCacheStorageImpl::GetAllInfoTask
: public DatabaseTask
{
327 explicit GetAllInfoTask(AppCacheStorageImpl
* storage
)
328 : DatabaseTask(storage
),
329 info_collection_(new AppCacheInfoCollection()) {
333 virtual void Run() OVERRIDE
;
334 virtual void RunCompleted() OVERRIDE
;
337 virtual ~GetAllInfoTask() {}
340 scoped_refptr
<AppCacheInfoCollection
> info_collection_
;
343 void AppCacheStorageImpl::GetAllInfoTask::Run() {
344 std::set
<GURL
> origins
;
345 database_
->FindOriginsWithGroups(&origins
);
346 for (std::set
<GURL
>::const_iterator origin
= origins
.begin();
347 origin
!= origins
.end(); ++origin
) {
348 AppCacheInfoVector
& infos
=
349 info_collection_
->infos_by_origin
[*origin
];
350 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
351 database_
->FindGroupsForOrigin(*origin
, &groups
);
352 for (std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator
353 group
= groups
.begin();
354 group
!= groups
.end(); ++group
) {
355 AppCacheDatabase::CacheRecord cache_record
;
356 database_
->FindCacheForGroup(group
->group_id
, &cache_record
);
358 info
.manifest_url
= group
->manifest_url
;
359 info
.creation_time
= group
->creation_time
;
360 info
.size
= cache_record
.cache_size
;
361 info
.last_access_time
= group
->last_access_time
;
362 info
.last_update_time
= cache_record
.update_time
;
363 info
.cache_id
= cache_record
.cache_id
;
364 info
.group_id
= group
->group_id
;
365 info
.is_complete
= true;
366 infos
.push_back(info
);
371 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
372 DCHECK(delegates_
.size() == 1);
373 FOR_EACH_DELEGATE(delegates_
, OnAllInfo(info_collection_
.get()));
376 // StoreOrLoadTask -------
378 class AppCacheStorageImpl::StoreOrLoadTask
: public DatabaseTask
{
380 explicit StoreOrLoadTask(AppCacheStorageImpl
* storage
)
381 : DatabaseTask(storage
) {}
382 virtual ~StoreOrLoadTask() {}
384 bool FindRelatedCacheRecords(int64 cache_id
);
385 void CreateCacheAndGroupFromRecords(
386 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
);
388 AppCacheDatabase::GroupRecord group_record_
;
389 AppCacheDatabase::CacheRecord cache_record_
;
390 std::vector
<AppCacheDatabase::EntryRecord
> entry_records_
;
391 std::vector
<AppCacheDatabase::NamespaceRecord
>
392 intercept_namespace_records_
;
393 std::vector
<AppCacheDatabase::NamespaceRecord
>
394 fallback_namespace_records_
;
395 std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
396 online_whitelist_records_
;
399 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
401 return database_
->FindEntriesForCache(cache_id
, &entry_records_
) &&
402 database_
->FindNamespacesForCache(
403 cache_id
, &intercept_namespace_records_
,
404 &fallback_namespace_records_
) &&
405 database_
->FindOnlineWhiteListForCache(
406 cache_id
, &online_whitelist_records_
);
409 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
410 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
) {
411 DCHECK(storage_
&& cache
&& group
);
413 (*cache
) = storage_
->working_set_
.GetCache(cache_record_
.cache_id
);
415 (*group
) = cache
->get()->owning_group();
416 DCHECK(group
->get());
417 DCHECK_EQ(group_record_
.group_id
, group
->get()->group_id());
419 // TODO(michaeln): histogram is fishing for clues to crbug/95101
420 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
421 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
422 AppCacheHistograms::CALLSITE_0
);
425 storage_
->NotifyStorageAccessed(group_record_
.origin
);
429 (*cache
) = new AppCache(storage_
, cache_record_
.cache_id
);
430 cache
->get()->InitializeWithDatabaseRecords(
431 cache_record_
, entry_records_
,
432 intercept_namespace_records_
,
433 fallback_namespace_records_
,
434 online_whitelist_records_
);
435 cache
->get()->set_complete(true);
437 (*group
) = storage_
->working_set_
.GetGroup(group_record_
.manifest_url
);
439 DCHECK(group_record_
.group_id
== group
->get()->group_id());
440 group
->get()->AddCache(cache
->get());
442 // TODO(michaeln): histogram is fishing for clues to crbug/95101
443 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
444 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
445 AppCacheHistograms::CALLSITE_1
);
448 (*group
) = new AppCacheGroup(
449 storage_
, group_record_
.manifest_url
,
450 group_record_
.group_id
);
451 group
->get()->set_creation_time(group_record_
.creation_time
);
452 group
->get()->AddCache(cache
->get());
454 // TODO(michaeln): histogram is fishing for clues to crbug/95101
455 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
456 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
457 AppCacheHistograms::CALLSITE_2
);
460 DCHECK(group
->get()->newest_complete_cache() == cache
->get());
462 // We have to update foriegn entries if MarkEntryAsForeignTasks
464 std::vector
<GURL
> urls
;
465 storage_
->GetPendingForeignMarkingsForCache(cache
->get()->cache_id(), &urls
);
466 for (std::vector
<GURL
>::iterator iter
= urls
.begin();
467 iter
!= urls
.end(); ++iter
) {
468 DCHECK(cache
->get()->GetEntry(*iter
));
469 cache
->get()->GetEntry(*iter
)->add_types(AppCacheEntry::FOREIGN
);
472 storage_
->NotifyStorageAccessed(group_record_
.origin
);
474 // TODO(michaeln): Maybe verify that the responses we expect to exist
475 // do actually exist in the disk_cache (and if not then what?)
478 // CacheLoadTask -------
480 class AppCacheStorageImpl::CacheLoadTask
: public StoreOrLoadTask
{
482 CacheLoadTask(int64 cache_id
, AppCacheStorageImpl
* storage
)
483 : StoreOrLoadTask(storage
), cache_id_(cache_id
),
487 virtual void Run() OVERRIDE
;
488 virtual void RunCompleted() OVERRIDE
;
491 virtual ~CacheLoadTask() {}
498 void AppCacheStorageImpl::CacheLoadTask::Run() {
500 database_
->FindCache(cache_id_
, &cache_record_
) &&
501 database_
->FindGroup(cache_record_
.group_id
, &group_record_
) &&
502 FindRelatedCacheRecords(cache_id_
);
505 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
509 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
510 storage_
->pending_cache_loads_
.erase(cache_id_
);
511 scoped_refptr
<AppCache
> cache
;
512 scoped_refptr
<AppCacheGroup
> group
;
513 if (success_
&& !storage_
->is_disabled()) {
514 DCHECK(cache_record_
.cache_id
== cache_id_
);
515 CreateCacheAndGroupFromRecords(&cache
, &group
);
517 FOR_EACH_DELEGATE(delegates_
, OnCacheLoaded(cache
.get(), cache_id_
));
520 // GroupLoadTask -------
522 class AppCacheStorageImpl::GroupLoadTask
: public StoreOrLoadTask
{
524 GroupLoadTask(GURL manifest_url
, AppCacheStorageImpl
* storage
)
525 : StoreOrLoadTask(storage
), manifest_url_(manifest_url
),
529 virtual void Run() OVERRIDE
;
530 virtual void RunCompleted() OVERRIDE
;
533 virtual ~GroupLoadTask() {}
540 void AppCacheStorageImpl::GroupLoadTask::Run() {
542 database_
->FindGroupForManifestUrl(manifest_url_
, &group_record_
) &&
543 database_
->FindCacheForGroup(group_record_
.group_id
, &cache_record_
) &&
544 FindRelatedCacheRecords(cache_record_
.cache_id
);
547 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
551 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
552 storage_
->pending_group_loads_
.erase(manifest_url_
);
553 scoped_refptr
<AppCacheGroup
> group
;
554 scoped_refptr
<AppCache
> cache
;
555 if (!storage_
->is_disabled()) {
557 DCHECK(group_record_
.manifest_url
== manifest_url_
);
558 CreateCacheAndGroupFromRecords(&cache
, &group
);
560 group
= storage_
->working_set_
.GetGroup(manifest_url_
);
563 new AppCacheGroup(storage_
, manifest_url_
, storage_
->NewGroupId());
567 FOR_EACH_DELEGATE(delegates_
, OnGroupLoaded(group
.get(), manifest_url_
));
570 // StoreGroupAndCacheTask -------
572 class AppCacheStorageImpl::StoreGroupAndCacheTask
: public StoreOrLoadTask
{
574 StoreGroupAndCacheTask(AppCacheStorageImpl
* storage
, AppCacheGroup
* group
,
575 AppCache
* newest_cache
);
577 void GetQuotaThenSchedule();
578 void OnQuotaCallback(
579 quota::QuotaStatusCode status
, int64 usage
, int64 quota
);
582 virtual void Run() OVERRIDE
;
583 virtual void RunCompleted() OVERRIDE
;
584 virtual void CancelCompletion() OVERRIDE
;
587 virtual ~StoreGroupAndCacheTask() {}
590 scoped_refptr
<AppCacheGroup
> group_
;
591 scoped_refptr
<AppCache
> cache_
;
593 bool would_exceed_quota_
;
594 int64 space_available_
;
595 int64 new_origin_usage_
;
596 std::vector
<int64
> newly_deletable_response_ids_
;
599 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
600 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, AppCache
* newest_cache
)
601 : StoreOrLoadTask(storage
), group_(group
), cache_(newest_cache
),
602 success_(false), would_exceed_quota_(false),
603 space_available_(-1), new_origin_usage_(-1) {
604 group_record_
.group_id
= group
->group_id();
605 group_record_
.manifest_url
= group
->manifest_url();
606 group_record_
.origin
= group_record_
.manifest_url
.GetOrigin();
607 newest_cache
->ToDatabaseRecords(
609 &cache_record_
, &entry_records_
,
610 &intercept_namespace_records_
,
611 &fallback_namespace_records_
,
612 &online_whitelist_records_
);
615 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
616 quota::QuotaManager
* quota_manager
= NULL
;
617 if (storage_
->service()->quota_manager_proxy()) {
619 storage_
->service()->quota_manager_proxy()->quota_manager();
622 if (!quota_manager
) {
623 if (storage_
->service()->special_storage_policy() &&
624 storage_
->service()->special_storage_policy()->IsStorageUnlimited(
625 group_record_
.origin
))
626 space_available_
= kint64max
;
631 // We have to ask the quota manager for the value.
632 storage_
->pending_quota_queries_
.insert(this);
633 quota_manager
->GetUsageAndQuota(
634 group_record_
.origin
, quota::kStorageTypeTemporary
,
635 base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback
, this));
638 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
639 quota::QuotaStatusCode status
, int64 usage
, int64 quota
) {
641 if (status
== quota::kQuotaStatusOk
)
642 space_available_
= std::max(static_cast<int64
>(0), quota
- usage
);
644 space_available_
= 0;
645 storage_
->pending_quota_queries_
.erase(this);
650 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
652 sql::Connection
* connection
= database_
->db_connection();
656 sql::Transaction
transaction(connection
);
657 if (!transaction
.Begin())
660 int64 old_origin_usage
= database_
->GetOriginUsage(group_record_
.origin
);
662 AppCacheDatabase::GroupRecord existing_group
;
663 success_
= database_
->FindGroup(group_record_
.group_id
, &existing_group
);
665 group_record_
.creation_time
= base::Time::Now();
666 group_record_
.last_access_time
= base::Time::Now();
667 success_
= database_
->InsertGroup(&group_record_
);
669 DCHECK(group_record_
.group_id
== existing_group
.group_id
);
670 DCHECK(group_record_
.manifest_url
== existing_group
.manifest_url
);
671 DCHECK(group_record_
.origin
== existing_group
.origin
);
673 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
676 AppCacheDatabase::CacheRecord cache
;
677 if (database_
->FindCacheForGroup(group_record_
.group_id
, &cache
)) {
678 // Get the set of response ids in the old cache.
679 std::set
<int64
> existing_response_ids
;
680 database_
->FindResponseIdsForCacheAsSet(cache
.cache_id
,
681 &existing_response_ids
);
683 // Remove those that remain in the new cache.
684 std::vector
<AppCacheDatabase::EntryRecord
>::const_iterator entry_iter
=
685 entry_records_
.begin();
686 while (entry_iter
!= entry_records_
.end()) {
687 existing_response_ids
.erase(entry_iter
->response_id
);
691 // The rest are deletable.
692 std::set
<int64
>::const_iterator id_iter
= existing_response_ids
.begin();
693 while (id_iter
!= existing_response_ids
.end()) {
694 newly_deletable_response_ids_
.push_back(*id_iter
);
699 database_
->DeleteCache(cache
.cache_id
) &&
700 database_
->DeleteEntriesForCache(cache
.cache_id
) &&
701 database_
->DeleteNamespacesForCache(cache
.cache_id
) &&
702 database_
->DeleteOnlineWhiteListForCache(cache
.cache_id
) &&
703 database_
->InsertDeletableResponseIds(newly_deletable_response_ids_
);
704 // TODO(michaeln): store group_id too with deletable ids
706 NOTREACHED() << "A existing group without a cache is unexpected";
712 database_
->InsertCache(&cache_record_
) &&
713 database_
->InsertEntryRecords(entry_records_
) &&
714 database_
->InsertNamespaceRecords(intercept_namespace_records_
) &&
715 database_
->InsertNamespaceRecords(fallback_namespace_records_
) &&
716 database_
->InsertOnlineWhiteListRecords(online_whitelist_records_
);
721 new_origin_usage_
= database_
->GetOriginUsage(group_record_
.origin
);
723 // Only check quota when the new usage exceeds the old usage.
724 if (new_origin_usage_
<= old_origin_usage
) {
725 success_
= transaction
.Commit();
729 // Use a simple hard-coded value when not using quota management.
730 if (space_available_
== -1) {
731 if (new_origin_usage_
> kDefaultQuota
) {
732 would_exceed_quota_
= true;
736 success_
= transaction
.Commit();
740 // Check limits based on the space availbable given to us via the
742 int64 delta
= new_origin_usage_
- old_origin_usage
;
743 if (delta
> space_available_
) {
744 would_exceed_quota_
= true;
749 success_
= transaction
.Commit();
752 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
754 storage_
->UpdateUsageMapAndNotify(
755 group_
->manifest_url().GetOrigin(), new_origin_usage_
);
756 if (cache_
.get() != group_
->newest_complete_cache()) {
757 cache_
->set_complete(true);
758 group_
->AddCache(cache_
.get());
760 if (group_
->creation_time().is_null())
761 group_
->set_creation_time(group_record_
.creation_time
);
762 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
766 OnGroupAndNewestCacheStored(
767 group_
.get(), cache_
.get(), success_
, would_exceed_quota_
));
771 // TODO(michaeln): if (would_exceed_quota_) what if the current usage
772 // also exceeds the quota? http://crbug.com/83968
775 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
776 // Overriden to safely drop our reference to the group and cache
777 // which are not thread safe refcounted.
778 DatabaseTask::CancelCompletion();
783 // FindMainResponseTask -------
785 // Helpers for FindMainResponseTask::Run()
787 class SortByCachePreference
788 : public std::binary_function
<
789 AppCacheDatabase::EntryRecord
,
790 AppCacheDatabase::EntryRecord
,
793 SortByCachePreference(int64 preferred_id
, const std::set
<int64
>& in_use_ids
)
794 : preferred_id_(preferred_id
), in_use_ids_(in_use_ids
) {
797 const AppCacheDatabase::EntryRecord
& lhs
,
798 const AppCacheDatabase::EntryRecord
& rhs
) {
799 return compute_value(lhs
) > compute_value(rhs
);
802 int compute_value(const AppCacheDatabase::EntryRecord
& entry
) {
803 if (entry
.cache_id
== preferred_id_
)
805 else if (in_use_ids_
.find(entry
.cache_id
) != in_use_ids_
.end())
810 const std::set
<int64
>& in_use_ids_
;
814 const AppCacheDatabase::NamespaceRecord
& lhs
,
815 const AppCacheDatabase::NamespaceRecord
& rhs
) {
816 return lhs
.namespace_
.namespace_url
.spec().length() >
817 rhs
.namespace_
.namespace_url
.spec().length();
820 class NetworkNamespaceHelper
{
822 explicit NetworkNamespaceHelper(AppCacheDatabase
* database
)
823 : database_(database
) {
826 bool IsInNetworkNamespace(const GURL
& url
, int64 cache_id
) {
827 typedef std::pair
<WhiteListMap::iterator
, bool> InsertResult
;
828 InsertResult result
= namespaces_map_
.insert(
829 WhiteListMap::value_type(cache_id
, NamespaceVector()));
831 GetOnlineWhiteListForCache(cache_id
, &result
.first
->second
);
832 return AppCache::FindNamespace(result
.first
->second
, url
) != NULL
;
836 void GetOnlineWhiteListForCache(
837 int64 cache_id
, NamespaceVector
* namespaces
) {
838 DCHECK(namespaces
&& namespaces
->empty());
839 typedef std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
841 WhiteListVector records
;
842 if (!database_
->FindOnlineWhiteListForCache(cache_id
, &records
))
844 WhiteListVector::const_iterator iter
= records
.begin();
845 while (iter
!= records
.end()) {
846 namespaces
->push_back(
847 Namespace(NETWORK_NAMESPACE
, iter
->namespace_url
, GURL(),
854 typedef std::map
<int64
, NamespaceVector
> WhiteListMap
;
855 WhiteListMap namespaces_map_
;
856 AppCacheDatabase
* database_
;
861 class AppCacheStorageImpl::FindMainResponseTask
: public DatabaseTask
{
863 FindMainResponseTask(AppCacheStorageImpl
* storage
,
865 const GURL
& preferred_manifest_url
,
866 const AppCacheWorkingSet::GroupMap
* groups_in_use
)
867 : DatabaseTask(storage
), url_(url
),
868 preferred_manifest_url_(preferred_manifest_url
),
869 cache_id_(kNoCacheId
), group_id_(0) {
871 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
872 groups_in_use
->begin();
873 it
!= groups_in_use
->end(); ++it
) {
874 AppCacheGroup
* group
= it
->second
;
875 AppCache
* cache
= group
->newest_complete_cache();
876 if (group
->is_obsolete() || !cache
)
878 cache_ids_in_use_
.insert(cache
->cache_id());
884 virtual void Run() OVERRIDE
;
885 virtual void RunCompleted() OVERRIDE
;
888 virtual ~FindMainResponseTask() {}
891 typedef std::vector
<AppCacheDatabase::NamespaceRecord
*>
892 NamespaceRecordPtrVector
;
894 bool FindExactMatch(int64 preferred_id
);
895 bool FindNamespaceMatch(int64 preferred_id
);
896 bool FindNamespaceHelper(
897 int64 preferred_cache_id
,
898 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
899 NetworkNamespaceHelper
* network_namespace_helper
);
900 bool FindFirstValidNamespace(const NamespaceRecordPtrVector
& namespaces
);
903 GURL preferred_manifest_url_
;
904 std::set
<int64
> cache_ids_in_use_
;
905 AppCacheEntry entry_
;
906 AppCacheEntry fallback_entry_
;
907 GURL namespace_entry_url_
;
915 void AppCacheStorageImpl::FindMainResponseTask::Run() {
916 // NOTE: The heuristics around choosing amoungst multiple candidates
917 // is underspecified, and just plain not fully understood. This needs
920 // The 'preferred_manifest_url' is the url of the manifest associated
921 // with the page that opened or embedded the page being loaded now.
922 // We have a strong preference to use resources from that cache.
923 // We also have a lesser bias to use resources from caches that are currently
924 // being used by other unrelated pages.
925 // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
926 // - when navigating a frame whose current contents are from an appcache
927 // - when clicking an href in a frame that is appcached
928 int64 preferred_cache_id
= kNoCacheId
;
929 if (!preferred_manifest_url_
.is_empty()) {
930 AppCacheDatabase::GroupRecord preferred_group
;
931 AppCacheDatabase::CacheRecord preferred_cache
;
932 if (database_
->FindGroupForManifestUrl(
933 preferred_manifest_url_
, &preferred_group
) &&
934 database_
->FindCacheForGroup(
935 preferred_group
.group_id
, &preferred_cache
)) {
936 preferred_cache_id
= preferred_cache
.cache_id
;
940 if (FindExactMatch(preferred_cache_id
) ||
941 FindNamespaceMatch(preferred_cache_id
)) {
942 // We found something.
943 DCHECK(cache_id_
!= kNoCacheId
&& !manifest_url_
.is_empty() &&
948 // We didn't find anything.
949 DCHECK(cache_id_
== kNoCacheId
&& manifest_url_
.is_empty() &&
953 bool AppCacheStorageImpl::
954 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id
) {
955 std::vector
<AppCacheDatabase::EntryRecord
> entries
;
956 if (database_
->FindEntriesForUrl(url_
, &entries
) && !entries
.empty()) {
957 // Sort them in order of preference, from the preferred_cache first,
958 // followed by hits from caches that are 'in use', then the rest.
959 std::sort(entries
.begin(), entries
.end(),
960 SortByCachePreference(preferred_cache_id
, cache_ids_in_use_
));
962 // Take the first with a valid, non-foreign entry.
963 std::vector
<AppCacheDatabase::EntryRecord
>::iterator iter
;
964 for (iter
= entries
.begin(); iter
< entries
.end(); ++iter
) {
965 AppCacheDatabase::GroupRecord group_record
;
966 if ((iter
->flags
& AppCacheEntry::FOREIGN
) ||
967 !database_
->FindGroupForCache(iter
->cache_id
, &group_record
)) {
970 manifest_url_
= group_record
.manifest_url
;
971 group_id_
= group_record
.group_id
;
972 entry_
= AppCacheEntry(iter
->flags
, iter
->response_id
);
973 cache_id_
= iter
->cache_id
;
974 return true; // We found an exact match.
980 bool AppCacheStorageImpl::
981 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id
) {
982 AppCacheDatabase::NamespaceRecordVector all_intercepts
;
983 AppCacheDatabase::NamespaceRecordVector all_fallbacks
;
984 if (!database_
->FindNamespacesForOrigin(
985 url_
.GetOrigin(), &all_intercepts
, &all_fallbacks
)
986 || (all_intercepts
.empty() && all_fallbacks
.empty())) {
990 NetworkNamespaceHelper
network_namespace_helper(database_
);
991 if (FindNamespaceHelper(preferred_cache_id
,
993 &network_namespace_helper
) ||
994 FindNamespaceHelper(preferred_cache_id
,
996 &network_namespace_helper
)) {
1002 bool AppCacheStorageImpl::
1003 FindMainResponseTask::FindNamespaceHelper(
1004 int64 preferred_cache_id
,
1005 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
1006 NetworkNamespaceHelper
* network_namespace_helper
) {
1007 // Sort them by length, longer matches within the same cache/bucket take
1009 std::sort(namespaces
->begin(), namespaces
->end(), SortByLength
);
1011 NamespaceRecordPtrVector preferred_namespaces
;
1012 NamespaceRecordPtrVector inuse_namespaces
;
1013 NamespaceRecordPtrVector other_namespaces
;
1014 std::vector
<AppCacheDatabase::NamespaceRecord
>::iterator iter
;
1015 for (iter
= namespaces
->begin(); iter
< namespaces
->end(); ++iter
) {
1016 // Skip those that aren't a match.
1017 if (!iter
->namespace_
.IsMatch(url_
))
1020 // Skip namespaces where the requested url falls into a network
1021 // namespace of its containing appcache.
1022 if (network_namespace_helper
->IsInNetworkNamespace(url_
, iter
->cache_id
))
1025 // Bin them into one of our three buckets.
1026 if (iter
->cache_id
== preferred_cache_id
)
1027 preferred_namespaces
.push_back(&(*iter
));
1028 else if (cache_ids_in_use_
.find(iter
->cache_id
) != cache_ids_in_use_
.end())
1029 inuse_namespaces
.push_back(&(*iter
));
1031 other_namespaces
.push_back(&(*iter
));
1034 if (FindFirstValidNamespace(preferred_namespaces
) ||
1035 FindFirstValidNamespace(inuse_namespaces
) ||
1036 FindFirstValidNamespace(other_namespaces
))
1037 return true; // We found one.
1039 // We didn't find anything.
1043 bool AppCacheStorageImpl::
1044 FindMainResponseTask::FindFirstValidNamespace(
1045 const NamespaceRecordPtrVector
& namespaces
) {
1046 // Take the first with a valid, non-foreign entry.
1047 NamespaceRecordPtrVector::const_iterator iter
;
1048 for (iter
= namespaces
.begin(); iter
< namespaces
.end(); ++iter
) {
1049 AppCacheDatabase::EntryRecord entry_record
;
1050 if (database_
->FindEntry((*iter
)->cache_id
, (*iter
)->namespace_
.target_url
,
1052 AppCacheDatabase::GroupRecord group_record
;
1053 if ((entry_record
.flags
& AppCacheEntry::FOREIGN
) ||
1054 !database_
->FindGroupForCache(entry_record
.cache_id
, &group_record
)) {
1057 manifest_url_
= group_record
.manifest_url
;
1058 group_id_
= group_record
.group_id
;
1059 cache_id_
= (*iter
)->cache_id
;
1060 namespace_entry_url_
= (*iter
)->namespace_
.target_url
;
1061 if ((*iter
)->namespace_
.type
== FALLBACK_NAMESPACE
)
1062 fallback_entry_
= AppCacheEntry(entry_record
.flags
,
1063 entry_record
.response_id
);
1065 entry_
= AppCacheEntry(entry_record
.flags
, entry_record
.response_id
);
1066 return true; // We found one.
1069 return false; // We didn't find a match.
1072 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
1073 storage_
->CallOnMainResponseFound(
1074 &delegates_
, url_
, entry_
, namespace_entry_url_
, fallback_entry_
,
1075 cache_id_
, group_id_
, manifest_url_
);
1078 // MarkEntryAsForeignTask -------
1080 class AppCacheStorageImpl::MarkEntryAsForeignTask
: public DatabaseTask
{
1082 MarkEntryAsForeignTask(
1083 AppCacheStorageImpl
* storage
, const GURL
& url
, int64 cache_id
)
1084 : DatabaseTask(storage
), cache_id_(cache_id
), entry_url_(url
) {}
1087 virtual void Run() OVERRIDE
;
1088 virtual void RunCompleted() OVERRIDE
;
1091 virtual ~MarkEntryAsForeignTask() {}
1098 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
1099 database_
->AddEntryFlags(entry_url_
, cache_id_
, AppCacheEntry::FOREIGN
);
1102 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
1103 DCHECK(storage_
->pending_foreign_markings_
.front().first
== entry_url_
&&
1104 storage_
->pending_foreign_markings_
.front().second
== cache_id_
);
1105 storage_
->pending_foreign_markings_
.pop_front();
1108 // MakeGroupObsoleteTask -------
1110 class AppCacheStorageImpl::MakeGroupObsoleteTask
: public DatabaseTask
{
1112 MakeGroupObsoleteTask(AppCacheStorageImpl
* storage
, AppCacheGroup
* group
);
1115 virtual void Run() OVERRIDE
;
1116 virtual void RunCompleted() OVERRIDE
;
1117 virtual void CancelCompletion() OVERRIDE
;
1120 virtual ~MakeGroupObsoleteTask() {}
1123 scoped_refptr
<AppCacheGroup
> group_
;
1127 int64 new_origin_usage_
;
1128 std::vector
<int64
> newly_deletable_response_ids_
;
1131 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
1132 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
)
1133 : DatabaseTask(storage
), group_(group
), group_id_(group
->group_id()),
1134 origin_(group
->manifest_url().GetOrigin()),
1135 success_(false), new_origin_usage_(-1) {
1138 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
1140 sql::Connection
* connection
= database_
->db_connection();
1144 sql::Transaction
transaction(connection
);
1145 if (!transaction
.Begin())
1148 AppCacheDatabase::GroupRecord group_record
;
1149 if (!database_
->FindGroup(group_id_
, &group_record
)) {
1150 // This group doesn't exists in the database, nothing todo here.
1151 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1156 DCHECK_EQ(group_record
.origin
, origin_
);
1157 success_
= DeleteGroupAndRelatedRecords(database_
,
1159 &newly_deletable_response_ids_
);
1161 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1162 success_
= success_
&& transaction
.Commit();
1165 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
1167 group_
->set_obsolete(true);
1168 if (!storage_
->is_disabled()) {
1169 storage_
->UpdateUsageMapAndNotify(origin_
, new_origin_usage_
);
1170 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
1172 // Also remove from the working set, caches for an 'obsolete' group
1173 // may linger in use, but the group itself cannot be looked up by
1174 // 'manifest_url' in the working set any longer.
1175 storage_
->working_set()->RemoveGroup(group_
.get());
1178 FOR_EACH_DELEGATE(delegates_
, OnGroupMadeObsolete(group_
.get(), success_
));
1182 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
1183 // Overriden to safely drop our reference to the group
1184 // which is not thread safe refcounted.
1185 DatabaseTask::CancelCompletion();
1189 // GetDeletableResponseIdsTask -------
1191 class AppCacheStorageImpl::GetDeletableResponseIdsTask
: public DatabaseTask
{
1193 GetDeletableResponseIdsTask(AppCacheStorageImpl
* storage
, int64 max_rowid
)
1194 : DatabaseTask(storage
), max_rowid_(max_rowid
) {}
1197 virtual void Run() OVERRIDE
;
1198 virtual void RunCompleted() OVERRIDE
;
1201 virtual ~GetDeletableResponseIdsTask() {}
1205 std::vector
<int64
> response_ids_
;
1208 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
1209 const int kSqlLimit
= 1000;
1210 database_
->GetDeletableResponseIds(&response_ids_
, max_rowid_
, kSqlLimit
);
1211 // TODO(michaeln): retrieve group_ids too
1214 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
1215 if (!response_ids_
.empty())
1216 storage_
->StartDeletingResponses(response_ids_
);
1219 // InsertDeletableResponseIdsTask -------
1221 class AppCacheStorageImpl::InsertDeletableResponseIdsTask
1222 : public DatabaseTask
{
1224 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1225 : DatabaseTask(storage
) {}
1228 virtual void Run() OVERRIDE
;
1230 std::vector
<int64
> response_ids_
;
1233 virtual ~InsertDeletableResponseIdsTask() {}
1236 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
1237 database_
->InsertDeletableResponseIds(response_ids_
);
1238 // TODO(michaeln): store group_ids too
1241 // DeleteDeletableResponseIdsTask -------
1243 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
1244 : public DatabaseTask
{
1246 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1247 : DatabaseTask(storage
) {}
1250 virtual void Run() OVERRIDE
;
1252 std::vector
<int64
> response_ids_
;
1255 virtual ~DeleteDeletableResponseIdsTask() {}
1258 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1259 database_
->DeleteDeletableResponseIds(response_ids_
);
1262 // UpdateGroupLastAccessTimeTask -------
1264 class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
1265 : public DatabaseTask
{
1267 UpdateGroupLastAccessTimeTask(
1268 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, base::Time time
)
1269 : DatabaseTask(storage
), group_id_(group
->group_id()),
1270 last_access_time_(time
) {
1271 storage
->NotifyStorageAccessed(group
->manifest_url().GetOrigin());
1275 virtual void Run() OVERRIDE
;
1278 virtual ~UpdateGroupLastAccessTimeTask() {}
1282 base::Time last_access_time_
;
1285 void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
1286 database_
->UpdateGroupLastAccessTime(group_id_
, last_access_time_
);
1290 // AppCacheStorageImpl ---------------------------------------------------
1292 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheService
* service
)
1293 : AppCacheStorage(service
),
1294 is_incognito_(false),
1295 is_response_deletion_scheduled_(false),
1296 did_start_deleting_responses_(false),
1297 last_deletable_response_rowid_(0),
1299 is_disabled_(false),
1300 weak_factory_(this) {
1303 AppCacheStorageImpl::~AppCacheStorageImpl() {
1304 std::for_each(pending_quota_queries_
.begin(),
1305 pending_quota_queries_
.end(),
1306 std::mem_fun(&DatabaseTask::CancelCompletion
));
1307 std::for_each(scheduled_database_tasks_
.begin(),
1308 scheduled_database_tasks_
.end(),
1309 std::mem_fun(&DatabaseTask::CancelCompletion
));
1312 !db_thread_
->PostTask(
1314 base::Bind(&ClearSessionOnlyOrigins
, database_
,
1315 make_scoped_refptr(service_
->special_storage_policy()),
1316 service()->force_keep_session_state()))) {
1321 void AppCacheStorageImpl::Initialize(const base::FilePath
& cache_directory
,
1322 base::MessageLoopProxy
* db_thread
,
1323 base::MessageLoopProxy
* cache_thread
) {
1326 cache_directory_
= cache_directory
;
1327 is_incognito_
= cache_directory_
.empty();
1329 base::FilePath db_file_path
;
1331 db_file_path
= cache_directory_
.Append(kAppCacheDatabaseName
);
1332 database_
= new AppCacheDatabase(db_file_path
);
1334 db_thread_
= db_thread
;
1335 cache_thread_
= cache_thread
;
1337 scoped_refptr
<InitTask
> task(new InitTask(this));
1341 void AppCacheStorageImpl::Disable() {
1344 VLOG(1) << "Disabling appcache storage.";
1345 is_disabled_
= true;
1346 ClearUsageMapAndNotify();
1347 working_set()->Disable();
1349 disk_cache_
->Disable();
1350 scoped_refptr
<DisableDatabaseTask
> task(new DisableDatabaseTask(this));
1354 void AppCacheStorageImpl::GetAllInfo(Delegate
* delegate
) {
1356 scoped_refptr
<GetAllInfoTask
> task(new GetAllInfoTask(this));
1357 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1361 void AppCacheStorageImpl::LoadCache(int64 id
, Delegate
* delegate
) {
1364 delegate
->OnCacheLoaded(NULL
, id
);
1368 AppCache
* cache
= working_set_
.GetCache(id
);
1370 delegate
->OnCacheLoaded(cache
, id
);
1371 if (cache
->owning_group()) {
1372 scoped_refptr
<DatabaseTask
> update_task(
1373 new UpdateGroupLastAccessTimeTask(
1374 this, cache
->owning_group(), base::Time::Now()));
1375 update_task
->Schedule();
1379 scoped_refptr
<CacheLoadTask
> task(GetPendingCacheLoadTask(id
));
1381 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1384 task
= new CacheLoadTask(id
, this);
1385 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1387 pending_cache_loads_
[id
] = task
.get();
1390 void AppCacheStorageImpl::LoadOrCreateGroup(
1391 const GURL
& manifest_url
, Delegate
* delegate
) {
1394 delegate
->OnGroupLoaded(NULL
, manifest_url
);
1398 AppCacheGroup
* group
= working_set_
.GetGroup(manifest_url
);
1400 delegate
->OnGroupLoaded(group
, manifest_url
);
1401 scoped_refptr
<DatabaseTask
> update_task(
1402 new UpdateGroupLastAccessTimeTask(
1403 this, group
, base::Time::Now()));
1404 update_task
->Schedule();
1408 scoped_refptr
<GroupLoadTask
> task(GetPendingGroupLoadTask(manifest_url
));
1410 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1414 if (usage_map_
.find(manifest_url
.GetOrigin()) == usage_map_
.end()) {
1415 // No need to query the database, return a new group immediately.
1416 scoped_refptr
<AppCacheGroup
> group(new AppCacheGroup(
1417 service_
->storage(), manifest_url
, NewGroupId()));
1418 delegate
->OnGroupLoaded(group
.get(), manifest_url
);
1422 task
= new GroupLoadTask(manifest_url
, this);
1423 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1425 pending_group_loads_
[manifest_url
] = task
.get();
1428 void AppCacheStorageImpl::StoreGroupAndNewestCache(
1429 AppCacheGroup
* group
, AppCache
* newest_cache
, Delegate
* delegate
) {
1430 // TODO(michaeln): distinguish between a simple update of an existing
1431 // cache that just adds new master entry(s), and the insertion of a
1432 // whole new cache. The StoreGroupAndCacheTask as written will handle
1433 // the simple update case in a very heavy weight way (delete all and
1434 // the reinsert all over again).
1435 DCHECK(group
&& delegate
&& newest_cache
);
1436 scoped_refptr
<StoreGroupAndCacheTask
> task(
1437 new StoreGroupAndCacheTask(this, group
, newest_cache
));
1438 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1439 task
->GetQuotaThenSchedule();
1441 // TODO(michaeln): histogram is fishing for clues to crbug/95101
1442 if (!newest_cache
->GetEntry(group
->manifest_url())) {
1443 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
1444 AppCacheHistograms::CALLSITE_3
);
1448 void AppCacheStorageImpl::FindResponseForMainRequest(
1449 const GURL
& url
, const GURL
& preferred_manifest_url
,
1450 Delegate
* delegate
) {
1453 const GURL
* url_ptr
= &url
;
1455 if (url
.has_ref()) {
1456 GURL::Replacements replacements
;
1457 replacements
.ClearRef();
1458 url_no_ref
= url
.ReplaceComponents(replacements
);
1459 url_ptr
= &url_no_ref
;
1462 const GURL origin
= url
.GetOrigin();
1464 // First look in our working set for a direct hit without having to query
1466 const AppCacheWorkingSet::GroupMap
* groups_in_use
=
1467 working_set()->GetGroupsInOrigin(origin
);
1468 if (groups_in_use
) {
1469 if (!preferred_manifest_url
.is_empty()) {
1470 AppCacheWorkingSet::GroupMap::const_iterator found
=
1471 groups_in_use
->find(preferred_manifest_url
);
1472 if (found
!= groups_in_use
->end() &&
1473 FindResponseForMainRequestInGroup(
1474 found
->second
, *url_ptr
, delegate
)) {
1478 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
1479 groups_in_use
->begin();
1480 it
!= groups_in_use
->end(); ++it
) {
1481 if (FindResponseForMainRequestInGroup(
1482 it
->second
, *url_ptr
, delegate
)) {
1489 if (IsInitTaskComplete() && usage_map_
.find(origin
) == usage_map_
.end()) {
1490 // No need to query the database, return async'ly but without going thru
1492 scoped_refptr
<AppCacheGroup
> no_group
;
1493 scoped_refptr
<AppCache
> no_cache
;
1495 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1496 weak_factory_
.GetWeakPtr(), url
, AppCacheEntry(), no_group
,
1498 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1502 // We have to query the database, schedule a database task to do so.
1503 scoped_refptr
<FindMainResponseTask
> task(
1504 new FindMainResponseTask(this, *url_ptr
, preferred_manifest_url
,
1506 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1510 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
1511 AppCacheGroup
* group
, const GURL
& url
, Delegate
* delegate
) {
1512 AppCache
* cache
= group
->newest_complete_cache();
1513 if (group
->is_obsolete() || !cache
)
1516 AppCacheEntry
* entry
= cache
->GetEntry(url
);
1517 if (!entry
|| entry
->IsForeign())
1521 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1522 weak_factory_
.GetWeakPtr(), url
, *entry
,
1523 make_scoped_refptr(group
), make_scoped_refptr(cache
),
1524 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1528 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
1530 const AppCacheEntry
& found_entry
,
1531 scoped_refptr
<AppCacheGroup
> group
,
1532 scoped_refptr
<AppCache
> cache
,
1533 scoped_refptr
<DelegateReference
> delegate_ref
) {
1534 if (delegate_ref
->delegate
) {
1535 DelegateReferenceVector
delegates(1, delegate_ref
);
1536 CallOnMainResponseFound(
1537 &delegates
, url
, found_entry
,
1538 GURL(), AppCacheEntry(),
1539 cache
.get() ? cache
->cache_id() : kNoCacheId
,
1540 group
.get() ? group
->group_id() : kNoCacheId
,
1541 group
.get() ? group
->manifest_url() : GURL());
1545 void AppCacheStorageImpl::CallOnMainResponseFound(
1546 DelegateReferenceVector
* delegates
,
1547 const GURL
& url
, const AppCacheEntry
& entry
,
1548 const GURL
& namespace_entry_url
, const AppCacheEntry
& fallback_entry
,
1549 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
1552 OnMainResponseFound(url
, entry
,
1553 namespace_entry_url
, fallback_entry
,
1554 cache_id
, group_id
, manifest_url
));
1557 void AppCacheStorageImpl::FindResponseForSubRequest(
1558 AppCache
* cache
, const GURL
& url
,
1559 AppCacheEntry
* found_entry
, AppCacheEntry
* found_fallback_entry
,
1560 bool* found_network_namespace
) {
1561 DCHECK(cache
&& cache
->is_complete());
1563 // When a group is forcibly deleted, all subresource loads for pages
1564 // using caches in the group will result in a synthesized network errors.
1565 // Forcible deletion is not a function that is covered by the HTML5 spec.
1566 if (cache
->owning_group()->is_being_deleted()) {
1567 *found_entry
= AppCacheEntry();
1568 *found_fallback_entry
= AppCacheEntry();
1569 *found_network_namespace
= false;
1573 GURL fallback_namespace_not_used
;
1574 GURL intercept_namespace_not_used
;
1575 cache
->FindResponseForRequest(
1576 url
, found_entry
, &intercept_namespace_not_used
,
1577 found_fallback_entry
, &fallback_namespace_not_used
,
1578 found_network_namespace
);
1581 void AppCacheStorageImpl::MarkEntryAsForeign(
1582 const GURL
& entry_url
, int64 cache_id
) {
1583 AppCache
* cache
= working_set_
.GetCache(cache_id
);
1585 AppCacheEntry
* entry
= cache
->GetEntry(entry_url
);
1588 entry
->add_types(AppCacheEntry::FOREIGN
);
1590 scoped_refptr
<MarkEntryAsForeignTask
> task(
1591 new MarkEntryAsForeignTask(this, entry_url
, cache_id
));
1593 pending_foreign_markings_
.push_back(std::make_pair(entry_url
, cache_id
));
1596 void AppCacheStorageImpl::MakeGroupObsolete(
1597 AppCacheGroup
* group
, Delegate
* delegate
) {
1598 DCHECK(group
&& delegate
);
1599 scoped_refptr
<MakeGroupObsoleteTask
> task(
1600 new MakeGroupObsoleteTask(this, group
));
1601 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1605 AppCacheResponseReader
* AppCacheStorageImpl::CreateResponseReader(
1606 const GURL
& manifest_url
, int64 group_id
, int64 response_id
) {
1607 return new AppCacheResponseReader(response_id
, group_id
, disk_cache());
1610 AppCacheResponseWriter
* AppCacheStorageImpl::CreateResponseWriter(
1611 const GURL
& manifest_url
, int64 group_id
) {
1612 return new AppCacheResponseWriter(NewResponseId(), group_id
, disk_cache());
1615 void AppCacheStorageImpl::DoomResponses(
1616 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1617 if (response_ids
.empty())
1620 // Start deleting them from the disk cache lazily.
1621 StartDeletingResponses(response_ids
);
1623 // Also schedule a database task to record these ids in the
1624 // deletable responses table.
1625 // TODO(michaeln): There is a race here. If the browser crashes
1626 // prior to committing these rows to the database and prior to us
1627 // having deleted them from the disk cache, we'll never delete them.
1628 scoped_refptr
<InsertDeletableResponseIdsTask
> task(
1629 new InsertDeletableResponseIdsTask(this));
1630 task
->response_ids_
= response_ids
;
1634 void AppCacheStorageImpl::DeleteResponses(
1635 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1636 if (response_ids
.empty())
1638 StartDeletingResponses(response_ids
);
1641 void AppCacheStorageImpl::PurgeMemory() {
1642 scoped_refptr
<CloseConnectionTask
> task(new CloseConnectionTask(this));
1646 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
1647 // Only if we haven't already begun.
1648 if (!did_start_deleting_responses_
) {
1649 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1650 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1655 void AppCacheStorageImpl::StartDeletingResponses(
1656 const std::vector
<int64
>& response_ids
) {
1657 DCHECK(!response_ids
.empty());
1658 did_start_deleting_responses_
= true;
1659 deletable_response_ids_
.insert(
1660 deletable_response_ids_
.end(),
1661 response_ids
.begin(), response_ids
.end());
1662 if (!is_response_deletion_scheduled_
)
1663 ScheduleDeleteOneResponse();
1666 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
1667 DCHECK(!is_response_deletion_scheduled_
);
1668 const base::TimeDelta kDelay
= base::TimeDelta::FromMilliseconds(10);
1669 base::MessageLoop::current()->PostDelayedTask(
1671 base::Bind(&AppCacheStorageImpl::DeleteOneResponse
,
1672 weak_factory_
.GetWeakPtr()),
1674 is_response_deletion_scheduled_
= true;
1677 void AppCacheStorageImpl::DeleteOneResponse() {
1678 DCHECK(is_response_deletion_scheduled_
);
1679 DCHECK(!deletable_response_ids_
.empty());
1681 if (!disk_cache()) {
1682 DCHECK(is_disabled_
);
1683 deletable_response_ids_
.clear();
1684 deleted_response_ids_
.clear();
1685 is_response_deletion_scheduled_
= false;
1689 // TODO(michaeln): add group_id to DoomEntry args
1690 int64 id
= deletable_response_ids_
.front();
1691 int rv
= disk_cache_
->DoomEntry(
1692 id
, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse
,
1693 base::Unretained(this)));
1694 if (rv
!= net::ERR_IO_PENDING
)
1695 OnDeletedOneResponse(rv
);
1698 void AppCacheStorageImpl::OnDeletedOneResponse(int rv
) {
1699 is_response_deletion_scheduled_
= false;
1703 int64 id
= deletable_response_ids_
.front();
1704 deletable_response_ids_
.pop_front();
1705 if (rv
!= net::ERR_ABORTED
)
1706 deleted_response_ids_
.push_back(id
);
1708 const size_t kBatchSize
= 50U;
1709 if (deleted_response_ids_
.size() >= kBatchSize
||
1710 deletable_response_ids_
.empty()) {
1711 scoped_refptr
<DeleteDeletableResponseIdsTask
> task(
1712 new DeleteDeletableResponseIdsTask(this));
1713 task
->response_ids_
.swap(deleted_response_ids_
);
1717 if (deletable_response_ids_
.empty()) {
1718 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1719 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1724 ScheduleDeleteOneResponse();
1727 AppCacheStorageImpl::CacheLoadTask
*
1728 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id
) {
1729 PendingCacheLoads::iterator found
= pending_cache_loads_
.find(cache_id
);
1730 if (found
!= pending_cache_loads_
.end())
1731 return found
->second
;
1735 AppCacheStorageImpl::GroupLoadTask
*
1736 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL
& manifest_url
) {
1737 PendingGroupLoads::iterator found
= pending_group_loads_
.find(manifest_url
);
1738 if (found
!= pending_group_loads_
.end())
1739 return found
->second
;
1743 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
1744 int64 cache_id
, std::vector
<GURL
>* urls
) {
1745 PendingForeignMarkings::iterator iter
= pending_foreign_markings_
.begin();
1746 while (iter
!= pending_foreign_markings_
.end()) {
1747 if (iter
->second
== cache_id
)
1748 urls
->push_back(iter
->first
);
1753 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure
& task
) {
1754 pending_simple_tasks_
.push_back(task
);
1755 base::MessageLoop::current()->PostTask(
1757 base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask
,
1758 weak_factory_
.GetWeakPtr()));
1761 void AppCacheStorageImpl::RunOnePendingSimpleTask() {
1762 DCHECK(!pending_simple_tasks_
.empty());
1763 base::Closure task
= pending_simple_tasks_
.front();
1764 pending_simple_tasks_
.pop_front();
1768 AppCacheDiskCache
* AppCacheStorageImpl::disk_cache() {
1769 DCHECK(IsInitTaskComplete());
1776 disk_cache_
.reset(new AppCacheDiskCache
);
1777 if (is_incognito_
) {
1778 rv
= disk_cache_
->InitWithMemBackend(
1779 kMaxMemDiskCacheSize
,
1780 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1781 base::Unretained(this)));
1783 rv
= disk_cache_
->InitWithDiskBackend(
1784 cache_directory_
.Append(kDiskCacheDirectoryName
),
1787 cache_thread_
.get(),
1788 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1789 base::Unretained(this)));
1792 // We should not keep this reference around.
1793 cache_thread_
= NULL
;
1795 if (rv
!= net::ERR_IO_PENDING
)
1796 OnDiskCacheInitialized(rv
);
1798 return disk_cache_
.get();
1801 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv
) {
1802 if (rv
!= net::OK
) {
1803 LOG(ERROR
) << "Failed to open the appcache diskcache.";
1804 AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR
);
1806 // We're unable to open the disk cache, this is a fatal error that we can't
1807 // really recover from. We handle it by disabling the appcache for this
1808 // browser session and deleting the directory on disk. The next browser
1809 // session should start with a clean slate.
1811 if (!is_incognito_
) {
1812 VLOG(1) << "Deleting existing appcache data and starting over.";
1813 db_thread_
->PostTask(
1814 FROM_HERE
, base::Bind(base::IgnoreResult(&base::DeleteFile
),
1815 cache_directory_
, true));
1820 } // namespace appcache