| Index: webkit/appcache/appcache_storage_impl.cc
|
| ===================================================================
|
| --- webkit/appcache/appcache_storage_impl.cc (revision 88615)
|
| +++ 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,6 +38,9 @@
|
|
|
| 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[] =
|
| @@ -146,14 +154,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 +171,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 +312,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 +407,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 +419,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 +428,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 +436,45 @@
|
| 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) {
|
| + if (storage_) {
|
| + if (status == quota::kQuotaStatusOk)
|
| + space_available_ = std::max(static_cast<int64>(0), quota - usage);
|
| + else
|
| + space_available_ = 0;
|
| + 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 management.
|
| + 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_);
|
| @@ -523,6 +592,9 @@
|
| would_exceed_quota_));
|
| group_ = NULL;
|
| cache_ = NULL;
|
| +
|
| + // TODO(michaeln): if (would_exceed_quota_) what if the current usage
|
| + // also exceeds the quota? http://crbug.com/83968
|
| }
|
|
|
| void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
|
| @@ -837,15 +909,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 +935,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 +958,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 +1046,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 +1081,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 +1112,7 @@
|
| return;
|
| VLOG(1) << "Disabling appcache storage.";
|
| is_disabled_ = true;
|
| - origins_with_groups_.clear();
|
| + ClearUsageMapAndNotify();
|
| working_set()->Disable();
|
| if (disk_cache_.get())
|
| disk_cache_->Disable();
|
| @@ -1058,7 +1140,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 +1169,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 +1180,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 +1205,7 @@
|
| scoped_refptr<StoreGroupAndCacheTask> task(
|
| new StoreGroupAndCacheTask(this, group, newest_cache));
|
| task->AddDelegate(GetOrCreateDelegateReference(delegate));
|
| - task->Schedule();
|
| + task->GetQuotaThenSchedule();
|
| }
|
|
|
| void AppCacheStorageImpl::FindResponseForMainRequest(
|
| @@ -1168,8 +1249,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;
|
|
|