Chromium Code Reviews| Index: webkit/appcache/appcache_storage_impl.cc |
| =================================================================== |
| --- webkit/appcache/appcache_storage_impl.cc (revision 88004) |
| +++ webkit/appcache/appcache_storage_impl.cc (working copy) |
| @@ -4,6 +4,8 @@ |
| #include "webkit/appcache/appcache_storage_impl.h" |
| +#include <set> |
| + |
| #include "app/sql/connection.h" |
| #include "app/sql/transaction.h" |
| #include "base/file_util.h" |
| @@ -19,9 +21,12 @@ |
| #include "webkit/appcache/appcache_group.h" |
| #include "webkit/appcache/appcache_histograms.h" |
| #include "webkit/appcache/appcache_policy.h" |
| +#include "webkit/appcache/appcache_quota_client.h" |
| #include "webkit/appcache/appcache_response.h" |
| #include "webkit/appcache/appcache_service.h" |
| #include "webkit/appcache/appcache_thread.h" |
| +#include "webkit/quota/quota_client.h" |
| +#include "webkit/quota/quota_manager.h" |
| #include "webkit/quota/special_storage_policy.h" |
| namespace { |
| @@ -33,11 +38,15 @@ |
| namespace appcache { |
| +// Hard coded default when not using quota management. |
| +static const int kDefaultQuota = 5 * 1024 * 1024; |
| + |
| static const int kMaxDiskCacheSize = 250 * 1024 * 1024; |
| static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024; |
| static const FilePath::CharType kDiskCacheDirectoryName[] = |
| FILE_PATH_LITERAL("Cache"); |
| + |
| // DatabaseTask ----------------------------------------- |
| class AppCacheStorageImpl::DatabaseTask |
| @@ -146,14 +155,14 @@ |
| int64 last_cache_id_; |
| int64 last_response_id_; |
| int64 last_deletable_response_rowid_; |
| - std::set<GURL> origins_with_groups_; |
| + std::map<GURL, int64> usage_map_; |
| }; |
| void AppCacheStorageImpl::InitTask::Run() { |
| database_->FindLastStorageIds( |
| &last_group_id_, &last_cache_id_, &last_response_id_, |
| &last_deletable_response_rowid_); |
| - database_->FindOriginsWithGroups(&origins_with_groups_); |
| + database_->GetAllOriginUsage(&usage_map_); |
| } |
| void AppCacheStorageImpl::InitTask::RunCompleted() { |
| @@ -163,14 +172,16 @@ |
| storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_; |
| if (!storage_->is_disabled()) { |
| - storage_->origins_with_groups_.swap(origins_with_groups_); |
| - |
| + storage_->usage_map_.swap(usage_map_); |
| const int kDelayMillis = 5 * 60 * 1000; // Five minutes. |
| MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| storage_->method_factory_.NewRunnableMethod( |
| &AppCacheStorageImpl::DelayedStartDeletingUnusedResponses), |
| kDelayMillis); |
| } |
| + |
| + if (storage_->service()->quota_client()) |
| + storage_->service()->quota_client()->NotifyAppCacheReady(); |
| } |
| // CloseConnectionTask ------- |
| @@ -302,6 +313,8 @@ |
| cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN); |
| } |
| + storage_->NotifyStorageAccessed(group_record_.origin); |
| + |
| // TODO(michaeln): Maybe verify that the responses we expect to exist |
| // do actually exist in the disk_cache (and if not then what?) |
| } |
| @@ -395,6 +408,10 @@ |
| StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group, |
| AppCache* newest_cache); |
| + void GetQuotaThenSchedule(); |
| + void OnQuotaCallback( |
| + quota::QuotaStatusCode status, int64 usage, int64 quota); |
| + |
| virtual void Run(); |
| virtual void RunCompleted(); |
| virtual void CancelCompletion(); |
| @@ -403,7 +420,8 @@ |
| scoped_refptr<AppCache> cache_; |
| bool success_; |
| bool would_exceed_quota_; |
| - int64 quota_override_; |
| + int64 space_available_; |
| + int64 new_origin_usage_; |
| std::vector<int64> newly_deletable_response_ids_; |
| }; |
| @@ -411,7 +429,7 @@ |
| AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache) |
| : StoreOrLoadTask(storage), group_(group), cache_(newest_cache), |
| success_(false), would_exceed_quota_(false), |
| - quota_override_(-1) { |
| + space_available_(-1), new_origin_usage_(-1) { |
| group_record_.group_id = group->group_id(); |
| group_record_.manifest_url = group->manifest_url(); |
| group_record_.origin = group_record_.manifest_url.GetOrigin(); |
| @@ -419,14 +437,44 @@ |
| group, |
| &cache_record_, &entry_records_, &fallback_namespace_records_, |
| &online_whitelist_records_); |
| +} |
| - if (storage->service()->special_storage_policy() && |
| - storage->service()->special_storage_policy()->IsStorageUnlimited( |
| - group_record_.origin)) { |
| - quota_override_ = kint64max; |
| +void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() { |
| + quota::QuotaManager* quota_manager = NULL; |
| + if (storage_->service()->quota_manager_proxy()) { |
| + quota_manager = |
| + storage_->service()->quota_manager_proxy()->quota_manager(); |
| } |
| + |
| + if (!quota_manager) { |
| + if (storage_->service()->special_storage_policy() && |
| + storage_->service()->special_storage_policy()->IsStorageUnlimited( |
| + group_record_.origin)) |
| + space_available_ = kint64max; |
| + Schedule(); |
| + return; |
| + } |
| + |
| + // We have to ask the quota manager for the value. |
| + AddRef(); // balanced in the OnQuotaCallback |
| + storage_->pending_quota_queries_.insert(this); |
| + quota_manager->GetUsageAndQuota( |
| + group_record_.origin, quota::kStorageTypeTemporary, |
| + NewCallback(this, &StoreGroupAndCacheTask::OnQuotaCallback)); |
| } |
| +void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback( |
| + quota::QuotaStatusCode status, int64 usage, int64 quota) { |
| + // TODO(michaeln): what do we do if status is not OK, fail the store op? |
|
michaeln
2011/06/08 18:09:54
Took care of this TODO by setting space_available_
|
| + DCHECK_EQ(quota::kQuotaStatusOk, status); |
| + space_available_ = std::max(static_cast<int64>(0), quota - usage); |
| + if (storage_) { |
| + storage_->pending_quota_queries_.erase(this); |
| + Schedule(); |
| + } |
| + Release(); // balanced in GetQuotaThenSchedule |
| +} |
| + |
| void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() { |
| DCHECK(!success_); |
| sql::Connection* connection = database_->db_connection(); |
| @@ -437,6 +485,8 @@ |
| if (!transaction.Begin()) |
| return; |
| + int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin); |
| + |
| AppCacheDatabase::GroupRecord existing_group; |
| success_ = database_->FindGroup(group_record_.group_id, &existing_group); |
| if (!success_) { |
| @@ -494,11 +544,29 @@ |
| if (!success_) |
| return; |
| - int64 quota = (quota_override_ >= 0) ? |
| - quota_override_ : |
| - database_->GetOriginQuota(group_record_.origin); |
| + new_origin_usage_ = database_->GetOriginUsage(group_record_.origin); |
| - if (database_->GetOriginUsage(group_record_.origin) > quota) { |
| + // Only check quota when the new usage exceeds the old usage. |
| + if (new_origin_usage_ <= old_origin_usage) { |
| + success_ = transaction.Commit(); |
| + return; |
| + } |
| + |
| + // Use a simple hard-coded value when not using quota managment. |
| + if (space_available_ == -1) { |
| + if (new_origin_usage_ > kDefaultQuota) { |
| + would_exceed_quota_ = true; |
| + success_ = false; |
| + return; |
| + } |
| + success_ = transaction.Commit(); |
| + return; |
| + } |
| + |
| + // Check limits based on the space availbable given to us via the |
| + // quota system. |
| + int64 delta = new_origin_usage_ - old_origin_usage; |
| + if (delta > space_available_) { |
| would_exceed_quota_ = true; |
| success_ = false; |
| return; |
| @@ -509,7 +577,8 @@ |
| void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() { |
| if (success_) { |
| - storage_->origins_with_groups_.insert(group_->manifest_url().GetOrigin()); |
| + storage_->UpdateUsageMapAndNotify( |
| + group_->manifest_url().GetOrigin(), new_origin_usage_); |
| if (cache_ != group_->newest_complete_cache()) { |
| cache_->set_complete(true); |
| group_->AddCache(cache_); |
| @@ -837,15 +906,17 @@ |
| scoped_refptr<AppCacheGroup> group_; |
| int64 group_id_; |
| + GURL origin_; |
| bool success_; |
| - std::set<GURL> origins_with_groups_; |
| + int64 new_origin_usage_; |
| std::vector<int64> newly_deletable_response_ids_; |
| }; |
| AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask( |
| AppCacheStorageImpl* storage, AppCacheGroup* group) |
| : DatabaseTask(storage), group_(group), group_id_(group->group_id()), |
| - success_(false) { |
| + origin_(group->manifest_url().GetOrigin()), |
| + success_(false), new_origin_usage_(-1) { |
| } |
| void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() { |
| @@ -861,10 +932,13 @@ |
| AppCacheDatabase::GroupRecord group_record; |
| if (!database_->FindGroup(group_id_, &group_record)) { |
| // This group doesn't exists in the database, nothing todo here. |
| + new_origin_usage_ = database_->GetOriginUsage(origin_); |
| success_ = true; |
| return; |
| } |
| + DCHECK_EQ(group_record.origin, origin_); |
| + |
| AppCacheDatabase::CacheRecord cache_record; |
| if (database_->FindCacheForGroup(group_id_, &cache_record)) { |
| database_->FindResponseIdsForCacheAsVector(cache_record.cache_id, |
| @@ -881,16 +955,15 @@ |
| success_ = database_->DeleteGroup(group_id_); |
| } |
| - success_ = success_ && |
| - database_->FindOriginsWithGroups(&origins_with_groups_) && |
| - transaction.Commit(); |
| + new_origin_usage_ = database_->GetOriginUsage(origin_); |
| + success_ = success_ && transaction.Commit(); |
| } |
| void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() { |
| if (success_) { |
| group_->set_obsolete(true); |
| if (!storage_->is_disabled()) { |
| - storage_->origins_with_groups_.swap(origins_with_groups_); |
| + storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_); |
| group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); |
| // Also remove from the working set, caches for an 'obsolete' group |
| @@ -970,8 +1043,11 @@ |
| : public DatabaseTask { |
| public: |
| UpdateGroupLastAccessTimeTask( |
| - AppCacheStorageImpl* storage, int64 group_id, base::Time time) |
| - : DatabaseTask(storage), group_id_(group_id), last_access_time_(time) {} |
| + AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time) |
| + : DatabaseTask(storage), group_id_(group->group_id()), |
| + last_access_time_(time) { |
| + storage->NotifyStorageAccessed(group->manifest_url().GetOrigin()); |
| + } |
| virtual void Run(); |
| @@ -1002,6 +1078,9 @@ |
| AppCacheStorageImpl::~AppCacheStorageImpl() { |
| STLDeleteElements(&pending_simple_tasks_); |
| + std::for_each(pending_quota_queries_.begin(), |
| + pending_quota_queries_.end(), |
| + std::mem_fun(&DatabaseTask::CancelCompletion)); |
| std::for_each(scheduled_database_tasks_.begin(), |
| scheduled_database_tasks_.end(), |
| std::mem_fun(&DatabaseTask::CancelCompletion)); |
| @@ -1030,7 +1109,7 @@ |
| return; |
| VLOG(1) << "Disabling appcache storage."; |
| is_disabled_ = true; |
| - origins_with_groups_.clear(); |
| + usage_map_.clear(); // TODO(michaeln): What to tell the QuotaManager? |
|
michaeln
2011/06/08 18:09:54
Took are of this TODO with ClearUsageMapAndNotify(
|
| working_set()->Disable(); |
| if (disk_cache_.get()) |
| disk_cache_->Disable(); |
| @@ -1058,7 +1137,7 @@ |
| if (cache->owning_group()) { |
| scoped_refptr<DatabaseTask> update_task( |
| new UpdateGroupLastAccessTimeTask( |
| - this, cache->owning_group()->group_id(), base::Time::Now())); |
| + this, cache->owning_group(), base::Time::Now())); |
| update_task->Schedule(); |
| } |
| return; |
| @@ -1087,7 +1166,7 @@ |
| delegate->OnGroupLoaded(group, manifest_url); |
| scoped_refptr<DatabaseTask> update_task( |
| new UpdateGroupLastAccessTimeTask( |
| - this, group->group_id(), base::Time::Now())); |
| + this, group, base::Time::Now())); |
| update_task->Schedule(); |
| return; |
| } |
| @@ -1098,8 +1177,7 @@ |
| return; |
| } |
| - if (origins_with_groups_.find(manifest_url.GetOrigin()) == |
| - origins_with_groups_.end()) { |
| + if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) { |
| // No need to query the database, return a new group immediately. |
| scoped_refptr<AppCacheGroup> group(new AppCacheGroup( |
| service_, manifest_url, NewGroupId())); |
| @@ -1124,7 +1202,7 @@ |
| scoped_refptr<StoreGroupAndCacheTask> task( |
| new StoreGroupAndCacheTask(this, group, newest_cache)); |
| task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
| - task->Schedule(); |
| + task->GetQuotaThenSchedule(); |
| } |
| void AppCacheStorageImpl::FindResponseForMainRequest( |
| @@ -1168,8 +1246,7 @@ |
| } |
| } |
| - if (IsInitTaskComplete() && |
| - origins_with_groups_.find(origin) == origins_with_groups_.end()) { |
| + if (IsInitTaskComplete() && usage_map_.find(origin) == usage_map_.end()) { |
| // No need to query the database, return async'ly but without going thru |
| // the DB thread. |
| scoped_refptr<AppCacheGroup> no_group; |