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/logging.h"
16 #include "base/message_loop/message_loop.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 "content/browser/appcache/appcache.h"
22 #include "content/browser/appcache/appcache_database.h"
23 #include "content/browser/appcache/appcache_entry.h"
24 #include "content/browser/appcache/appcache_group.h"
25 #include "content/browser/appcache/appcache_histograms.h"
26 #include "content/browser/appcache/appcache_quota_client.h"
27 #include "content/browser/appcache/appcache_response.h"
28 #include "content/browser/appcache/appcache_service_impl.h"
29 #include "net/base/cache_type.h"
30 #include "net/base/net_errors.h"
31 #include "sql/connection.h"
32 #include "sql/transaction.h"
33 #include "storage/browser/quota/quota_client.h"
34 #include "storage/browser/quota/quota_manager.h"
35 #include "storage/browser/quota/quota_manager_proxy.h"
36 #include "storage/browser/quota/special_storage_policy.h"
40 // Hard coded default when not using quota management.
41 static const int kDefaultQuota
= 5 * 1024 * 1024;
43 static const int kMaxDiskCacheSize
= 250 * 1024 * 1024;
44 static const int kMaxMemDiskCacheSize
= 10 * 1024 * 1024;
45 static const base::FilePath::CharType kDiskCacheDirectoryName
[] =
46 FILE_PATH_LITERAL("Cache");
50 // Helpers for clearing data from the AppCacheDatabase.
51 bool DeleteGroupAndRelatedRecords(AppCacheDatabase
* database
,
53 std::vector
<int64
>* deletable_response_ids
) {
54 AppCacheDatabase::CacheRecord cache_record
;
56 if (database
->FindCacheForGroup(group_id
, &cache_record
)) {
57 database
->FindResponseIdsForCacheAsVector(cache_record
.cache_id
,
58 deletable_response_ids
);
60 database
->DeleteGroup(group_id
) &&
61 database
->DeleteCache(cache_record
.cache_id
) &&
62 database
->DeleteEntriesForCache(cache_record
.cache_id
) &&
63 database
->DeleteNamespacesForCache(cache_record
.cache_id
) &&
64 database
->DeleteOnlineWhiteListForCache(cache_record
.cache_id
) &&
65 database
->InsertDeletableResponseIds(*deletable_response_ids
);
67 NOTREACHED() << "A existing group without a cache is unexpected";
68 success
= database
->DeleteGroup(group_id
);
73 // Destroys |database|. If there is appcache data to be deleted
74 // (|force_keep_session_state| is false), deletes session-only appcache data.
75 void ClearSessionOnlyOrigins(
76 AppCacheDatabase
* database
,
77 scoped_refptr
<storage::SpecialStoragePolicy
> special_storage_policy
,
78 bool force_keep_session_state
) {
79 scoped_ptr
<AppCacheDatabase
> database_to_delete(database
);
81 // If saving session state, only delete the database.
82 if (force_keep_session_state
)
85 bool has_session_only_appcaches
=
86 special_storage_policy
.get() &&
87 special_storage_policy
->HasSessionOnlyOrigins();
89 // Clearning only session-only databases, and there are none.
90 if (!has_session_only_appcaches
)
93 std::set
<GURL
> origins
;
94 database
->FindOriginsWithGroups(&origins
);
96 return; // nothing to delete
98 sql::Connection
* connection
= database
->db_connection();
100 NOTREACHED() << "Missing database connection.";
104 std::set
<GURL
>::const_iterator origin
;
105 DCHECK(special_storage_policy
.get());
106 for (origin
= origins
.begin(); origin
!= origins
.end(); ++origin
) {
107 if (!special_storage_policy
->IsStorageSessionOnly(*origin
))
109 if (special_storage_policy
->IsStorageProtected(*origin
))
112 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
113 database
->FindGroupsForOrigin(*origin
, &groups
);
114 std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator group
;
115 for (group
= groups
.begin(); group
!= groups
.end(); ++group
) {
116 sql::Transaction
transaction(connection
);
117 if (!transaction
.Begin()) {
118 NOTREACHED() << "Failed to start transaction";
121 std::vector
<int64
> deletable_response_ids
;
122 bool success
= DeleteGroupAndRelatedRecords(database
,
124 &deletable_response_ids
);
125 success
= success
&& transaction
.Commit();
133 // DatabaseTask -----------------------------------------
135 class AppCacheStorageImpl::DatabaseTask
136 : public base::RefCountedThreadSafe
<DatabaseTask
> {
138 explicit DatabaseTask(AppCacheStorageImpl
* storage
)
139 : storage_(storage
), database_(storage
->database_
),
140 io_thread_(base::MessageLoopProxy::current()) {
141 DCHECK(io_thread_
.get());
144 void AddDelegate(DelegateReference
* delegate_reference
) {
145 delegates_
.push_back(make_scoped_refptr(delegate_reference
));
148 // Schedules a task to be Run() on the DB thread. Tasks
149 // are run in the order in which they are scheduled.
152 // Called on the DB thread.
153 virtual void Run() = 0;
155 // Called on the IO thread after Run() has completed.
156 virtual void RunCompleted() {}
158 // Once scheduled a task cannot be cancelled, but the
159 // call to RunCompleted may be. This method should only be
160 // called on the IO thread. This is used by AppCacheStorageImpl
161 // to cancel the completion calls when AppCacheStorageImpl is
162 // destructed. This method may be overriden to release or delete
163 // additional data associated with the task that is not DB thread
164 // safe. If overriden, this base class method must be called from
165 // within the override.
166 virtual void CancelCompletion();
169 friend class base::RefCountedThreadSafe
<DatabaseTask
>;
170 virtual ~DatabaseTask() {}
172 AppCacheStorageImpl
* storage_
;
173 AppCacheDatabase
* database_
;
174 DelegateReferenceVector delegates_
;
177 void CallRun(base::TimeTicks schedule_time
);
178 void CallRunCompleted(base::TimeTicks schedule_time
);
181 scoped_refptr
<base::MessageLoopProxy
> io_thread_
;
184 void AppCacheStorageImpl::DatabaseTask::Schedule() {
186 DCHECK(io_thread_
->BelongsToCurrentThread());
187 if (!storage_
->database_
)
190 if (storage_
->db_thread_
->PostTask(
192 base::Bind(&DatabaseTask::CallRun
, this, base::TimeTicks::Now()))) {
193 storage_
->scheduled_database_tasks_
.push_back(this);
195 NOTREACHED() << "Thread for database tasks is not running.";
199 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
200 DCHECK(io_thread_
->BelongsToCurrentThread());
205 void AppCacheStorageImpl::DatabaseTask::CallRun(
206 base::TimeTicks schedule_time
) {
207 AppCacheHistograms::AddTaskQueueTimeSample(
208 base::TimeTicks::Now() - schedule_time
);
209 if (!database_
->is_disabled()) {
210 base::TimeTicks run_time
= base::TimeTicks::Now();
212 AppCacheHistograms::AddTaskRunTimeSample(
213 base::TimeTicks::Now() - run_time
);
215 if (database_
->was_corruption_detected()) {
216 AppCacheHistograms::CountCorruptionDetected();
217 database_
->Disable();
219 if (database_
->is_disabled()) {
220 io_thread_
->PostTask(
222 base::Bind(&DatabaseTask::OnFatalError
, this));
225 io_thread_
->PostTask(
227 base::Bind(&DatabaseTask::CallRunCompleted
, this,
228 base::TimeTicks::Now()));
231 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
232 base::TimeTicks schedule_time
) {
233 AppCacheHistograms::AddCompletionQueueTimeSample(
234 base::TimeTicks::Now() - schedule_time
);
236 DCHECK(io_thread_
->BelongsToCurrentThread());
237 DCHECK(storage_
->scheduled_database_tasks_
.front() == this);
238 storage_
->scheduled_database_tasks_
.pop_front();
239 base::TimeTicks run_time
= base::TimeTicks::Now();
241 AppCacheHistograms::AddCompletionRunTimeSample(
242 base::TimeTicks::Now() - run_time
);
247 void AppCacheStorageImpl::DatabaseTask::OnFatalError() {
249 DCHECK(io_thread_
->BelongsToCurrentThread());
251 storage_
->DeleteAndStartOver();
257 class AppCacheStorageImpl::InitTask
: public DatabaseTask
{
259 explicit InitTask(AppCacheStorageImpl
* storage
)
260 : DatabaseTask(storage
), last_group_id_(0),
261 last_cache_id_(0), last_response_id_(0),
262 last_deletable_response_rowid_(0) {
263 if (!storage
->is_incognito_
) {
265 storage
->cache_directory_
.Append(kAppCacheDatabaseName
);
266 disk_cache_directory_
=
267 storage
->cache_directory_
.Append(kDiskCacheDirectoryName
);
273 void RunCompleted() override
;
276 ~InitTask() override
{}
279 base::FilePath db_file_path_
;
280 base::FilePath disk_cache_directory_
;
281 int64 last_group_id_
;
282 int64 last_cache_id_
;
283 int64 last_response_id_
;
284 int64 last_deletable_response_rowid_
;
285 std::map
<GURL
, int64
> usage_map_
;
288 void AppCacheStorageImpl::InitTask::Run() {
289 tracked_objects::ScopedTracker
tracking_profile(
290 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::InitTask"));
291 // If there is no sql database, ensure there is no disk cache either.
292 if (!db_file_path_
.empty() &&
293 !base::PathExists(db_file_path_
) &&
294 base::DirectoryExists(disk_cache_directory_
)) {
295 base::DeleteFile(disk_cache_directory_
, true);
296 if (base::DirectoryExists(disk_cache_directory_
)) {
297 database_
->Disable(); // This triggers OnFatalError handling.
302 database_
->FindLastStorageIds(
303 &last_group_id_
, &last_cache_id_
, &last_response_id_
,
304 &last_deletable_response_rowid_
);
305 database_
->GetAllOriginUsage(&usage_map_
);
308 void AppCacheStorageImpl::InitTask::RunCompleted() {
309 storage_
->last_group_id_
= last_group_id_
;
310 storage_
->last_cache_id_
= last_cache_id_
;
311 storage_
->last_response_id_
= last_response_id_
;
312 storage_
->last_deletable_response_rowid_
= last_deletable_response_rowid_
;
314 if (!storage_
->is_disabled()) {
315 storage_
->usage_map_
.swap(usage_map_
);
316 const base::TimeDelta kDelay
= base::TimeDelta::FromMinutes(5);
317 base::MessageLoop::current()->PostDelayedTask(
319 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses
,
320 storage_
->weak_factory_
.GetWeakPtr()),
324 if (storage_
->service()->quota_client())
325 storage_
->service()->quota_client()->NotifyAppCacheReady();
328 // DisableDatabaseTask -------
330 class AppCacheStorageImpl::DisableDatabaseTask
: public DatabaseTask
{
332 explicit DisableDatabaseTask(AppCacheStorageImpl
* storage
)
333 : DatabaseTask(storage
) {}
336 void Run() override
{ database_
->Disable(); }
339 ~DisableDatabaseTask() override
{}
342 // GetAllInfoTask -------
344 class AppCacheStorageImpl::GetAllInfoTask
: public DatabaseTask
{
346 explicit GetAllInfoTask(AppCacheStorageImpl
* storage
)
347 : DatabaseTask(storage
),
348 info_collection_(new AppCacheInfoCollection()) {
353 void RunCompleted() override
;
356 ~GetAllInfoTask() override
{}
359 scoped_refptr
<AppCacheInfoCollection
> info_collection_
;
362 void AppCacheStorageImpl::GetAllInfoTask::Run() {
363 std::set
<GURL
> origins
;
364 database_
->FindOriginsWithGroups(&origins
);
365 for (std::set
<GURL
>::const_iterator origin
= origins
.begin();
366 origin
!= origins
.end(); ++origin
) {
367 AppCacheInfoVector
& infos
=
368 info_collection_
->infos_by_origin
[*origin
];
369 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
370 database_
->FindGroupsForOrigin(*origin
, &groups
);
371 for (std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator
372 group
= groups
.begin();
373 group
!= groups
.end(); ++group
) {
374 AppCacheDatabase::CacheRecord cache_record
;
375 database_
->FindCacheForGroup(group
->group_id
, &cache_record
);
377 info
.manifest_url
= group
->manifest_url
;
378 info
.creation_time
= group
->creation_time
;
379 info
.size
= cache_record
.cache_size
;
380 info
.last_access_time
= group
->last_access_time
;
381 info
.last_update_time
= cache_record
.update_time
;
382 info
.cache_id
= cache_record
.cache_id
;
383 info
.group_id
= group
->group_id
;
384 info
.is_complete
= true;
385 infos
.push_back(info
);
390 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
391 DCHECK_EQ(1U, delegates_
.size());
392 FOR_EACH_DELEGATE(delegates_
, OnAllInfo(info_collection_
.get()));
395 // StoreOrLoadTask -------
397 class AppCacheStorageImpl::StoreOrLoadTask
: public DatabaseTask
{
399 explicit StoreOrLoadTask(AppCacheStorageImpl
* storage
)
400 : DatabaseTask(storage
) {}
401 ~StoreOrLoadTask() override
{}
403 bool FindRelatedCacheRecords(int64 cache_id
);
404 void CreateCacheAndGroupFromRecords(
405 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
);
407 AppCacheDatabase::GroupRecord group_record_
;
408 AppCacheDatabase::CacheRecord cache_record_
;
409 std::vector
<AppCacheDatabase::EntryRecord
> entry_records_
;
410 std::vector
<AppCacheDatabase::NamespaceRecord
>
411 intercept_namespace_records_
;
412 std::vector
<AppCacheDatabase::NamespaceRecord
>
413 fallback_namespace_records_
;
414 std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
415 online_whitelist_records_
;
418 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
420 return database_
->FindEntriesForCache(cache_id
, &entry_records_
) &&
421 database_
->FindNamespacesForCache(
422 cache_id
, &intercept_namespace_records_
,
423 &fallback_namespace_records_
) &&
424 database_
->FindOnlineWhiteListForCache(
425 cache_id
, &online_whitelist_records_
);
428 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
429 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
) {
430 DCHECK(storage_
&& cache
&& group
);
432 (*cache
) = storage_
->working_set_
.GetCache(cache_record_
.cache_id
);
434 (*group
) = cache
->get()->owning_group();
435 DCHECK(group
->get());
436 DCHECK_EQ(group_record_
.group_id
, group
->get()->group_id());
438 // TODO(michaeln): histogram is fishing for clues to crbug/95101
439 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
440 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
441 AppCacheHistograms::CALLSITE_0
);
444 storage_
->NotifyStorageAccessed(group_record_
.origin
);
448 (*cache
) = new AppCache(storage_
, cache_record_
.cache_id
);
449 cache
->get()->InitializeWithDatabaseRecords(
450 cache_record_
, entry_records_
,
451 intercept_namespace_records_
,
452 fallback_namespace_records_
,
453 online_whitelist_records_
);
454 cache
->get()->set_complete(true);
456 (*group
) = storage_
->working_set_
.GetGroup(group_record_
.manifest_url
);
458 DCHECK(group_record_
.group_id
== group
->get()->group_id());
459 group
->get()->AddCache(cache
->get());
461 // TODO(michaeln): histogram is fishing for clues to crbug/95101
462 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
463 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
464 AppCacheHistograms::CALLSITE_1
);
467 (*group
) = new AppCacheGroup(
468 storage_
, group_record_
.manifest_url
,
469 group_record_
.group_id
);
470 group
->get()->set_creation_time(group_record_
.creation_time
);
471 group
->get()->AddCache(cache
->get());
473 // TODO(michaeln): histogram is fishing for clues to crbug/95101
474 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
475 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
476 AppCacheHistograms::CALLSITE_2
);
479 DCHECK(group
->get()->newest_complete_cache() == cache
->get());
481 // We have to update foriegn entries if MarkEntryAsForeignTasks
483 std::vector
<GURL
> urls
;
484 storage_
->GetPendingForeignMarkingsForCache(cache
->get()->cache_id(), &urls
);
485 for (std::vector
<GURL
>::iterator iter
= urls
.begin();
486 iter
!= urls
.end(); ++iter
) {
487 DCHECK(cache
->get()->GetEntry(*iter
));
488 cache
->get()->GetEntry(*iter
)->add_types(AppCacheEntry::FOREIGN
);
491 storage_
->NotifyStorageAccessed(group_record_
.origin
);
493 // TODO(michaeln): Maybe verify that the responses we expect to exist
494 // do actually exist in the disk_cache (and if not then what?)
497 // CacheLoadTask -------
499 class AppCacheStorageImpl::CacheLoadTask
: public StoreOrLoadTask
{
501 CacheLoadTask(int64 cache_id
, AppCacheStorageImpl
* storage
)
502 : StoreOrLoadTask(storage
), cache_id_(cache_id
),
507 void RunCompleted() override
;
510 ~CacheLoadTask() override
{}
517 void AppCacheStorageImpl::CacheLoadTask::Run() {
518 tracked_objects::ScopedTracker
tracking_profile(
519 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::CacheLoadTask"));
521 database_
->FindCache(cache_id_
, &cache_record_
) &&
522 database_
->FindGroup(cache_record_
.group_id
, &group_record_
) &&
523 FindRelatedCacheRecords(cache_id_
);
526 database_
->LazyUpdateLastAccessTime(group_record_
.group_id
,
530 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
531 storage_
->pending_cache_loads_
.erase(cache_id_
);
532 scoped_refptr
<AppCache
> cache
;
533 scoped_refptr
<AppCacheGroup
> group
;
534 if (success_
&& !storage_
->is_disabled()) {
535 storage_
->LazilyCommitLastAccessTimes();
536 DCHECK(cache_record_
.cache_id
== cache_id_
);
537 CreateCacheAndGroupFromRecords(&cache
, &group
);
539 FOR_EACH_DELEGATE(delegates_
, OnCacheLoaded(cache
.get(), cache_id_
));
542 // GroupLoadTask -------
544 class AppCacheStorageImpl::GroupLoadTask
: public StoreOrLoadTask
{
546 GroupLoadTask(GURL manifest_url
, AppCacheStorageImpl
* storage
)
547 : StoreOrLoadTask(storage
), manifest_url_(manifest_url
),
552 void RunCompleted() override
;
555 ~GroupLoadTask() override
{}
562 void AppCacheStorageImpl::GroupLoadTask::Run() {
563 tracked_objects::ScopedTracker
tracking_profile(
564 FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::GroupLoadTask"));
566 database_
->FindGroupForManifestUrl(manifest_url_
, &group_record_
) &&
567 database_
->FindCacheForGroup(group_record_
.group_id
, &cache_record_
) &&
568 FindRelatedCacheRecords(cache_record_
.cache_id
);
571 database_
->LazyUpdateLastAccessTime(group_record_
.group_id
,
575 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
576 storage_
->pending_group_loads_
.erase(manifest_url_
);
577 scoped_refptr
<AppCacheGroup
> group
;
578 scoped_refptr
<AppCache
> cache
;
579 if (!storage_
->is_disabled()) {
581 storage_
->LazilyCommitLastAccessTimes();
582 DCHECK(group_record_
.manifest_url
== manifest_url_
);
583 CreateCacheAndGroupFromRecords(&cache
, &group
);
585 group
= storage_
->working_set_
.GetGroup(manifest_url_
);
588 new AppCacheGroup(storage_
, manifest_url_
, storage_
->NewGroupId());
592 FOR_EACH_DELEGATE(delegates_
, OnGroupLoaded(group
.get(), manifest_url_
));
595 // StoreGroupAndCacheTask -------
597 class AppCacheStorageImpl::StoreGroupAndCacheTask
: public StoreOrLoadTask
{
599 StoreGroupAndCacheTask(AppCacheStorageImpl
* storage
, AppCacheGroup
* group
,
600 AppCache
* newest_cache
);
602 void GetQuotaThenSchedule();
603 void OnQuotaCallback(storage::QuotaStatusCode status
,
609 void RunCompleted() override
;
610 void CancelCompletion() override
;
613 ~StoreGroupAndCacheTask() override
{}
616 scoped_refptr
<AppCacheGroup
> group_
;
617 scoped_refptr
<AppCache
> cache_
;
619 bool would_exceed_quota_
;
620 int64 space_available_
;
621 int64 new_origin_usage_
;
622 std::vector
<int64
> newly_deletable_response_ids_
;
625 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
626 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, AppCache
* newest_cache
)
627 : StoreOrLoadTask(storage
), group_(group
), cache_(newest_cache
),
628 success_(false), would_exceed_quota_(false),
629 space_available_(-1), new_origin_usage_(-1) {
630 group_record_
.group_id
= group
->group_id();
631 group_record_
.manifest_url
= group
->manifest_url();
632 group_record_
.origin
= group_record_
.manifest_url
.GetOrigin();
633 newest_cache
->ToDatabaseRecords(
635 &cache_record_
, &entry_records_
,
636 &intercept_namespace_records_
,
637 &fallback_namespace_records_
,
638 &online_whitelist_records_
);
641 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
642 storage::QuotaManager
* quota_manager
= NULL
;
643 if (storage_
->service()->quota_manager_proxy()) {
645 storage_
->service()->quota_manager_proxy()->quota_manager();
648 if (!quota_manager
) {
649 if (storage_
->service()->special_storage_policy() &&
650 storage_
->service()->special_storage_policy()->IsStorageUnlimited(
651 group_record_
.origin
))
652 space_available_
= kint64max
;
657 // We have to ask the quota manager for the value.
658 storage_
->pending_quota_queries_
.insert(this);
659 quota_manager
->GetUsageAndQuota(
660 group_record_
.origin
,
661 storage::kStorageTypeTemporary
,
662 base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback
, this));
665 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
666 storage::QuotaStatusCode status
,
670 if (status
== storage::kQuotaStatusOk
)
671 space_available_
= std::max(static_cast<int64
>(0), quota
- usage
);
673 space_available_
= 0;
674 storage_
->pending_quota_queries_
.erase(this);
679 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
681 sql::Connection
* connection
= database_
->db_connection();
685 sql::Transaction
transaction(connection
);
686 if (!transaction
.Begin())
689 int64 old_origin_usage
= database_
->GetOriginUsage(group_record_
.origin
);
691 AppCacheDatabase::GroupRecord existing_group
;
692 success_
= database_
->FindGroup(group_record_
.group_id
, &existing_group
);
694 group_record_
.creation_time
= base::Time::Now();
695 group_record_
.last_access_time
= base::Time::Now();
696 success_
= database_
->InsertGroup(&group_record_
);
698 DCHECK(group_record_
.group_id
== existing_group
.group_id
);
699 DCHECK(group_record_
.manifest_url
== existing_group
.manifest_url
);
700 DCHECK(group_record_
.origin
== existing_group
.origin
);
702 database_
->UpdateLastAccessTime(group_record_
.group_id
,
705 AppCacheDatabase::CacheRecord cache
;
706 if (database_
->FindCacheForGroup(group_record_
.group_id
, &cache
)) {
707 // Get the set of response ids in the old cache.
708 std::set
<int64
> existing_response_ids
;
709 database_
->FindResponseIdsForCacheAsSet(cache
.cache_id
,
710 &existing_response_ids
);
712 // Remove those that remain in the new cache.
713 std::vector
<AppCacheDatabase::EntryRecord
>::const_iterator entry_iter
=
714 entry_records_
.begin();
715 while (entry_iter
!= entry_records_
.end()) {
716 existing_response_ids
.erase(entry_iter
->response_id
);
720 // The rest are deletable.
721 std::set
<int64
>::const_iterator id_iter
= existing_response_ids
.begin();
722 while (id_iter
!= existing_response_ids
.end()) {
723 newly_deletable_response_ids_
.push_back(*id_iter
);
728 database_
->DeleteCache(cache
.cache_id
) &&
729 database_
->DeleteEntriesForCache(cache
.cache_id
) &&
730 database_
->DeleteNamespacesForCache(cache
.cache_id
) &&
731 database_
->DeleteOnlineWhiteListForCache(cache
.cache_id
) &&
732 database_
->InsertDeletableResponseIds(newly_deletable_response_ids_
);
733 // TODO(michaeln): store group_id too with deletable ids
735 NOTREACHED() << "A existing group without a cache is unexpected";
741 database_
->InsertCache(&cache_record_
) &&
742 database_
->InsertEntryRecords(entry_records_
) &&
743 database_
->InsertNamespaceRecords(intercept_namespace_records_
) &&
744 database_
->InsertNamespaceRecords(fallback_namespace_records_
) &&
745 database_
->InsertOnlineWhiteListRecords(online_whitelist_records_
);
750 new_origin_usage_
= database_
->GetOriginUsage(group_record_
.origin
);
752 // Only check quota when the new usage exceeds the old usage.
753 if (new_origin_usage_
<= old_origin_usage
) {
754 success_
= transaction
.Commit();
758 // Use a simple hard-coded value when not using quota management.
759 if (space_available_
== -1) {
760 if (new_origin_usage_
> kDefaultQuota
) {
761 would_exceed_quota_
= true;
765 success_
= transaction
.Commit();
769 // Check limits based on the space availbable given to us via the
771 int64 delta
= new_origin_usage_
- old_origin_usage
;
772 if (delta
> space_available_
) {
773 would_exceed_quota_
= true;
778 success_
= transaction
.Commit();
781 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
783 storage_
->UpdateUsageMapAndNotify(
784 group_
->manifest_url().GetOrigin(), new_origin_usage_
);
785 if (cache_
.get() != group_
->newest_complete_cache()) {
786 cache_
->set_complete(true);
787 group_
->AddCache(cache_
.get());
789 if (group_
->creation_time().is_null())
790 group_
->set_creation_time(group_record_
.creation_time
);
791 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
795 OnGroupAndNewestCacheStored(
796 group_
.get(), cache_
.get(), success_
, would_exceed_quota_
));
800 // TODO(michaeln): if (would_exceed_quota_) what if the current usage
801 // also exceeds the quota? http://crbug.com/83968
804 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
805 // Overriden to safely drop our reference to the group and cache
806 // which are not thread safe refcounted.
807 DatabaseTask::CancelCompletion();
812 // FindMainResponseTask -------
814 // Helpers for FindMainResponseTask::Run()
816 class SortByCachePreference
817 : public std::binary_function
<
818 AppCacheDatabase::EntryRecord
,
819 AppCacheDatabase::EntryRecord
,
822 SortByCachePreference(int64 preferred_id
, const std::set
<int64
>& in_use_ids
)
823 : preferred_id_(preferred_id
), in_use_ids_(in_use_ids
) {
826 const AppCacheDatabase::EntryRecord
& lhs
,
827 const AppCacheDatabase::EntryRecord
& rhs
) {
828 return compute_value(lhs
) > compute_value(rhs
);
831 int compute_value(const AppCacheDatabase::EntryRecord
& entry
) {
832 if (entry
.cache_id
== preferred_id_
)
834 else if (in_use_ids_
.find(entry
.cache_id
) != in_use_ids_
.end())
839 const std::set
<int64
>& in_use_ids_
;
843 const AppCacheDatabase::NamespaceRecord
& lhs
,
844 const AppCacheDatabase::NamespaceRecord
& rhs
) {
845 return lhs
.namespace_
.namespace_url
.spec().length() >
846 rhs
.namespace_
.namespace_url
.spec().length();
849 class NetworkNamespaceHelper
{
851 explicit NetworkNamespaceHelper(AppCacheDatabase
* database
)
852 : database_(database
) {
855 bool IsInNetworkNamespace(const GURL
& url
, int64 cache_id
) {
856 typedef std::pair
<WhiteListMap::iterator
, bool> InsertResult
;
857 InsertResult result
= namespaces_map_
.insert(
858 WhiteListMap::value_type(cache_id
, AppCacheNamespaceVector()));
860 GetOnlineWhiteListForCache(cache_id
, &result
.first
->second
);
861 return AppCache::FindNamespace(result
.first
->second
, url
) != NULL
;
865 void GetOnlineWhiteListForCache(
866 int64 cache_id
, AppCacheNamespaceVector
* namespaces
) {
867 DCHECK(namespaces
&& namespaces
->empty());
868 typedef std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
870 WhiteListVector records
;
871 if (!database_
->FindOnlineWhiteListForCache(cache_id
, &records
))
873 WhiteListVector::const_iterator iter
= records
.begin();
874 while (iter
!= records
.end()) {
875 namespaces
->push_back(
876 AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE
, iter
->namespace_url
,
877 GURL(), iter
->is_pattern
));
883 typedef std::map
<int64
, AppCacheNamespaceVector
> WhiteListMap
;
884 WhiteListMap namespaces_map_
;
885 AppCacheDatabase
* database_
;
890 class AppCacheStorageImpl::FindMainResponseTask
: public DatabaseTask
{
892 FindMainResponseTask(AppCacheStorageImpl
* storage
,
894 const GURL
& preferred_manifest_url
,
895 const AppCacheWorkingSet::GroupMap
* groups_in_use
)
896 : DatabaseTask(storage
), url_(url
),
897 preferred_manifest_url_(preferred_manifest_url
),
898 cache_id_(kAppCacheNoCacheId
), group_id_(0) {
900 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
901 groups_in_use
->begin();
902 it
!= groups_in_use
->end(); ++it
) {
903 AppCacheGroup
* group
= it
->second
;
904 AppCache
* cache
= group
->newest_complete_cache();
905 if (group
->is_obsolete() || !cache
)
907 cache_ids_in_use_
.insert(cache
->cache_id());
914 void RunCompleted() override
;
917 ~FindMainResponseTask() override
{}
920 typedef std::vector
<AppCacheDatabase::NamespaceRecord
*>
921 NamespaceRecordPtrVector
;
923 bool FindExactMatch(int64 preferred_id
);
924 bool FindNamespaceMatch(int64 preferred_id
);
925 bool FindNamespaceHelper(
926 int64 preferred_cache_id
,
927 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
928 NetworkNamespaceHelper
* network_namespace_helper
);
929 bool FindFirstValidNamespace(const NamespaceRecordPtrVector
& namespaces
);
932 GURL preferred_manifest_url_
;
933 std::set
<int64
> cache_ids_in_use_
;
934 AppCacheEntry entry_
;
935 AppCacheEntry fallback_entry_
;
936 GURL namespace_entry_url_
;
942 void AppCacheStorageImpl::FindMainResponseTask::Run() {
943 tracked_objects::ScopedTracker
tracking_profile(
944 FROM_HERE_WITH_EXPLICIT_FUNCTION(
945 "AppCacheStorageImpl::FindMainResponseTask"));
946 // NOTE: The heuristics around choosing amoungst multiple candidates
947 // is underspecified, and just plain not fully understood. This needs
950 // The 'preferred_manifest_url' is the url of the manifest associated
951 // with the page that opened or embedded the page being loaded now.
952 // We have a strong preference to use resources from that cache.
953 // We also have a lesser bias to use resources from caches that are currently
954 // being used by other unrelated pages.
955 // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
956 // - when navigating a frame whose current contents are from an appcache
957 // - when clicking an href in a frame that is appcached
958 int64 preferred_cache_id
= kAppCacheNoCacheId
;
959 if (!preferred_manifest_url_
.is_empty()) {
960 AppCacheDatabase::GroupRecord preferred_group
;
961 AppCacheDatabase::CacheRecord preferred_cache
;
962 if (database_
->FindGroupForManifestUrl(
963 preferred_manifest_url_
, &preferred_group
) &&
964 database_
->FindCacheForGroup(
965 preferred_group
.group_id
, &preferred_cache
)) {
966 preferred_cache_id
= preferred_cache
.cache_id
;
970 if (FindExactMatch(preferred_cache_id
) ||
971 FindNamespaceMatch(preferred_cache_id
)) {
972 // We found something.
973 DCHECK(cache_id_
!= kAppCacheNoCacheId
&& !manifest_url_
.is_empty() &&
978 // We didn't find anything.
979 DCHECK(cache_id_
== kAppCacheNoCacheId
&& manifest_url_
.is_empty() &&
983 bool AppCacheStorageImpl::
984 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id
) {
985 std::vector
<AppCacheDatabase::EntryRecord
> entries
;
986 if (database_
->FindEntriesForUrl(url_
, &entries
) && !entries
.empty()) {
987 // Sort them in order of preference, from the preferred_cache first,
988 // followed by hits from caches that are 'in use', then the rest.
989 std::sort(entries
.begin(), entries
.end(),
990 SortByCachePreference(preferred_cache_id
, cache_ids_in_use_
));
992 // Take the first with a valid, non-foreign entry.
993 std::vector
<AppCacheDatabase::EntryRecord
>::iterator iter
;
994 for (iter
= entries
.begin(); iter
< entries
.end(); ++iter
) {
995 AppCacheDatabase::GroupRecord group_record
;
996 if ((iter
->flags
& AppCacheEntry::FOREIGN
) ||
997 !database_
->FindGroupForCache(iter
->cache_id
, &group_record
)) {
1000 manifest_url_
= group_record
.manifest_url
;
1001 group_id_
= group_record
.group_id
;
1002 entry_
= AppCacheEntry(iter
->flags
, iter
->response_id
);
1003 cache_id_
= iter
->cache_id
;
1004 return true; // We found an exact match.
1010 bool AppCacheStorageImpl::
1011 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id
) {
1012 AppCacheDatabase::NamespaceRecordVector all_intercepts
;
1013 AppCacheDatabase::NamespaceRecordVector all_fallbacks
;
1014 if (!database_
->FindNamespacesForOrigin(
1015 url_
.GetOrigin(), &all_intercepts
, &all_fallbacks
)
1016 || (all_intercepts
.empty() && all_fallbacks
.empty())) {
1020 NetworkNamespaceHelper
network_namespace_helper(database_
);
1021 if (FindNamespaceHelper(preferred_cache_id
,
1023 &network_namespace_helper
) ||
1024 FindNamespaceHelper(preferred_cache_id
,
1026 &network_namespace_helper
)) {
1032 bool AppCacheStorageImpl::
1033 FindMainResponseTask::FindNamespaceHelper(
1034 int64 preferred_cache_id
,
1035 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
1036 NetworkNamespaceHelper
* network_namespace_helper
) {
1037 // Sort them by length, longer matches within the same cache/bucket take
1039 std::sort(namespaces
->begin(), namespaces
->end(), SortByLength
);
1041 NamespaceRecordPtrVector preferred_namespaces
;
1042 NamespaceRecordPtrVector inuse_namespaces
;
1043 NamespaceRecordPtrVector other_namespaces
;
1044 std::vector
<AppCacheDatabase::NamespaceRecord
>::iterator iter
;
1045 for (iter
= namespaces
->begin(); iter
< namespaces
->end(); ++iter
) {
1046 // Skip those that aren't a match.
1047 if (!iter
->namespace_
.IsMatch(url_
))
1050 // Skip namespaces where the requested url falls into a network
1051 // namespace of its containing appcache.
1052 if (network_namespace_helper
->IsInNetworkNamespace(url_
, iter
->cache_id
))
1055 // Bin them into one of our three buckets.
1056 if (iter
->cache_id
== preferred_cache_id
)
1057 preferred_namespaces
.push_back(&(*iter
));
1058 else if (cache_ids_in_use_
.find(iter
->cache_id
) != cache_ids_in_use_
.end())
1059 inuse_namespaces
.push_back(&(*iter
));
1061 other_namespaces
.push_back(&(*iter
));
1064 if (FindFirstValidNamespace(preferred_namespaces
) ||
1065 FindFirstValidNamespace(inuse_namespaces
) ||
1066 FindFirstValidNamespace(other_namespaces
))
1067 return true; // We found one.
1069 // We didn't find anything.
1073 bool AppCacheStorageImpl::
1074 FindMainResponseTask::FindFirstValidNamespace(
1075 const NamespaceRecordPtrVector
& namespaces
) {
1076 // Take the first with a valid, non-foreign entry.
1077 NamespaceRecordPtrVector::const_iterator iter
;
1078 for (iter
= namespaces
.begin(); iter
< namespaces
.end(); ++iter
) {
1079 AppCacheDatabase::EntryRecord entry_record
;
1080 if (database_
->FindEntry((*iter
)->cache_id
, (*iter
)->namespace_
.target_url
,
1082 AppCacheDatabase::GroupRecord group_record
;
1083 if ((entry_record
.flags
& AppCacheEntry::FOREIGN
) ||
1084 !database_
->FindGroupForCache(entry_record
.cache_id
, &group_record
)) {
1087 manifest_url_
= group_record
.manifest_url
;
1088 group_id_
= group_record
.group_id
;
1089 cache_id_
= (*iter
)->cache_id
;
1090 namespace_entry_url_
= (*iter
)->namespace_
.target_url
;
1091 if ((*iter
)->namespace_
.type
== APPCACHE_FALLBACK_NAMESPACE
)
1092 fallback_entry_
= AppCacheEntry(entry_record
.flags
,
1093 entry_record
.response_id
);
1095 entry_
= AppCacheEntry(entry_record
.flags
, entry_record
.response_id
);
1096 return true; // We found one.
1099 return false; // We didn't find a match.
1102 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
1103 storage_
->CallOnMainResponseFound(
1104 &delegates_
, url_
, entry_
, namespace_entry_url_
, fallback_entry_
,
1105 cache_id_
, group_id_
, manifest_url_
);
1108 // MarkEntryAsForeignTask -------
1110 class AppCacheStorageImpl::MarkEntryAsForeignTask
: public DatabaseTask
{
1112 MarkEntryAsForeignTask(
1113 AppCacheStorageImpl
* storage
, const GURL
& url
, int64 cache_id
)
1114 : DatabaseTask(storage
), cache_id_(cache_id
), entry_url_(url
) {}
1117 void Run() override
;
1118 void RunCompleted() override
;
1121 ~MarkEntryAsForeignTask() override
{}
1128 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
1129 database_
->AddEntryFlags(entry_url_
, cache_id_
, AppCacheEntry::FOREIGN
);
1132 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
1133 DCHECK(storage_
->pending_foreign_markings_
.front().first
== entry_url_
&&
1134 storage_
->pending_foreign_markings_
.front().second
== cache_id_
);
1135 storage_
->pending_foreign_markings_
.pop_front();
1138 // MakeGroupObsoleteTask -------
1140 class AppCacheStorageImpl::MakeGroupObsoleteTask
: public DatabaseTask
{
1142 MakeGroupObsoleteTask(AppCacheStorageImpl
* storage
,
1143 AppCacheGroup
* group
,
1147 void Run() override
;
1148 void RunCompleted() override
;
1149 void CancelCompletion() override
;
1152 ~MakeGroupObsoleteTask() override
{}
1155 scoped_refptr
<AppCacheGroup
> group_
;
1160 int64 new_origin_usage_
;
1161 std::vector
<int64
> newly_deletable_response_ids_
;
1164 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
1165 AppCacheStorageImpl
* storage
,
1166 AppCacheGroup
* group
,
1168 : DatabaseTask(storage
),
1170 group_id_(group
->group_id()),
1171 origin_(group
->manifest_url().GetOrigin()),
1173 response_code_(response_code
),
1174 new_origin_usage_(-1) {}
1176 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
1178 sql::Connection
* connection
= database_
->db_connection();
1182 sql::Transaction
transaction(connection
);
1183 if (!transaction
.Begin())
1186 AppCacheDatabase::GroupRecord group_record
;
1187 if (!database_
->FindGroup(group_id_
, &group_record
)) {
1188 // This group doesn't exists in the database, nothing todo here.
1189 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1194 DCHECK_EQ(group_record
.origin
, origin_
);
1195 success_
= DeleteGroupAndRelatedRecords(database_
,
1197 &newly_deletable_response_ids_
);
1199 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1200 success_
= success_
&& transaction
.Commit();
1203 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
1205 group_
->set_obsolete(true);
1206 if (!storage_
->is_disabled()) {
1207 storage_
->UpdateUsageMapAndNotify(origin_
, new_origin_usage_
);
1208 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
1210 // Also remove from the working set, caches for an 'obsolete' group
1211 // may linger in use, but the group itself cannot be looked up by
1212 // 'manifest_url' in the working set any longer.
1213 storage_
->working_set()->RemoveGroup(group_
.get());
1217 delegates_
, OnGroupMadeObsolete(group_
.get(), success_
, response_code_
));
1221 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
1222 // Overriden to safely drop our reference to the group
1223 // which is not thread safe refcounted.
1224 DatabaseTask::CancelCompletion();
1228 // GetDeletableResponseIdsTask -------
1230 class AppCacheStorageImpl::GetDeletableResponseIdsTask
: public DatabaseTask
{
1232 GetDeletableResponseIdsTask(AppCacheStorageImpl
* storage
, int64 max_rowid
)
1233 : DatabaseTask(storage
), max_rowid_(max_rowid
) {}
1236 void Run() override
;
1237 void RunCompleted() override
;
1240 ~GetDeletableResponseIdsTask() override
{}
1244 std::vector
<int64
> response_ids_
;
1247 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
1248 const int kSqlLimit
= 1000;
1249 database_
->GetDeletableResponseIds(&response_ids_
, max_rowid_
, kSqlLimit
);
1250 // TODO(michaeln): retrieve group_ids too
1253 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
1254 if (!response_ids_
.empty())
1255 storage_
->StartDeletingResponses(response_ids_
);
1258 // InsertDeletableResponseIdsTask -------
1260 class AppCacheStorageImpl::InsertDeletableResponseIdsTask
1261 : public DatabaseTask
{
1263 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1264 : DatabaseTask(storage
) {}
1267 void Run() override
;
1269 std::vector
<int64
> response_ids_
;
1272 ~InsertDeletableResponseIdsTask() override
{}
1275 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
1276 database_
->InsertDeletableResponseIds(response_ids_
);
1277 // TODO(michaeln): store group_ids too
1280 // DeleteDeletableResponseIdsTask -------
1282 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
1283 : public DatabaseTask
{
1285 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1286 : DatabaseTask(storage
) {}
1289 void Run() override
;
1291 std::vector
<int64
> response_ids_
;
1294 ~DeleteDeletableResponseIdsTask() override
{}
1297 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1298 database_
->DeleteDeletableResponseIds(response_ids_
);
1301 // LazyUpdateLastAccessTimeTask -------
1303 class AppCacheStorageImpl::LazyUpdateLastAccessTimeTask
1304 : public DatabaseTask
{
1306 LazyUpdateLastAccessTimeTask(
1307 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, base::Time time
)
1308 : DatabaseTask(storage
), group_id_(group
->group_id()),
1309 last_access_time_(time
) {
1310 storage
->NotifyStorageAccessed(group
->manifest_url().GetOrigin());
1314 void Run() override
;
1315 void RunCompleted() override
;
1318 ~LazyUpdateLastAccessTimeTask() override
{}
1322 base::Time last_access_time_
;
1325 void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::Run() {
1326 tracked_objects::ScopedTracker
tracking_profile(
1327 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1328 "AppCacheStorageImpl::LazyUpdateLastAccessTimeTask"));
1329 database_
->LazyUpdateLastAccessTime(group_id_
, last_access_time_
);
1332 void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::RunCompleted() {
1333 storage_
->LazilyCommitLastAccessTimes();
1336 // CommitLastAccessTimesTask -------
1338 class AppCacheStorageImpl::CommitLastAccessTimesTask
1339 : public DatabaseTask
{
1341 CommitLastAccessTimesTask(AppCacheStorageImpl
* storage
)
1342 : DatabaseTask(storage
) {}
1345 void Run() override
{
1346 tracked_objects::ScopedTracker
tracking_profile(
1347 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1348 "AppCacheStorageImpl::CommitLastAccessTimesTask"));
1349 database_
->CommitLazyLastAccessTimes();
1353 ~CommitLastAccessTimesTask() override
{}
1356 // AppCacheStorageImpl ---------------------------------------------------
1358 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl
* service
)
1359 : AppCacheStorage(service
),
1360 is_incognito_(false),
1361 is_response_deletion_scheduled_(false),
1362 did_start_deleting_responses_(false),
1363 last_deletable_response_rowid_(0),
1365 is_disabled_(false),
1366 weak_factory_(this) {
1369 AppCacheStorageImpl::~AppCacheStorageImpl() {
1370 std::for_each(pending_quota_queries_
.begin(),
1371 pending_quota_queries_
.end(),
1372 std::mem_fun(&DatabaseTask::CancelCompletion
));
1373 std::for_each(scheduled_database_tasks_
.begin(),
1374 scheduled_database_tasks_
.end(),
1375 std::mem_fun(&DatabaseTask::CancelCompletion
));
1378 !db_thread_
->PostTask(
1380 base::Bind(&ClearSessionOnlyOrigins
,
1382 make_scoped_refptr(service_
->special_storage_policy()),
1383 service()->force_keep_session_state()))) {
1386 database_
= NULL
; // So no further database tasks can be scheduled.
1389 void AppCacheStorageImpl::Initialize(
1390 const base::FilePath
& cache_directory
,
1391 const scoped_refptr
<base::SingleThreadTaskRunner
>& db_thread
,
1392 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
) {
1393 DCHECK(db_thread
.get());
1395 cache_directory_
= cache_directory
;
1396 is_incognito_
= cache_directory_
.empty();
1398 base::FilePath db_file_path
;
1400 db_file_path
= cache_directory_
.Append(kAppCacheDatabaseName
);
1401 database_
= new AppCacheDatabase(db_file_path
);
1403 db_thread_
= db_thread
;
1404 cache_thread_
= cache_thread
;
1406 scoped_refptr
<InitTask
> task(new InitTask(this));
1410 void AppCacheStorageImpl::Disable() {
1413 VLOG(1) << "Disabling appcache storage.";
1414 is_disabled_
= true;
1415 ClearUsageMapAndNotify();
1416 working_set()->Disable();
1418 disk_cache_
->Disable();
1419 scoped_refptr
<DisableDatabaseTask
> task(new DisableDatabaseTask(this));
1423 void AppCacheStorageImpl::GetAllInfo(Delegate
* delegate
) {
1425 scoped_refptr
<GetAllInfoTask
> task(new GetAllInfoTask(this));
1426 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1430 void AppCacheStorageImpl::LoadCache(int64 id
, Delegate
* delegate
) {
1433 delegate
->OnCacheLoaded(NULL
, id
);
1437 AppCache
* cache
= working_set_
.GetCache(id
);
1439 delegate
->OnCacheLoaded(cache
, id
);
1440 if (cache
->owning_group()) {
1441 scoped_refptr
<DatabaseTask
> update_task(
1442 new LazyUpdateLastAccessTimeTask(
1443 this, cache
->owning_group(), base::Time::Now()));
1444 update_task
->Schedule();
1448 scoped_refptr
<CacheLoadTask
> task(GetPendingCacheLoadTask(id
));
1450 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1453 task
= new CacheLoadTask(id
, this);
1454 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1456 pending_cache_loads_
[id
] = task
.get();
1459 void AppCacheStorageImpl::LoadOrCreateGroup(
1460 const GURL
& manifest_url
, Delegate
* delegate
) {
1463 delegate
->OnGroupLoaded(NULL
, manifest_url
);
1467 AppCacheGroup
* group
= working_set_
.GetGroup(manifest_url
);
1469 delegate
->OnGroupLoaded(group
, manifest_url
);
1470 scoped_refptr
<DatabaseTask
> update_task(
1471 new LazyUpdateLastAccessTimeTask(
1472 this, group
, base::Time::Now()));
1473 update_task
->Schedule();
1477 scoped_refptr
<GroupLoadTask
> task(GetPendingGroupLoadTask(manifest_url
));
1479 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1483 if (usage_map_
.find(manifest_url
.GetOrigin()) == usage_map_
.end()) {
1484 // No need to query the database, return a new group immediately.
1485 scoped_refptr
<AppCacheGroup
> group(new AppCacheGroup(
1486 this, manifest_url
, NewGroupId()));
1487 delegate
->OnGroupLoaded(group
.get(), manifest_url
);
1491 task
= new GroupLoadTask(manifest_url
, this);
1492 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1494 pending_group_loads_
[manifest_url
] = task
.get();
1497 void AppCacheStorageImpl::StoreGroupAndNewestCache(
1498 AppCacheGroup
* group
, AppCache
* newest_cache
, Delegate
* delegate
) {
1499 // TODO(michaeln): distinguish between a simple update of an existing
1500 // cache that just adds new master entry(s), and the insertion of a
1501 // whole new cache. The StoreGroupAndCacheTask as written will handle
1502 // the simple update case in a very heavy weight way (delete all and
1503 // the reinsert all over again).
1504 DCHECK(group
&& delegate
&& newest_cache
);
1505 scoped_refptr
<StoreGroupAndCacheTask
> task(
1506 new StoreGroupAndCacheTask(this, group
, newest_cache
));
1507 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1508 task
->GetQuotaThenSchedule();
1510 // TODO(michaeln): histogram is fishing for clues to crbug/95101
1511 if (!newest_cache
->GetEntry(group
->manifest_url())) {
1512 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
1513 AppCacheHistograms::CALLSITE_3
);
1517 void AppCacheStorageImpl::FindResponseForMainRequest(
1518 const GURL
& url
, const GURL
& preferred_manifest_url
,
1519 Delegate
* delegate
) {
1522 const GURL
* url_ptr
= &url
;
1524 if (url
.has_ref()) {
1525 GURL::Replacements replacements
;
1526 replacements
.ClearRef();
1527 url_no_ref
= url
.ReplaceComponents(replacements
);
1528 url_ptr
= &url_no_ref
;
1531 const GURL origin
= url
.GetOrigin();
1533 // First look in our working set for a direct hit without having to query
1535 const AppCacheWorkingSet::GroupMap
* groups_in_use
=
1536 working_set()->GetGroupsInOrigin(origin
);
1537 if (groups_in_use
) {
1538 if (!preferred_manifest_url
.is_empty()) {
1539 AppCacheWorkingSet::GroupMap::const_iterator found
=
1540 groups_in_use
->find(preferred_manifest_url
);
1541 if (found
!= groups_in_use
->end() &&
1542 FindResponseForMainRequestInGroup(
1543 found
->second
, *url_ptr
, delegate
)) {
1547 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
1548 groups_in_use
->begin();
1549 it
!= groups_in_use
->end(); ++it
) {
1550 if (FindResponseForMainRequestInGroup(
1551 it
->second
, *url_ptr
, delegate
)) {
1558 if (IsInitTaskComplete() && usage_map_
.find(origin
) == usage_map_
.end()) {
1559 // No need to query the database, return async'ly but without going thru
1561 scoped_refptr
<AppCacheGroup
> no_group
;
1562 scoped_refptr
<AppCache
> no_cache
;
1564 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1565 weak_factory_
.GetWeakPtr(), url
, AppCacheEntry(), no_group
,
1567 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1571 // We have to query the database, schedule a database task to do so.
1572 scoped_refptr
<FindMainResponseTask
> task(
1573 new FindMainResponseTask(this, *url_ptr
, preferred_manifest_url
,
1575 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1579 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
1580 AppCacheGroup
* group
, const GURL
& url
, Delegate
* delegate
) {
1581 AppCache
* cache
= group
->newest_complete_cache();
1582 if (group
->is_obsolete() || !cache
)
1585 AppCacheEntry
* entry
= cache
->GetEntry(url
);
1586 if (!entry
|| entry
->IsForeign())
1590 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1591 weak_factory_
.GetWeakPtr(), url
, *entry
,
1592 make_scoped_refptr(group
), make_scoped_refptr(cache
),
1593 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1597 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
1599 const AppCacheEntry
& found_entry
,
1600 scoped_refptr
<AppCacheGroup
> group
,
1601 scoped_refptr
<AppCache
> cache
,
1602 scoped_refptr
<DelegateReference
> delegate_ref
) {
1603 if (delegate_ref
->delegate
) {
1604 DelegateReferenceVector
delegates(1, delegate_ref
);
1605 CallOnMainResponseFound(
1606 &delegates
, url
, found_entry
,
1607 GURL(), AppCacheEntry(),
1608 cache
.get() ? cache
->cache_id() : kAppCacheNoCacheId
,
1609 group
.get() ? group
->group_id() : kAppCacheNoCacheId
,
1610 group
.get() ? group
->manifest_url() : GURL());
1614 void AppCacheStorageImpl::CallOnMainResponseFound(
1615 DelegateReferenceVector
* delegates
,
1616 const GURL
& url
, const AppCacheEntry
& entry
,
1617 const GURL
& namespace_entry_url
, const AppCacheEntry
& fallback_entry
,
1618 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
1621 OnMainResponseFound(url
, entry
,
1622 namespace_entry_url
, fallback_entry
,
1623 cache_id
, group_id
, manifest_url
));
1626 void AppCacheStorageImpl::FindResponseForSubRequest(
1627 AppCache
* cache
, const GURL
& url
,
1628 AppCacheEntry
* found_entry
, AppCacheEntry
* found_fallback_entry
,
1629 bool* found_network_namespace
) {
1630 DCHECK(cache
&& cache
->is_complete());
1632 // When a group is forcibly deleted, all subresource loads for pages
1633 // using caches in the group will result in a synthesized network errors.
1634 // Forcible deletion is not a function that is covered by the HTML5 spec.
1635 if (cache
->owning_group()->is_being_deleted()) {
1636 *found_entry
= AppCacheEntry();
1637 *found_fallback_entry
= AppCacheEntry();
1638 *found_network_namespace
= false;
1642 GURL fallback_namespace_not_used
;
1643 GURL intercept_namespace_not_used
;
1644 cache
->FindResponseForRequest(
1645 url
, found_entry
, &intercept_namespace_not_used
,
1646 found_fallback_entry
, &fallback_namespace_not_used
,
1647 found_network_namespace
);
1650 void AppCacheStorageImpl::MarkEntryAsForeign(
1651 const GURL
& entry_url
, int64 cache_id
) {
1652 AppCache
* cache
= working_set_
.GetCache(cache_id
);
1654 AppCacheEntry
* entry
= cache
->GetEntry(entry_url
);
1657 entry
->add_types(AppCacheEntry::FOREIGN
);
1659 scoped_refptr
<MarkEntryAsForeignTask
> task(
1660 new MarkEntryAsForeignTask(this, entry_url
, cache_id
));
1662 pending_foreign_markings_
.push_back(std::make_pair(entry_url
, cache_id
));
1665 void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup
* group
,
1667 int response_code
) {
1668 DCHECK(group
&& delegate
);
1669 scoped_refptr
<MakeGroupObsoleteTask
> task(
1670 new MakeGroupObsoleteTask(this, group
, response_code
));
1671 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1675 AppCacheResponseReader
* AppCacheStorageImpl::CreateResponseReader(
1676 const GURL
& manifest_url
, int64 group_id
, int64 response_id
) {
1677 return new AppCacheResponseReader(response_id
, group_id
, disk_cache());
1680 AppCacheResponseWriter
* AppCacheStorageImpl::CreateResponseWriter(
1681 const GURL
& manifest_url
, int64 group_id
) {
1682 return new AppCacheResponseWriter(NewResponseId(), group_id
, disk_cache());
1685 AppCacheResponseMetadataWriter
*
1686 AppCacheStorageImpl::CreateResponseMetadataWriter(int64 group_id
,
1687 int64 response_id
) {
1688 return new AppCacheResponseMetadataWriter(response_id
, group_id
,
1692 void AppCacheStorageImpl::DoomResponses(
1693 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1694 if (response_ids
.empty())
1697 // Start deleting them from the disk cache lazily.
1698 StartDeletingResponses(response_ids
);
1700 // Also schedule a database task to record these ids in the
1701 // deletable responses table.
1702 // TODO(michaeln): There is a race here. If the browser crashes
1703 // prior to committing these rows to the database and prior to us
1704 // having deleted them from the disk cache, we'll never delete them.
1705 scoped_refptr
<InsertDeletableResponseIdsTask
> task(
1706 new InsertDeletableResponseIdsTask(this));
1707 task
->response_ids_
= response_ids
;
1711 void AppCacheStorageImpl::DeleteResponses(
1712 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1713 if (response_ids
.empty())
1715 StartDeletingResponses(response_ids
);
1718 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
1719 // Only if we haven't already begun.
1720 if (!did_start_deleting_responses_
) {
1721 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1722 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1727 void AppCacheStorageImpl::StartDeletingResponses(
1728 const std::vector
<int64
>& response_ids
) {
1729 DCHECK(!response_ids
.empty());
1730 did_start_deleting_responses_
= true;
1731 deletable_response_ids_
.insert(
1732 deletable_response_ids_
.end(),
1733 response_ids
.begin(), response_ids
.end());
1734 if (!is_response_deletion_scheduled_
)
1735 ScheduleDeleteOneResponse();
1738 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
1739 DCHECK(!is_response_deletion_scheduled_
);
1740 const base::TimeDelta kBriefDelay
= base::TimeDelta::FromMilliseconds(10);
1741 base::MessageLoop::current()->PostDelayedTask(
1743 base::Bind(&AppCacheStorageImpl::DeleteOneResponse
,
1744 weak_factory_
.GetWeakPtr()),
1746 is_response_deletion_scheduled_
= true;
1749 void AppCacheStorageImpl::DeleteOneResponse() {
1750 DCHECK(is_response_deletion_scheduled_
);
1751 DCHECK(!deletable_response_ids_
.empty());
1753 if (!disk_cache()) {
1754 DCHECK(is_disabled_
);
1755 deletable_response_ids_
.clear();
1756 deleted_response_ids_
.clear();
1757 is_response_deletion_scheduled_
= false;
1761 // TODO(michaeln): add group_id to DoomEntry args
1762 int64 id
= deletable_response_ids_
.front();
1763 int rv
= disk_cache_
->DoomEntry(
1764 id
, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse
,
1765 base::Unretained(this)));
1766 if (rv
!= net::ERR_IO_PENDING
)
1767 OnDeletedOneResponse(rv
);
1770 void AppCacheStorageImpl::OnDeletedOneResponse(int rv
) {
1771 is_response_deletion_scheduled_
= false;
1775 int64 id
= deletable_response_ids_
.front();
1776 deletable_response_ids_
.pop_front();
1777 if (rv
!= net::ERR_ABORTED
)
1778 deleted_response_ids_
.push_back(id
);
1780 const size_t kBatchSize
= 50U;
1781 if (deleted_response_ids_
.size() >= kBatchSize
||
1782 deletable_response_ids_
.empty()) {
1783 scoped_refptr
<DeleteDeletableResponseIdsTask
> task(
1784 new DeleteDeletableResponseIdsTask(this));
1785 task
->response_ids_
.swap(deleted_response_ids_
);
1789 if (deletable_response_ids_
.empty()) {
1790 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1791 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1796 ScheduleDeleteOneResponse();
1799 AppCacheStorageImpl::CacheLoadTask
*
1800 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id
) {
1801 PendingCacheLoads::iterator found
= pending_cache_loads_
.find(cache_id
);
1802 if (found
!= pending_cache_loads_
.end())
1803 return found
->second
;
1807 AppCacheStorageImpl::GroupLoadTask
*
1808 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL
& manifest_url
) {
1809 PendingGroupLoads::iterator found
= pending_group_loads_
.find(manifest_url
);
1810 if (found
!= pending_group_loads_
.end())
1811 return found
->second
;
1815 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
1816 int64 cache_id
, std::vector
<GURL
>* urls
) {
1817 PendingForeignMarkings::iterator iter
= pending_foreign_markings_
.begin();
1818 while (iter
!= pending_foreign_markings_
.end()) {
1819 if (iter
->second
== cache_id
)
1820 urls
->push_back(iter
->first
);
1825 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure
& task
) {
1826 pending_simple_tasks_
.push_back(task
);
1827 base::MessageLoop::current()->PostTask(
1829 base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask
,
1830 weak_factory_
.GetWeakPtr()));
1833 void AppCacheStorageImpl::RunOnePendingSimpleTask() {
1834 DCHECK(!pending_simple_tasks_
.empty());
1835 base::Closure task
= pending_simple_tasks_
.front();
1836 pending_simple_tasks_
.pop_front();
1840 AppCacheDiskCache
* AppCacheStorageImpl::disk_cache() {
1841 DCHECK(IsInitTaskComplete());
1848 disk_cache_
.reset(new AppCacheDiskCache
);
1849 if (is_incognito_
) {
1850 rv
= disk_cache_
->InitWithMemBackend(
1851 kMaxMemDiskCacheSize
,
1852 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1853 base::Unretained(this)));
1855 rv
= disk_cache_
->InitWithDiskBackend(
1856 cache_directory_
.Append(kDiskCacheDirectoryName
),
1859 cache_thread_
.get(),
1860 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1861 base::Unretained(this)));
1864 if (rv
!= net::ERR_IO_PENDING
)
1865 OnDiskCacheInitialized(rv
);
1867 return disk_cache_
.get();
1870 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv
) {
1871 if (rv
!= net::OK
) {
1872 LOG(ERROR
) << "Failed to open the appcache diskcache.";
1873 AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR
);
1875 // We're unable to open the disk cache, this is a fatal error that we can't
1876 // really recover from. We handle it by temporarily disabling the appcache
1877 // deleting the directory on disk and reinitializing the appcache system.
1879 if (rv
!= net::ERR_ABORTED
)
1880 DeleteAndStartOver();
1884 void AppCacheStorageImpl::DeleteAndStartOver() {
1885 DCHECK(is_disabled_
);
1886 if (!is_incognito_
) {
1887 VLOG(1) << "Deleting existing appcache data and starting over.";
1888 // We can have tasks in flight to close file handles on both the db
1889 // and cache threads, we need to allow those tasks to cycle thru
1890 // prior to deleting the files and calling reinit.
1891 cache_thread_
->PostTaskAndReply(
1893 base::Bind(&base::DoNothing
),
1894 base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2
,
1895 weak_factory_
.GetWeakPtr()));
1899 void AppCacheStorageImpl::DeleteAndStartOverPart2() {
1900 db_thread_
->PostTaskAndReply(
1902 base::Bind(base::IgnoreResult(&base::DeleteFile
), cache_directory_
, true),
1903 base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize
,
1904 weak_factory_
.GetWeakPtr()));
1907 void AppCacheStorageImpl::CallScheduleReinitialize() {
1908 service_
->ScheduleReinitialize();
1909 // note: 'this' may be deleted at this point.
1912 void AppCacheStorageImpl::LazilyCommitLastAccessTimes() {
1913 if (lazy_commit_timer_
.IsRunning())
1915 const base::TimeDelta kDelay
= base::TimeDelta::FromMinutes(5);
1916 lazy_commit_timer_
.Start(
1918 base::Bind(&AppCacheStorageImpl::OnLazyCommitTimer
,
1919 weak_factory_
.GetWeakPtr()));
1922 void AppCacheStorageImpl::OnLazyCommitTimer() {
1923 lazy_commit_timer_
.Stop();
1926 scoped_refptr
<DatabaseTask
> task(new CommitLastAccessTimesTask(this));
1930 } // namespace content