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_impl.h"
31 #include "webkit/browser/quota/quota_client.h"
32 #include "webkit/browser/quota/quota_manager.h"
33 #include "webkit/browser/quota/quota_manager_proxy.h"
34 #include "webkit/browser/quota/special_storage_policy.h"
38 // Hard coded default when not using quota management.
39 static const int kDefaultQuota
= 5 * 1024 * 1024;
41 static const int kMaxDiskCacheSize
= 250 * 1024 * 1024;
42 static const int kMaxMemDiskCacheSize
= 10 * 1024 * 1024;
43 static const base::FilePath::CharType kDiskCacheDirectoryName
[] =
44 FILE_PATH_LITERAL("Cache");
48 // Helpers for clearing data from the AppCacheDatabase.
49 bool DeleteGroupAndRelatedRecords(AppCacheDatabase
* database
,
51 std::vector
<int64
>* deletable_response_ids
) {
52 AppCacheDatabase::CacheRecord cache_record
;
54 if (database
->FindCacheForGroup(group_id
, &cache_record
)) {
55 database
->FindResponseIdsForCacheAsVector(cache_record
.cache_id
,
56 deletable_response_ids
);
58 database
->DeleteGroup(group_id
) &&
59 database
->DeleteCache(cache_record
.cache_id
) &&
60 database
->DeleteEntriesForCache(cache_record
.cache_id
) &&
61 database
->DeleteNamespacesForCache(cache_record
.cache_id
) &&
62 database
->DeleteOnlineWhiteListForCache(cache_record
.cache_id
) &&
63 database
->InsertDeletableResponseIds(*deletable_response_ids
);
65 NOTREACHED() << "A existing group without a cache is unexpected";
66 success
= database
->DeleteGroup(group_id
);
71 // Destroys |database|. If there is appcache data to be deleted
72 // (|force_keep_session_state| is false), deletes session-only appcache data.
73 void ClearSessionOnlyOrigins(
74 AppCacheDatabase
* database
,
75 scoped_refptr
<quota::SpecialStoragePolicy
> special_storage_policy
,
76 bool force_keep_session_state
) {
77 scoped_ptr
<AppCacheDatabase
> database_to_delete(database
);
79 // If saving session state, only delete the database.
80 if (force_keep_session_state
)
83 bool has_session_only_appcaches
=
84 special_storage_policy
.get() &&
85 special_storage_policy
->HasSessionOnlyOrigins();
87 // Clearning only session-only databases, and there are none.
88 if (!has_session_only_appcaches
)
91 std::set
<GURL
> origins
;
92 database
->FindOriginsWithGroups(&origins
);
94 return; // nothing to delete
96 sql::Connection
* connection
= database
->db_connection();
98 NOTREACHED() << "Missing database connection.";
102 std::set
<GURL
>::const_iterator origin
;
103 for (origin
= origins
.begin(); origin
!= origins
.end(); ++origin
) {
104 if (!special_storage_policy
->IsStorageSessionOnly(*origin
))
106 if (special_storage_policy
.get() &&
107 special_storage_policy
->IsStorageProtected(*origin
))
110 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
111 database
->FindGroupsForOrigin(*origin
, &groups
);
112 std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator group
;
113 for (group
= groups
.begin(); group
!= groups
.end(); ++group
) {
114 sql::Transaction
transaction(connection
);
115 if (!transaction
.Begin()) {
116 NOTREACHED() << "Failed to start transaction";
119 std::vector
<int64
> deletable_response_ids
;
120 bool success
= DeleteGroupAndRelatedRecords(database
,
122 &deletable_response_ids
);
123 success
= success
&& transaction
.Commit();
131 // DatabaseTask -----------------------------------------
133 class AppCacheStorageImpl::DatabaseTask
134 : public base::RefCountedThreadSafe
<DatabaseTask
> {
136 explicit DatabaseTask(AppCacheStorageImpl
* storage
)
137 : storage_(storage
), database_(storage
->database_
),
138 io_thread_(base::MessageLoopProxy::current()) {
139 DCHECK(io_thread_
.get());
142 void AddDelegate(DelegateReference
* delegate_reference
) {
143 delegates_
.push_back(make_scoped_refptr(delegate_reference
));
146 // Schedules a task to be Run() on the DB thread. Tasks
147 // are run in the order in which they are scheduled.
150 // Called on the DB thread.
151 virtual void Run() = 0;
153 // Called on the IO thread after Run() has completed.
154 virtual void RunCompleted() {}
156 // Once scheduled a task cannot be cancelled, but the
157 // call to RunCompleted may be. This method should only be
158 // called on the IO thread. This is used by AppCacheStorageImpl
159 // to cancel the completion calls when AppCacheStorageImpl is
160 // destructed. This method may be overriden to release or delete
161 // additional data associated with the task that is not DB thread
162 // safe. If overriden, this base class method must be called from
163 // within the override.
164 virtual void CancelCompletion();
167 friend class base::RefCountedThreadSafe
<DatabaseTask
>;
168 virtual ~DatabaseTask() {}
170 AppCacheStorageImpl
* storage_
;
171 AppCacheDatabase
* database_
;
172 DelegateReferenceVector delegates_
;
175 void CallRun(base::TimeTicks schedule_time
);
176 void CallRunCompleted(base::TimeTicks schedule_time
);
179 scoped_refptr
<base::MessageLoopProxy
> io_thread_
;
182 void AppCacheStorageImpl::DatabaseTask::Schedule() {
184 DCHECK(io_thread_
->BelongsToCurrentThread());
185 if (!storage_
->database_
)
188 if (storage_
->db_thread_
->PostTask(
190 base::Bind(&DatabaseTask::CallRun
, this, base::TimeTicks::Now()))) {
191 storage_
->scheduled_database_tasks_
.push_back(this);
193 NOTREACHED() << "Thread for database tasks is not running.";
197 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
198 DCHECK(io_thread_
->BelongsToCurrentThread());
203 void AppCacheStorageImpl::DatabaseTask::CallRun(
204 base::TimeTicks schedule_time
) {
205 AppCacheHistograms::AddTaskQueueTimeSample(
206 base::TimeTicks::Now() - schedule_time
);
207 if (!database_
->is_disabled()) {
208 base::TimeTicks run_time
= base::TimeTicks::Now();
210 AppCacheHistograms::AddTaskRunTimeSample(
211 base::TimeTicks::Now() - run_time
);
213 if (database_
->was_corruption_detected()) {
214 AppCacheHistograms::CountCorruptionDetected();
215 database_
->Disable();
217 if (database_
->is_disabled()) {
218 io_thread_
->PostTask(
220 base::Bind(&DatabaseTask::OnFatalError
, this));
223 io_thread_
->PostTask(
225 base::Bind(&DatabaseTask::CallRunCompleted
, this,
226 base::TimeTicks::Now()));
229 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
230 base::TimeTicks schedule_time
) {
231 AppCacheHistograms::AddCompletionQueueTimeSample(
232 base::TimeTicks::Now() - schedule_time
);
234 DCHECK(io_thread_
->BelongsToCurrentThread());
235 DCHECK(storage_
->scheduled_database_tasks_
.front() == this);
236 storage_
->scheduled_database_tasks_
.pop_front();
237 base::TimeTicks run_time
= base::TimeTicks::Now();
239 AppCacheHistograms::AddCompletionRunTimeSample(
240 base::TimeTicks::Now() - run_time
);
245 void AppCacheStorageImpl::DatabaseTask::OnFatalError() {
247 DCHECK(io_thread_
->BelongsToCurrentThread());
249 storage_
->DeleteAndStartOver();
255 class AppCacheStorageImpl::InitTask
: public DatabaseTask
{
257 explicit InitTask(AppCacheStorageImpl
* storage
)
258 : DatabaseTask(storage
), last_group_id_(0),
259 last_cache_id_(0), last_response_id_(0),
260 last_deletable_response_rowid_(0) {
261 if (!storage
->is_incognito_
) {
263 storage
->cache_directory_
.Append(kAppCacheDatabaseName
);
264 disk_cache_directory_
=
265 storage
->cache_directory_
.Append(kDiskCacheDirectoryName
);
270 virtual void Run() OVERRIDE
;
271 virtual void RunCompleted() OVERRIDE
;
274 virtual ~InitTask() {}
277 base::FilePath db_file_path_
;
278 base::FilePath disk_cache_directory_
;
279 int64 last_group_id_
;
280 int64 last_cache_id_
;
281 int64 last_response_id_
;
282 int64 last_deletable_response_rowid_
;
283 std::map
<GURL
, int64
> usage_map_
;
286 void AppCacheStorageImpl::InitTask::Run() {
287 // If there is no sql database, ensure there is no disk cache either.
288 if (!db_file_path_
.empty() &&
289 !base::PathExists(db_file_path_
) &&
290 base::DirectoryExists(disk_cache_directory_
)) {
291 base::DeleteFile(disk_cache_directory_
, true);
292 if (base::DirectoryExists(disk_cache_directory_
)) {
293 database_
->Disable(); // This triggers OnFatalError handling.
298 database_
->FindLastStorageIds(
299 &last_group_id_
, &last_cache_id_
, &last_response_id_
,
300 &last_deletable_response_rowid_
);
301 database_
->GetAllOriginUsage(&usage_map_
);
304 void AppCacheStorageImpl::InitTask::RunCompleted() {
305 storage_
->last_group_id_
= last_group_id_
;
306 storage_
->last_cache_id_
= last_cache_id_
;
307 storage_
->last_response_id_
= last_response_id_
;
308 storage_
->last_deletable_response_rowid_
= last_deletable_response_rowid_
;
310 if (!storage_
->is_disabled()) {
311 storage_
->usage_map_
.swap(usage_map_
);
312 const base::TimeDelta kDelay
= base::TimeDelta::FromMinutes(5);
313 base::MessageLoop::current()->PostDelayedTask(
315 base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses
,
316 storage_
->weak_factory_
.GetWeakPtr()),
320 if (storage_
->service()->quota_client())
321 storage_
->service()->quota_client()->NotifyAppCacheReady();
324 // DisableDatabaseTask -------
326 class AppCacheStorageImpl::DisableDatabaseTask
: public DatabaseTask
{
328 explicit DisableDatabaseTask(AppCacheStorageImpl
* storage
)
329 : DatabaseTask(storage
) {}
332 virtual void Run() OVERRIDE
{ database_
->Disable(); }
335 virtual ~DisableDatabaseTask() {}
338 // GetAllInfoTask -------
340 class AppCacheStorageImpl::GetAllInfoTask
: public DatabaseTask
{
342 explicit GetAllInfoTask(AppCacheStorageImpl
* storage
)
343 : DatabaseTask(storage
),
344 info_collection_(new AppCacheInfoCollection()) {
348 virtual void Run() OVERRIDE
;
349 virtual void RunCompleted() OVERRIDE
;
352 virtual ~GetAllInfoTask() {}
355 scoped_refptr
<AppCacheInfoCollection
> info_collection_
;
358 void AppCacheStorageImpl::GetAllInfoTask::Run() {
359 std::set
<GURL
> origins
;
360 database_
->FindOriginsWithGroups(&origins
);
361 for (std::set
<GURL
>::const_iterator origin
= origins
.begin();
362 origin
!= origins
.end(); ++origin
) {
363 AppCacheInfoVector
& infos
=
364 info_collection_
->infos_by_origin
[*origin
];
365 std::vector
<AppCacheDatabase::GroupRecord
> groups
;
366 database_
->FindGroupsForOrigin(*origin
, &groups
);
367 for (std::vector
<AppCacheDatabase::GroupRecord
>::const_iterator
368 group
= groups
.begin();
369 group
!= groups
.end(); ++group
) {
370 AppCacheDatabase::CacheRecord cache_record
;
371 database_
->FindCacheForGroup(group
->group_id
, &cache_record
);
373 info
.manifest_url
= group
->manifest_url
;
374 info
.creation_time
= group
->creation_time
;
375 info
.size
= cache_record
.cache_size
;
376 info
.last_access_time
= group
->last_access_time
;
377 info
.last_update_time
= cache_record
.update_time
;
378 info
.cache_id
= cache_record
.cache_id
;
379 info
.group_id
= group
->group_id
;
380 info
.is_complete
= true;
381 infos
.push_back(info
);
386 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
387 DCHECK(delegates_
.size() == 1);
388 FOR_EACH_DELEGATE(delegates_
, OnAllInfo(info_collection_
.get()));
391 // StoreOrLoadTask -------
393 class AppCacheStorageImpl::StoreOrLoadTask
: public DatabaseTask
{
395 explicit StoreOrLoadTask(AppCacheStorageImpl
* storage
)
396 : DatabaseTask(storage
) {}
397 virtual ~StoreOrLoadTask() {}
399 bool FindRelatedCacheRecords(int64 cache_id
);
400 void CreateCacheAndGroupFromRecords(
401 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
);
403 AppCacheDatabase::GroupRecord group_record_
;
404 AppCacheDatabase::CacheRecord cache_record_
;
405 std::vector
<AppCacheDatabase::EntryRecord
> entry_records_
;
406 std::vector
<AppCacheDatabase::NamespaceRecord
>
407 intercept_namespace_records_
;
408 std::vector
<AppCacheDatabase::NamespaceRecord
>
409 fallback_namespace_records_
;
410 std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
411 online_whitelist_records_
;
414 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
416 return database_
->FindEntriesForCache(cache_id
, &entry_records_
) &&
417 database_
->FindNamespacesForCache(
418 cache_id
, &intercept_namespace_records_
,
419 &fallback_namespace_records_
) &&
420 database_
->FindOnlineWhiteListForCache(
421 cache_id
, &online_whitelist_records_
);
424 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
425 scoped_refptr
<AppCache
>* cache
, scoped_refptr
<AppCacheGroup
>* group
) {
426 DCHECK(storage_
&& cache
&& group
);
428 (*cache
) = storage_
->working_set_
.GetCache(cache_record_
.cache_id
);
430 (*group
) = cache
->get()->owning_group();
431 DCHECK(group
->get());
432 DCHECK_EQ(group_record_
.group_id
, group
->get()->group_id());
434 // TODO(michaeln): histogram is fishing for clues to crbug/95101
435 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
436 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
437 AppCacheHistograms::CALLSITE_0
);
440 storage_
->NotifyStorageAccessed(group_record_
.origin
);
444 (*cache
) = new AppCache(storage_
, cache_record_
.cache_id
);
445 cache
->get()->InitializeWithDatabaseRecords(
446 cache_record_
, entry_records_
,
447 intercept_namespace_records_
,
448 fallback_namespace_records_
,
449 online_whitelist_records_
);
450 cache
->get()->set_complete(true);
452 (*group
) = storage_
->working_set_
.GetGroup(group_record_
.manifest_url
);
454 DCHECK(group_record_
.group_id
== group
->get()->group_id());
455 group
->get()->AddCache(cache
->get());
457 // TODO(michaeln): histogram is fishing for clues to crbug/95101
458 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
459 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
460 AppCacheHistograms::CALLSITE_1
);
463 (*group
) = new AppCacheGroup(
464 storage_
, group_record_
.manifest_url
,
465 group_record_
.group_id
);
466 group
->get()->set_creation_time(group_record_
.creation_time
);
467 group
->get()->AddCache(cache
->get());
469 // TODO(michaeln): histogram is fishing for clues to crbug/95101
470 if (!cache
->get()->GetEntry(group_record_
.manifest_url
)) {
471 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
472 AppCacheHistograms::CALLSITE_2
);
475 DCHECK(group
->get()->newest_complete_cache() == cache
->get());
477 // We have to update foriegn entries if MarkEntryAsForeignTasks
479 std::vector
<GURL
> urls
;
480 storage_
->GetPendingForeignMarkingsForCache(cache
->get()->cache_id(), &urls
);
481 for (std::vector
<GURL
>::iterator iter
= urls
.begin();
482 iter
!= urls
.end(); ++iter
) {
483 DCHECK(cache
->get()->GetEntry(*iter
));
484 cache
->get()->GetEntry(*iter
)->add_types(AppCacheEntry::FOREIGN
);
487 storage_
->NotifyStorageAccessed(group_record_
.origin
);
489 // TODO(michaeln): Maybe verify that the responses we expect to exist
490 // do actually exist in the disk_cache (and if not then what?)
493 // CacheLoadTask -------
495 class AppCacheStorageImpl::CacheLoadTask
: public StoreOrLoadTask
{
497 CacheLoadTask(int64 cache_id
, AppCacheStorageImpl
* storage
)
498 : StoreOrLoadTask(storage
), cache_id_(cache_id
),
502 virtual void Run() OVERRIDE
;
503 virtual void RunCompleted() OVERRIDE
;
506 virtual ~CacheLoadTask() {}
513 void AppCacheStorageImpl::CacheLoadTask::Run() {
515 database_
->FindCache(cache_id_
, &cache_record_
) &&
516 database_
->FindGroup(cache_record_
.group_id
, &group_record_
) &&
517 FindRelatedCacheRecords(cache_id_
);
520 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
524 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
525 storage_
->pending_cache_loads_
.erase(cache_id_
);
526 scoped_refptr
<AppCache
> cache
;
527 scoped_refptr
<AppCacheGroup
> group
;
528 if (success_
&& !storage_
->is_disabled()) {
529 DCHECK(cache_record_
.cache_id
== cache_id_
);
530 CreateCacheAndGroupFromRecords(&cache
, &group
);
532 FOR_EACH_DELEGATE(delegates_
, OnCacheLoaded(cache
.get(), cache_id_
));
535 // GroupLoadTask -------
537 class AppCacheStorageImpl::GroupLoadTask
: public StoreOrLoadTask
{
539 GroupLoadTask(GURL manifest_url
, AppCacheStorageImpl
* storage
)
540 : StoreOrLoadTask(storage
), manifest_url_(manifest_url
),
544 virtual void Run() OVERRIDE
;
545 virtual void RunCompleted() OVERRIDE
;
548 virtual ~GroupLoadTask() {}
555 void AppCacheStorageImpl::GroupLoadTask::Run() {
557 database_
->FindGroupForManifestUrl(manifest_url_
, &group_record_
) &&
558 database_
->FindCacheForGroup(group_record_
.group_id
, &cache_record_
) &&
559 FindRelatedCacheRecords(cache_record_
.cache_id
);
562 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
566 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
567 storage_
->pending_group_loads_
.erase(manifest_url_
);
568 scoped_refptr
<AppCacheGroup
> group
;
569 scoped_refptr
<AppCache
> cache
;
570 if (!storage_
->is_disabled()) {
572 DCHECK(group_record_
.manifest_url
== manifest_url_
);
573 CreateCacheAndGroupFromRecords(&cache
, &group
);
575 group
= storage_
->working_set_
.GetGroup(manifest_url_
);
578 new AppCacheGroup(storage_
, manifest_url_
, storage_
->NewGroupId());
582 FOR_EACH_DELEGATE(delegates_
, OnGroupLoaded(group
.get(), manifest_url_
));
585 // StoreGroupAndCacheTask -------
587 class AppCacheStorageImpl::StoreGroupAndCacheTask
: public StoreOrLoadTask
{
589 StoreGroupAndCacheTask(AppCacheStorageImpl
* storage
, AppCacheGroup
* group
,
590 AppCache
* newest_cache
);
592 void GetQuotaThenSchedule();
593 void OnQuotaCallback(
594 quota::QuotaStatusCode status
, int64 usage
, int64 quota
);
597 virtual void Run() OVERRIDE
;
598 virtual void RunCompleted() OVERRIDE
;
599 virtual void CancelCompletion() OVERRIDE
;
602 virtual ~StoreGroupAndCacheTask() {}
605 scoped_refptr
<AppCacheGroup
> group_
;
606 scoped_refptr
<AppCache
> cache_
;
608 bool would_exceed_quota_
;
609 int64 space_available_
;
610 int64 new_origin_usage_
;
611 std::vector
<int64
> newly_deletable_response_ids_
;
614 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
615 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, AppCache
* newest_cache
)
616 : StoreOrLoadTask(storage
), group_(group
), cache_(newest_cache
),
617 success_(false), would_exceed_quota_(false),
618 space_available_(-1), new_origin_usage_(-1) {
619 group_record_
.group_id
= group
->group_id();
620 group_record_
.manifest_url
= group
->manifest_url();
621 group_record_
.origin
= group_record_
.manifest_url
.GetOrigin();
622 newest_cache
->ToDatabaseRecords(
624 &cache_record_
, &entry_records_
,
625 &intercept_namespace_records_
,
626 &fallback_namespace_records_
,
627 &online_whitelist_records_
);
630 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
631 quota::QuotaManager
* quota_manager
= NULL
;
632 if (storage_
->service()->quota_manager_proxy()) {
634 storage_
->service()->quota_manager_proxy()->quota_manager();
637 if (!quota_manager
) {
638 if (storage_
->service()->special_storage_policy() &&
639 storage_
->service()->special_storage_policy()->IsStorageUnlimited(
640 group_record_
.origin
))
641 space_available_
= kint64max
;
646 // We have to ask the quota manager for the value.
647 storage_
->pending_quota_queries_
.insert(this);
648 quota_manager
->GetUsageAndQuota(
649 group_record_
.origin
, quota::kStorageTypeTemporary
,
650 base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback
, this));
653 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
654 quota::QuotaStatusCode status
, int64 usage
, int64 quota
) {
656 if (status
== quota::kQuotaStatusOk
)
657 space_available_
= std::max(static_cast<int64
>(0), quota
- usage
);
659 space_available_
= 0;
660 storage_
->pending_quota_queries_
.erase(this);
665 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
667 sql::Connection
* connection
= database_
->db_connection();
671 sql::Transaction
transaction(connection
);
672 if (!transaction
.Begin())
675 int64 old_origin_usage
= database_
->GetOriginUsage(group_record_
.origin
);
677 AppCacheDatabase::GroupRecord existing_group
;
678 success_
= database_
->FindGroup(group_record_
.group_id
, &existing_group
);
680 group_record_
.creation_time
= base::Time::Now();
681 group_record_
.last_access_time
= base::Time::Now();
682 success_
= database_
->InsertGroup(&group_record_
);
684 DCHECK(group_record_
.group_id
== existing_group
.group_id
);
685 DCHECK(group_record_
.manifest_url
== existing_group
.manifest_url
);
686 DCHECK(group_record_
.origin
== existing_group
.origin
);
688 database_
->UpdateGroupLastAccessTime(group_record_
.group_id
,
691 AppCacheDatabase::CacheRecord cache
;
692 if (database_
->FindCacheForGroup(group_record_
.group_id
, &cache
)) {
693 // Get the set of response ids in the old cache.
694 std::set
<int64
> existing_response_ids
;
695 database_
->FindResponseIdsForCacheAsSet(cache
.cache_id
,
696 &existing_response_ids
);
698 // Remove those that remain in the new cache.
699 std::vector
<AppCacheDatabase::EntryRecord
>::const_iterator entry_iter
=
700 entry_records_
.begin();
701 while (entry_iter
!= entry_records_
.end()) {
702 existing_response_ids
.erase(entry_iter
->response_id
);
706 // The rest are deletable.
707 std::set
<int64
>::const_iterator id_iter
= existing_response_ids
.begin();
708 while (id_iter
!= existing_response_ids
.end()) {
709 newly_deletable_response_ids_
.push_back(*id_iter
);
714 database_
->DeleteCache(cache
.cache_id
) &&
715 database_
->DeleteEntriesForCache(cache
.cache_id
) &&
716 database_
->DeleteNamespacesForCache(cache
.cache_id
) &&
717 database_
->DeleteOnlineWhiteListForCache(cache
.cache_id
) &&
718 database_
->InsertDeletableResponseIds(newly_deletable_response_ids_
);
719 // TODO(michaeln): store group_id too with deletable ids
721 NOTREACHED() << "A existing group without a cache is unexpected";
727 database_
->InsertCache(&cache_record_
) &&
728 database_
->InsertEntryRecords(entry_records_
) &&
729 database_
->InsertNamespaceRecords(intercept_namespace_records_
) &&
730 database_
->InsertNamespaceRecords(fallback_namespace_records_
) &&
731 database_
->InsertOnlineWhiteListRecords(online_whitelist_records_
);
736 new_origin_usage_
= database_
->GetOriginUsage(group_record_
.origin
);
738 // Only check quota when the new usage exceeds the old usage.
739 if (new_origin_usage_
<= old_origin_usage
) {
740 success_
= transaction
.Commit();
744 // Use a simple hard-coded value when not using quota management.
745 if (space_available_
== -1) {
746 if (new_origin_usage_
> kDefaultQuota
) {
747 would_exceed_quota_
= true;
751 success_
= transaction
.Commit();
755 // Check limits based on the space availbable given to us via the
757 int64 delta
= new_origin_usage_
- old_origin_usage
;
758 if (delta
> space_available_
) {
759 would_exceed_quota_
= true;
764 success_
= transaction
.Commit();
767 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
769 storage_
->UpdateUsageMapAndNotify(
770 group_
->manifest_url().GetOrigin(), new_origin_usage_
);
771 if (cache_
.get() != group_
->newest_complete_cache()) {
772 cache_
->set_complete(true);
773 group_
->AddCache(cache_
.get());
775 if (group_
->creation_time().is_null())
776 group_
->set_creation_time(group_record_
.creation_time
);
777 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
781 OnGroupAndNewestCacheStored(
782 group_
.get(), cache_
.get(), success_
, would_exceed_quota_
));
786 // TODO(michaeln): if (would_exceed_quota_) what if the current usage
787 // also exceeds the quota? http://crbug.com/83968
790 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
791 // Overriden to safely drop our reference to the group and cache
792 // which are not thread safe refcounted.
793 DatabaseTask::CancelCompletion();
798 // FindMainResponseTask -------
800 // Helpers for FindMainResponseTask::Run()
802 class SortByCachePreference
803 : public std::binary_function
<
804 AppCacheDatabase::EntryRecord
,
805 AppCacheDatabase::EntryRecord
,
808 SortByCachePreference(int64 preferred_id
, const std::set
<int64
>& in_use_ids
)
809 : preferred_id_(preferred_id
), in_use_ids_(in_use_ids
) {
812 const AppCacheDatabase::EntryRecord
& lhs
,
813 const AppCacheDatabase::EntryRecord
& rhs
) {
814 return compute_value(lhs
) > compute_value(rhs
);
817 int compute_value(const AppCacheDatabase::EntryRecord
& entry
) {
818 if (entry
.cache_id
== preferred_id_
)
820 else if (in_use_ids_
.find(entry
.cache_id
) != in_use_ids_
.end())
825 const std::set
<int64
>& in_use_ids_
;
829 const AppCacheDatabase::NamespaceRecord
& lhs
,
830 const AppCacheDatabase::NamespaceRecord
& rhs
) {
831 return lhs
.namespace_
.namespace_url
.spec().length() >
832 rhs
.namespace_
.namespace_url
.spec().length();
835 class NetworkNamespaceHelper
{
837 explicit NetworkNamespaceHelper(AppCacheDatabase
* database
)
838 : database_(database
) {
841 bool IsInNetworkNamespace(const GURL
& url
, int64 cache_id
) {
842 typedef std::pair
<WhiteListMap::iterator
, bool> InsertResult
;
843 InsertResult result
= namespaces_map_
.insert(
844 WhiteListMap::value_type(cache_id
, NamespaceVector()));
846 GetOnlineWhiteListForCache(cache_id
, &result
.first
->second
);
847 return AppCache::FindNamespace(result
.first
->second
, url
) != NULL
;
851 void GetOnlineWhiteListForCache(
852 int64 cache_id
, NamespaceVector
* namespaces
) {
853 DCHECK(namespaces
&& namespaces
->empty());
854 typedef std::vector
<AppCacheDatabase::OnlineWhiteListRecord
>
856 WhiteListVector records
;
857 if (!database_
->FindOnlineWhiteListForCache(cache_id
, &records
))
859 WhiteListVector::const_iterator iter
= records
.begin();
860 while (iter
!= records
.end()) {
861 namespaces
->push_back(
862 Namespace(APPCACHE_NETWORK_NAMESPACE
, iter
->namespace_url
, GURL(),
869 typedef std::map
<int64
, NamespaceVector
> WhiteListMap
;
870 WhiteListMap namespaces_map_
;
871 AppCacheDatabase
* database_
;
876 class AppCacheStorageImpl::FindMainResponseTask
: public DatabaseTask
{
878 FindMainResponseTask(AppCacheStorageImpl
* storage
,
880 const GURL
& preferred_manifest_url
,
881 const AppCacheWorkingSet::GroupMap
* groups_in_use
)
882 : DatabaseTask(storage
), url_(url
),
883 preferred_manifest_url_(preferred_manifest_url
),
884 cache_id_(kAppCacheNoCacheId
), group_id_(0) {
886 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
887 groups_in_use
->begin();
888 it
!= groups_in_use
->end(); ++it
) {
889 AppCacheGroup
* group
= it
->second
;
890 AppCache
* cache
= group
->newest_complete_cache();
891 if (group
->is_obsolete() || !cache
)
893 cache_ids_in_use_
.insert(cache
->cache_id());
899 virtual void Run() OVERRIDE
;
900 virtual void RunCompleted() OVERRIDE
;
903 virtual ~FindMainResponseTask() {}
906 typedef std::vector
<AppCacheDatabase::NamespaceRecord
*>
907 NamespaceRecordPtrVector
;
909 bool FindExactMatch(int64 preferred_id
);
910 bool FindNamespaceMatch(int64 preferred_id
);
911 bool FindNamespaceHelper(
912 int64 preferred_cache_id
,
913 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
914 NetworkNamespaceHelper
* network_namespace_helper
);
915 bool FindFirstValidNamespace(const NamespaceRecordPtrVector
& namespaces
);
918 GURL preferred_manifest_url_
;
919 std::set
<int64
> cache_ids_in_use_
;
920 AppCacheEntry entry_
;
921 AppCacheEntry fallback_entry_
;
922 GURL namespace_entry_url_
;
928 void AppCacheStorageImpl::FindMainResponseTask::Run() {
929 // NOTE: The heuristics around choosing amoungst multiple candidates
930 // is underspecified, and just plain not fully understood. This needs
933 // The 'preferred_manifest_url' is the url of the manifest associated
934 // with the page that opened or embedded the page being loaded now.
935 // We have a strong preference to use resources from that cache.
936 // We also have a lesser bias to use resources from caches that are currently
937 // being used by other unrelated pages.
938 // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
939 // - when navigating a frame whose current contents are from an appcache
940 // - when clicking an href in a frame that is appcached
941 int64 preferred_cache_id
= kAppCacheNoCacheId
;
942 if (!preferred_manifest_url_
.is_empty()) {
943 AppCacheDatabase::GroupRecord preferred_group
;
944 AppCacheDatabase::CacheRecord preferred_cache
;
945 if (database_
->FindGroupForManifestUrl(
946 preferred_manifest_url_
, &preferred_group
) &&
947 database_
->FindCacheForGroup(
948 preferred_group
.group_id
, &preferred_cache
)) {
949 preferred_cache_id
= preferred_cache
.cache_id
;
953 if (FindExactMatch(preferred_cache_id
) ||
954 FindNamespaceMatch(preferred_cache_id
)) {
955 // We found something.
956 DCHECK(cache_id_
!= kAppCacheNoCacheId
&& !manifest_url_
.is_empty() &&
961 // We didn't find anything.
962 DCHECK(cache_id_
== kAppCacheNoCacheId
&& manifest_url_
.is_empty() &&
966 bool AppCacheStorageImpl::
967 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id
) {
968 std::vector
<AppCacheDatabase::EntryRecord
> entries
;
969 if (database_
->FindEntriesForUrl(url_
, &entries
) && !entries
.empty()) {
970 // Sort them in order of preference, from the preferred_cache first,
971 // followed by hits from caches that are 'in use', then the rest.
972 std::sort(entries
.begin(), entries
.end(),
973 SortByCachePreference(preferred_cache_id
, cache_ids_in_use_
));
975 // Take the first with a valid, non-foreign entry.
976 std::vector
<AppCacheDatabase::EntryRecord
>::iterator iter
;
977 for (iter
= entries
.begin(); iter
< entries
.end(); ++iter
) {
978 AppCacheDatabase::GroupRecord group_record
;
979 if ((iter
->flags
& AppCacheEntry::FOREIGN
) ||
980 !database_
->FindGroupForCache(iter
->cache_id
, &group_record
)) {
983 manifest_url_
= group_record
.manifest_url
;
984 group_id_
= group_record
.group_id
;
985 entry_
= AppCacheEntry(iter
->flags
, iter
->response_id
);
986 cache_id_
= iter
->cache_id
;
987 return true; // We found an exact match.
993 bool AppCacheStorageImpl::
994 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id
) {
995 AppCacheDatabase::NamespaceRecordVector all_intercepts
;
996 AppCacheDatabase::NamespaceRecordVector all_fallbacks
;
997 if (!database_
->FindNamespacesForOrigin(
998 url_
.GetOrigin(), &all_intercepts
, &all_fallbacks
)
999 || (all_intercepts
.empty() && all_fallbacks
.empty())) {
1003 NetworkNamespaceHelper
network_namespace_helper(database_
);
1004 if (FindNamespaceHelper(preferred_cache_id
,
1006 &network_namespace_helper
) ||
1007 FindNamespaceHelper(preferred_cache_id
,
1009 &network_namespace_helper
)) {
1015 bool AppCacheStorageImpl::
1016 FindMainResponseTask::FindNamespaceHelper(
1017 int64 preferred_cache_id
,
1018 AppCacheDatabase::NamespaceRecordVector
* namespaces
,
1019 NetworkNamespaceHelper
* network_namespace_helper
) {
1020 // Sort them by length, longer matches within the same cache/bucket take
1022 std::sort(namespaces
->begin(), namespaces
->end(), SortByLength
);
1024 NamespaceRecordPtrVector preferred_namespaces
;
1025 NamespaceRecordPtrVector inuse_namespaces
;
1026 NamespaceRecordPtrVector other_namespaces
;
1027 std::vector
<AppCacheDatabase::NamespaceRecord
>::iterator iter
;
1028 for (iter
= namespaces
->begin(); iter
< namespaces
->end(); ++iter
) {
1029 // Skip those that aren't a match.
1030 if (!iter
->namespace_
.IsMatch(url_
))
1033 // Skip namespaces where the requested url falls into a network
1034 // namespace of its containing appcache.
1035 if (network_namespace_helper
->IsInNetworkNamespace(url_
, iter
->cache_id
))
1038 // Bin them into one of our three buckets.
1039 if (iter
->cache_id
== preferred_cache_id
)
1040 preferred_namespaces
.push_back(&(*iter
));
1041 else if (cache_ids_in_use_
.find(iter
->cache_id
) != cache_ids_in_use_
.end())
1042 inuse_namespaces
.push_back(&(*iter
));
1044 other_namespaces
.push_back(&(*iter
));
1047 if (FindFirstValidNamespace(preferred_namespaces
) ||
1048 FindFirstValidNamespace(inuse_namespaces
) ||
1049 FindFirstValidNamespace(other_namespaces
))
1050 return true; // We found one.
1052 // We didn't find anything.
1056 bool AppCacheStorageImpl::
1057 FindMainResponseTask::FindFirstValidNamespace(
1058 const NamespaceRecordPtrVector
& namespaces
) {
1059 // Take the first with a valid, non-foreign entry.
1060 NamespaceRecordPtrVector::const_iterator iter
;
1061 for (iter
= namespaces
.begin(); iter
< namespaces
.end(); ++iter
) {
1062 AppCacheDatabase::EntryRecord entry_record
;
1063 if (database_
->FindEntry((*iter
)->cache_id
, (*iter
)->namespace_
.target_url
,
1065 AppCacheDatabase::GroupRecord group_record
;
1066 if ((entry_record
.flags
& AppCacheEntry::FOREIGN
) ||
1067 !database_
->FindGroupForCache(entry_record
.cache_id
, &group_record
)) {
1070 manifest_url_
= group_record
.manifest_url
;
1071 group_id_
= group_record
.group_id
;
1072 cache_id_
= (*iter
)->cache_id
;
1073 namespace_entry_url_
= (*iter
)->namespace_
.target_url
;
1074 if ((*iter
)->namespace_
.type
== APPCACHE_FALLBACK_NAMESPACE
)
1075 fallback_entry_
= AppCacheEntry(entry_record
.flags
,
1076 entry_record
.response_id
);
1078 entry_
= AppCacheEntry(entry_record
.flags
, entry_record
.response_id
);
1079 return true; // We found one.
1082 return false; // We didn't find a match.
1085 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
1086 storage_
->CallOnMainResponseFound(
1087 &delegates_
, url_
, entry_
, namespace_entry_url_
, fallback_entry_
,
1088 cache_id_
, group_id_
, manifest_url_
);
1091 // MarkEntryAsForeignTask -------
1093 class AppCacheStorageImpl::MarkEntryAsForeignTask
: public DatabaseTask
{
1095 MarkEntryAsForeignTask(
1096 AppCacheStorageImpl
* storage
, const GURL
& url
, int64 cache_id
)
1097 : DatabaseTask(storage
), cache_id_(cache_id
), entry_url_(url
) {}
1100 virtual void Run() OVERRIDE
;
1101 virtual void RunCompleted() OVERRIDE
;
1104 virtual ~MarkEntryAsForeignTask() {}
1111 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
1112 database_
->AddEntryFlags(entry_url_
, cache_id_
, AppCacheEntry::FOREIGN
);
1115 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
1116 DCHECK(storage_
->pending_foreign_markings_
.front().first
== entry_url_
&&
1117 storage_
->pending_foreign_markings_
.front().second
== cache_id_
);
1118 storage_
->pending_foreign_markings_
.pop_front();
1121 // MakeGroupObsoleteTask -------
1123 class AppCacheStorageImpl::MakeGroupObsoleteTask
: public DatabaseTask
{
1125 MakeGroupObsoleteTask(AppCacheStorageImpl
* storage
,
1126 AppCacheGroup
* group
,
1130 virtual void Run() OVERRIDE
;
1131 virtual void RunCompleted() OVERRIDE
;
1132 virtual void CancelCompletion() OVERRIDE
;
1135 virtual ~MakeGroupObsoleteTask() {}
1138 scoped_refptr
<AppCacheGroup
> group_
;
1143 int64 new_origin_usage_
;
1144 std::vector
<int64
> newly_deletable_response_ids_
;
1147 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
1148 AppCacheStorageImpl
* storage
,
1149 AppCacheGroup
* group
,
1151 : DatabaseTask(storage
),
1153 group_id_(group
->group_id()),
1154 origin_(group
->manifest_url().GetOrigin()),
1156 response_code_(response_code
),
1157 new_origin_usage_(-1) {}
1159 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
1161 sql::Connection
* connection
= database_
->db_connection();
1165 sql::Transaction
transaction(connection
);
1166 if (!transaction
.Begin())
1169 AppCacheDatabase::GroupRecord group_record
;
1170 if (!database_
->FindGroup(group_id_
, &group_record
)) {
1171 // This group doesn't exists in the database, nothing todo here.
1172 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1177 DCHECK_EQ(group_record
.origin
, origin_
);
1178 success_
= DeleteGroupAndRelatedRecords(database_
,
1180 &newly_deletable_response_ids_
);
1182 new_origin_usage_
= database_
->GetOriginUsage(origin_
);
1183 success_
= success_
&& transaction
.Commit();
1186 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
1188 group_
->set_obsolete(true);
1189 if (!storage_
->is_disabled()) {
1190 storage_
->UpdateUsageMapAndNotify(origin_
, new_origin_usage_
);
1191 group_
->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_
);
1193 // Also remove from the working set, caches for an 'obsolete' group
1194 // may linger in use, but the group itself cannot be looked up by
1195 // 'manifest_url' in the working set any longer.
1196 storage_
->working_set()->RemoveGroup(group_
.get());
1200 delegates_
, OnGroupMadeObsolete(group_
.get(), success_
, response_code_
));
1204 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
1205 // Overriden to safely drop our reference to the group
1206 // which is not thread safe refcounted.
1207 DatabaseTask::CancelCompletion();
1211 // GetDeletableResponseIdsTask -------
1213 class AppCacheStorageImpl::GetDeletableResponseIdsTask
: public DatabaseTask
{
1215 GetDeletableResponseIdsTask(AppCacheStorageImpl
* storage
, int64 max_rowid
)
1216 : DatabaseTask(storage
), max_rowid_(max_rowid
) {}
1219 virtual void Run() OVERRIDE
;
1220 virtual void RunCompleted() OVERRIDE
;
1223 virtual ~GetDeletableResponseIdsTask() {}
1227 std::vector
<int64
> response_ids_
;
1230 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
1231 const int kSqlLimit
= 1000;
1232 database_
->GetDeletableResponseIds(&response_ids_
, max_rowid_
, kSqlLimit
);
1233 // TODO(michaeln): retrieve group_ids too
1236 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
1237 if (!response_ids_
.empty())
1238 storage_
->StartDeletingResponses(response_ids_
);
1241 // InsertDeletableResponseIdsTask -------
1243 class AppCacheStorageImpl::InsertDeletableResponseIdsTask
1244 : public DatabaseTask
{
1246 explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1247 : DatabaseTask(storage
) {}
1250 virtual void Run() OVERRIDE
;
1252 std::vector
<int64
> response_ids_
;
1255 virtual ~InsertDeletableResponseIdsTask() {}
1258 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
1259 database_
->InsertDeletableResponseIds(response_ids_
);
1260 // TODO(michaeln): store group_ids too
1263 // DeleteDeletableResponseIdsTask -------
1265 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
1266 : public DatabaseTask
{
1268 explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl
* storage
)
1269 : DatabaseTask(storage
) {}
1272 virtual void Run() OVERRIDE
;
1274 std::vector
<int64
> response_ids_
;
1277 virtual ~DeleteDeletableResponseIdsTask() {}
1280 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1281 database_
->DeleteDeletableResponseIds(response_ids_
);
1284 // UpdateGroupLastAccessTimeTask -------
1286 class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
1287 : public DatabaseTask
{
1289 UpdateGroupLastAccessTimeTask(
1290 AppCacheStorageImpl
* storage
, AppCacheGroup
* group
, base::Time time
)
1291 : DatabaseTask(storage
), group_id_(group
->group_id()),
1292 last_access_time_(time
) {
1293 storage
->NotifyStorageAccessed(group
->manifest_url().GetOrigin());
1297 virtual void Run() OVERRIDE
;
1300 virtual ~UpdateGroupLastAccessTimeTask() {}
1304 base::Time last_access_time_
;
1307 void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
1308 database_
->UpdateGroupLastAccessTime(group_id_
, last_access_time_
);
1312 // AppCacheStorageImpl ---------------------------------------------------
1314 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl
* service
)
1315 : AppCacheStorage(service
),
1316 is_incognito_(false),
1317 is_response_deletion_scheduled_(false),
1318 did_start_deleting_responses_(false),
1319 last_deletable_response_rowid_(0),
1321 is_disabled_(false),
1322 weak_factory_(this) {
1325 AppCacheStorageImpl::~AppCacheStorageImpl() {
1326 std::for_each(pending_quota_queries_
.begin(),
1327 pending_quota_queries_
.end(),
1328 std::mem_fun(&DatabaseTask::CancelCompletion
));
1329 std::for_each(scheduled_database_tasks_
.begin(),
1330 scheduled_database_tasks_
.end(),
1331 std::mem_fun(&DatabaseTask::CancelCompletion
));
1334 !db_thread_
->PostTask(
1336 base::Bind(&ClearSessionOnlyOrigins
, database_
,
1337 make_scoped_refptr(service_
->special_storage_policy()),
1338 service()->force_keep_session_state()))) {
1341 database_
= NULL
; // So no further database tasks can be scheduled.
1344 void AppCacheStorageImpl::Initialize(const base::FilePath
& cache_directory
,
1345 base::MessageLoopProxy
* db_thread
,
1346 base::MessageLoopProxy
* cache_thread
) {
1349 cache_directory_
= cache_directory
;
1350 is_incognito_
= cache_directory_
.empty();
1352 base::FilePath db_file_path
;
1354 db_file_path
= cache_directory_
.Append(kAppCacheDatabaseName
);
1355 database_
= new AppCacheDatabase(db_file_path
);
1357 db_thread_
= db_thread
;
1358 cache_thread_
= cache_thread
;
1360 scoped_refptr
<InitTask
> task(new InitTask(this));
1364 void AppCacheStorageImpl::Disable() {
1367 VLOG(1) << "Disabling appcache storage.";
1368 is_disabled_
= true;
1369 ClearUsageMapAndNotify();
1370 working_set()->Disable();
1372 disk_cache_
->Disable();
1373 scoped_refptr
<DisableDatabaseTask
> task(new DisableDatabaseTask(this));
1377 void AppCacheStorageImpl::GetAllInfo(Delegate
* delegate
) {
1379 scoped_refptr
<GetAllInfoTask
> task(new GetAllInfoTask(this));
1380 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1384 void AppCacheStorageImpl::LoadCache(int64 id
, Delegate
* delegate
) {
1387 delegate
->OnCacheLoaded(NULL
, id
);
1391 AppCache
* cache
= working_set_
.GetCache(id
);
1393 delegate
->OnCacheLoaded(cache
, id
);
1394 if (cache
->owning_group()) {
1395 scoped_refptr
<DatabaseTask
> update_task(
1396 new UpdateGroupLastAccessTimeTask(
1397 this, cache
->owning_group(), base::Time::Now()));
1398 update_task
->Schedule();
1402 scoped_refptr
<CacheLoadTask
> task(GetPendingCacheLoadTask(id
));
1404 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1407 task
= new CacheLoadTask(id
, this);
1408 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1410 pending_cache_loads_
[id
] = task
.get();
1413 void AppCacheStorageImpl::LoadOrCreateGroup(
1414 const GURL
& manifest_url
, Delegate
* delegate
) {
1417 delegate
->OnGroupLoaded(NULL
, manifest_url
);
1421 AppCacheGroup
* group
= working_set_
.GetGroup(manifest_url
);
1423 delegate
->OnGroupLoaded(group
, manifest_url
);
1424 scoped_refptr
<DatabaseTask
> update_task(
1425 new UpdateGroupLastAccessTimeTask(
1426 this, group
, base::Time::Now()));
1427 update_task
->Schedule();
1431 scoped_refptr
<GroupLoadTask
> task(GetPendingGroupLoadTask(manifest_url
));
1433 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1437 if (usage_map_
.find(manifest_url
.GetOrigin()) == usage_map_
.end()) {
1438 // No need to query the database, return a new group immediately.
1439 scoped_refptr
<AppCacheGroup
> group(new AppCacheGroup(
1440 this, manifest_url
, NewGroupId()));
1441 delegate
->OnGroupLoaded(group
.get(), manifest_url
);
1445 task
= new GroupLoadTask(manifest_url
, this);
1446 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1448 pending_group_loads_
[manifest_url
] = task
.get();
1451 void AppCacheStorageImpl::StoreGroupAndNewestCache(
1452 AppCacheGroup
* group
, AppCache
* newest_cache
, Delegate
* delegate
) {
1453 // TODO(michaeln): distinguish between a simple update of an existing
1454 // cache that just adds new master entry(s), and the insertion of a
1455 // whole new cache. The StoreGroupAndCacheTask as written will handle
1456 // the simple update case in a very heavy weight way (delete all and
1457 // the reinsert all over again).
1458 DCHECK(group
&& delegate
&& newest_cache
);
1459 scoped_refptr
<StoreGroupAndCacheTask
> task(
1460 new StoreGroupAndCacheTask(this, group
, newest_cache
));
1461 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1462 task
->GetQuotaThenSchedule();
1464 // TODO(michaeln): histogram is fishing for clues to crbug/95101
1465 if (!newest_cache
->GetEntry(group
->manifest_url())) {
1466 AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
1467 AppCacheHistograms::CALLSITE_3
);
1471 void AppCacheStorageImpl::FindResponseForMainRequest(
1472 const GURL
& url
, const GURL
& preferred_manifest_url
,
1473 Delegate
* delegate
) {
1476 const GURL
* url_ptr
= &url
;
1478 if (url
.has_ref()) {
1479 GURL::Replacements replacements
;
1480 replacements
.ClearRef();
1481 url_no_ref
= url
.ReplaceComponents(replacements
);
1482 url_ptr
= &url_no_ref
;
1485 const GURL origin
= url
.GetOrigin();
1487 // First look in our working set for a direct hit without having to query
1489 const AppCacheWorkingSet::GroupMap
* groups_in_use
=
1490 working_set()->GetGroupsInOrigin(origin
);
1491 if (groups_in_use
) {
1492 if (!preferred_manifest_url
.is_empty()) {
1493 AppCacheWorkingSet::GroupMap::const_iterator found
=
1494 groups_in_use
->find(preferred_manifest_url
);
1495 if (found
!= groups_in_use
->end() &&
1496 FindResponseForMainRequestInGroup(
1497 found
->second
, *url_ptr
, delegate
)) {
1501 for (AppCacheWorkingSet::GroupMap::const_iterator it
=
1502 groups_in_use
->begin();
1503 it
!= groups_in_use
->end(); ++it
) {
1504 if (FindResponseForMainRequestInGroup(
1505 it
->second
, *url_ptr
, delegate
)) {
1512 if (IsInitTaskComplete() && usage_map_
.find(origin
) == usage_map_
.end()) {
1513 // No need to query the database, return async'ly but without going thru
1515 scoped_refptr
<AppCacheGroup
> no_group
;
1516 scoped_refptr
<AppCache
> no_cache
;
1518 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1519 weak_factory_
.GetWeakPtr(), url
, AppCacheEntry(), no_group
,
1521 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1525 // We have to query the database, schedule a database task to do so.
1526 scoped_refptr
<FindMainResponseTask
> task(
1527 new FindMainResponseTask(this, *url_ptr
, preferred_manifest_url
,
1529 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1533 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
1534 AppCacheGroup
* group
, const GURL
& url
, Delegate
* delegate
) {
1535 AppCache
* cache
= group
->newest_complete_cache();
1536 if (group
->is_obsolete() || !cache
)
1539 AppCacheEntry
* entry
= cache
->GetEntry(url
);
1540 if (!entry
|| entry
->IsForeign())
1544 base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse
,
1545 weak_factory_
.GetWeakPtr(), url
, *entry
,
1546 make_scoped_refptr(group
), make_scoped_refptr(cache
),
1547 make_scoped_refptr(GetOrCreateDelegateReference(delegate
))));
1551 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
1553 const AppCacheEntry
& found_entry
,
1554 scoped_refptr
<AppCacheGroup
> group
,
1555 scoped_refptr
<AppCache
> cache
,
1556 scoped_refptr
<DelegateReference
> delegate_ref
) {
1557 if (delegate_ref
->delegate
) {
1558 DelegateReferenceVector
delegates(1, delegate_ref
);
1559 CallOnMainResponseFound(
1560 &delegates
, url
, found_entry
,
1561 GURL(), AppCacheEntry(),
1562 cache
.get() ? cache
->cache_id() : kAppCacheNoCacheId
,
1563 group
.get() ? group
->group_id() : kAppCacheNoCacheId
,
1564 group
.get() ? group
->manifest_url() : GURL());
1568 void AppCacheStorageImpl::CallOnMainResponseFound(
1569 DelegateReferenceVector
* delegates
,
1570 const GURL
& url
, const AppCacheEntry
& entry
,
1571 const GURL
& namespace_entry_url
, const AppCacheEntry
& fallback_entry
,
1572 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
1575 OnMainResponseFound(url
, entry
,
1576 namespace_entry_url
, fallback_entry
,
1577 cache_id
, group_id
, manifest_url
));
1580 void AppCacheStorageImpl::FindResponseForSubRequest(
1581 AppCache
* cache
, const GURL
& url
,
1582 AppCacheEntry
* found_entry
, AppCacheEntry
* found_fallback_entry
,
1583 bool* found_network_namespace
) {
1584 DCHECK(cache
&& cache
->is_complete());
1586 // When a group is forcibly deleted, all subresource loads for pages
1587 // using caches in the group will result in a synthesized network errors.
1588 // Forcible deletion is not a function that is covered by the HTML5 spec.
1589 if (cache
->owning_group()->is_being_deleted()) {
1590 *found_entry
= AppCacheEntry();
1591 *found_fallback_entry
= AppCacheEntry();
1592 *found_network_namespace
= false;
1596 GURL fallback_namespace_not_used
;
1597 GURL intercept_namespace_not_used
;
1598 cache
->FindResponseForRequest(
1599 url
, found_entry
, &intercept_namespace_not_used
,
1600 found_fallback_entry
, &fallback_namespace_not_used
,
1601 found_network_namespace
);
1604 void AppCacheStorageImpl::MarkEntryAsForeign(
1605 const GURL
& entry_url
, int64 cache_id
) {
1606 AppCache
* cache
= working_set_
.GetCache(cache_id
);
1608 AppCacheEntry
* entry
= cache
->GetEntry(entry_url
);
1611 entry
->add_types(AppCacheEntry::FOREIGN
);
1613 scoped_refptr
<MarkEntryAsForeignTask
> task(
1614 new MarkEntryAsForeignTask(this, entry_url
, cache_id
));
1616 pending_foreign_markings_
.push_back(std::make_pair(entry_url
, cache_id
));
1619 void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup
* group
,
1621 int response_code
) {
1622 DCHECK(group
&& delegate
);
1623 scoped_refptr
<MakeGroupObsoleteTask
> task(
1624 new MakeGroupObsoleteTask(this, group
, response_code
));
1625 task
->AddDelegate(GetOrCreateDelegateReference(delegate
));
1629 AppCacheResponseReader
* AppCacheStorageImpl::CreateResponseReader(
1630 const GURL
& manifest_url
, int64 group_id
, int64 response_id
) {
1631 return new AppCacheResponseReader(response_id
, group_id
, disk_cache());
1634 AppCacheResponseWriter
* AppCacheStorageImpl::CreateResponseWriter(
1635 const GURL
& manifest_url
, int64 group_id
) {
1636 return new AppCacheResponseWriter(NewResponseId(), group_id
, disk_cache());
1639 void AppCacheStorageImpl::DoomResponses(
1640 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1641 if (response_ids
.empty())
1644 // Start deleting them from the disk cache lazily.
1645 StartDeletingResponses(response_ids
);
1647 // Also schedule a database task to record these ids in the
1648 // deletable responses table.
1649 // TODO(michaeln): There is a race here. If the browser crashes
1650 // prior to committing these rows to the database and prior to us
1651 // having deleted them from the disk cache, we'll never delete them.
1652 scoped_refptr
<InsertDeletableResponseIdsTask
> task(
1653 new InsertDeletableResponseIdsTask(this));
1654 task
->response_ids_
= response_ids
;
1658 void AppCacheStorageImpl::DeleteResponses(
1659 const GURL
& manifest_url
, const std::vector
<int64
>& response_ids
) {
1660 if (response_ids
.empty())
1662 StartDeletingResponses(response_ids
);
1665 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
1666 // Only if we haven't already begun.
1667 if (!did_start_deleting_responses_
) {
1668 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1669 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1674 void AppCacheStorageImpl::StartDeletingResponses(
1675 const std::vector
<int64
>& response_ids
) {
1676 DCHECK(!response_ids
.empty());
1677 did_start_deleting_responses_
= true;
1678 deletable_response_ids_
.insert(
1679 deletable_response_ids_
.end(),
1680 response_ids
.begin(), response_ids
.end());
1681 if (!is_response_deletion_scheduled_
)
1682 ScheduleDeleteOneResponse();
1685 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
1686 DCHECK(!is_response_deletion_scheduled_
);
1687 const base::TimeDelta kDelay
= base::TimeDelta::FromMilliseconds(10);
1688 base::MessageLoop::current()->PostDelayedTask(
1690 base::Bind(&AppCacheStorageImpl::DeleteOneResponse
,
1691 weak_factory_
.GetWeakPtr()),
1693 is_response_deletion_scheduled_
= true;
1696 void AppCacheStorageImpl::DeleteOneResponse() {
1697 DCHECK(is_response_deletion_scheduled_
);
1698 DCHECK(!deletable_response_ids_
.empty());
1700 if (!disk_cache()) {
1701 DCHECK(is_disabled_
);
1702 deletable_response_ids_
.clear();
1703 deleted_response_ids_
.clear();
1704 is_response_deletion_scheduled_
= false;
1708 // TODO(michaeln): add group_id to DoomEntry args
1709 int64 id
= deletable_response_ids_
.front();
1710 int rv
= disk_cache_
->DoomEntry(
1711 id
, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse
,
1712 base::Unretained(this)));
1713 if (rv
!= net::ERR_IO_PENDING
)
1714 OnDeletedOneResponse(rv
);
1717 void AppCacheStorageImpl::OnDeletedOneResponse(int rv
) {
1718 is_response_deletion_scheduled_
= false;
1722 int64 id
= deletable_response_ids_
.front();
1723 deletable_response_ids_
.pop_front();
1724 if (rv
!= net::ERR_ABORTED
)
1725 deleted_response_ids_
.push_back(id
);
1727 const size_t kBatchSize
= 50U;
1728 if (deleted_response_ids_
.size() >= kBatchSize
||
1729 deletable_response_ids_
.empty()) {
1730 scoped_refptr
<DeleteDeletableResponseIdsTask
> task(
1731 new DeleteDeletableResponseIdsTask(this));
1732 task
->response_ids_
.swap(deleted_response_ids_
);
1736 if (deletable_response_ids_
.empty()) {
1737 scoped_refptr
<GetDeletableResponseIdsTask
> task(
1738 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_
));
1743 ScheduleDeleteOneResponse();
1746 AppCacheStorageImpl::CacheLoadTask
*
1747 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id
) {
1748 PendingCacheLoads::iterator found
= pending_cache_loads_
.find(cache_id
);
1749 if (found
!= pending_cache_loads_
.end())
1750 return found
->second
;
1754 AppCacheStorageImpl::GroupLoadTask
*
1755 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL
& manifest_url
) {
1756 PendingGroupLoads::iterator found
= pending_group_loads_
.find(manifest_url
);
1757 if (found
!= pending_group_loads_
.end())
1758 return found
->second
;
1762 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
1763 int64 cache_id
, std::vector
<GURL
>* urls
) {
1764 PendingForeignMarkings::iterator iter
= pending_foreign_markings_
.begin();
1765 while (iter
!= pending_foreign_markings_
.end()) {
1766 if (iter
->second
== cache_id
)
1767 urls
->push_back(iter
->first
);
1772 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure
& task
) {
1773 pending_simple_tasks_
.push_back(task
);
1774 base::MessageLoop::current()->PostTask(
1776 base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask
,
1777 weak_factory_
.GetWeakPtr()));
1780 void AppCacheStorageImpl::RunOnePendingSimpleTask() {
1781 DCHECK(!pending_simple_tasks_
.empty());
1782 base::Closure task
= pending_simple_tasks_
.front();
1783 pending_simple_tasks_
.pop_front();
1787 AppCacheDiskCache
* AppCacheStorageImpl::disk_cache() {
1788 DCHECK(IsInitTaskComplete());
1795 disk_cache_
.reset(new AppCacheDiskCache
);
1796 if (is_incognito_
) {
1797 rv
= disk_cache_
->InitWithMemBackend(
1798 kMaxMemDiskCacheSize
,
1799 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1800 base::Unretained(this)));
1802 rv
= disk_cache_
->InitWithDiskBackend(
1803 cache_directory_
.Append(kDiskCacheDirectoryName
),
1806 cache_thread_
.get(),
1807 base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized
,
1808 base::Unretained(this)));
1811 if (rv
!= net::ERR_IO_PENDING
)
1812 OnDiskCacheInitialized(rv
);
1814 return disk_cache_
.get();
1817 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv
) {
1818 if (rv
!= net::OK
) {
1819 LOG(ERROR
) << "Failed to open the appcache diskcache.";
1820 AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR
);
1822 // We're unable to open the disk cache, this is a fatal error that we can't
1823 // really recover from. We handle it by temporarily disabling the appcache
1824 // deleting the directory on disk and reinitializing the appcache system.
1826 if (rv
!= net::ERR_ABORTED
)
1827 DeleteAndStartOver();
1831 void AppCacheStorageImpl::DeleteAndStartOver() {
1832 DCHECK(is_disabled_
);
1833 if (!is_incognito_
) {
1834 VLOG(1) << "Deleting existing appcache data and starting over.";
1835 // We can have tasks in flight to close file handles on both the db
1836 // and cache threads, we need to allow those tasks to cycle thru
1837 // prior to deleting the files and calling reinit.
1838 cache_thread_
->PostTaskAndReply(
1840 base::Bind(&base::DoNothing
),
1841 base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2
,
1842 weak_factory_
.GetWeakPtr()));
1846 void AppCacheStorageImpl::DeleteAndStartOverPart2() {
1847 db_thread_
->PostTaskAndReply(
1849 base::Bind(base::IgnoreResult(&base::DeleteFile
),
1850 cache_directory_
, true),
1851 base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize
,
1852 weak_factory_
.GetWeakPtr()));
1855 void AppCacheStorageImpl::CallScheduleReinitialize() {
1856 service_
->ScheduleReinitialize();
1857 // note: 'this' may be deleted at this point.
1860 } // namespace appcache