| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "webkit/quota/quota_manager.h" |
| |
| #include <algorithm> |
| #include <deque> |
| #include <set> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/file_path.h" |
| #include "base/message_loop_proxy.h" |
| #include "base/metrics/histogram.h" |
| #include "base/string_number_conversions.h" |
| #include "base/sys_info.h" |
| #include "base/time.h" |
| #include "net/base/net_util.h" |
| #include "webkit/quota/quota_database.h" |
| #include "webkit/quota/quota_temporary_storage_evictor.h" |
| #include "webkit/quota/quota_types.h" |
| #include "webkit/quota/usage_tracker.h" |
| |
| #define UMA_HISTOGRAM_MBYTES(name, sample) \ |
| UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
| (name), static_cast<int>((sample) / kMBytes), \ |
| 1, 10 * 1024 * 1024 /* 10TB */, 100) |
| |
| namespace quota { |
| |
| namespace { |
| |
| const int64 kMBytes = 1024 * 1024; |
| const int kMinutesInMilliSeconds = 60 * 1000; |
| |
| const int64 kIncognitoDefaultTemporaryQuota = 50 * kMBytes; |
| const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour |
| const double kTemporaryQuotaRatioToAvail = 0.5; // 50% |
| |
| void CountOriginType(const std::set<GURL>& origins, |
| SpecialStoragePolicy* policy, |
| size_t* protected_origins, |
| size_t* unlimited_origins) { |
| DCHECK(protected_origins); |
| DCHECK(unlimited_origins); |
| *protected_origins = 0; |
| *unlimited_origins = 0; |
| if (!policy) |
| return; |
| for (std::set<GURL>::const_iterator itr = origins.begin(); |
| itr != origins.end(); |
| ++itr) { |
| if (policy->IsStorageProtected(*itr)) |
| ++*protected_origins; |
| if (policy->IsStorageUnlimited(*itr)) |
| ++*unlimited_origins; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| const int QuotaManager::kPerHostTemporaryPortion = 5; // 20% |
| |
| const char QuotaManager::kDatabaseName[] = "QuotaManager"; |
| |
| const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3; |
| |
| const int QuotaManager::kEvictionIntervalInMilliSeconds = |
| 30 * kMinutesInMilliSeconds; |
| |
| // Callback translators. |
| void CallGetUsageAndQuotaCallback( |
| const QuotaManager::GetUsageAndQuotaCallback& callback, |
| bool unlimited, |
| QuotaStatusCode status, |
| const QuotaAndUsage& quota_and_usage) { |
| int64 usage = |
| unlimited ? quota_and_usage.unlimited_usage : quota_and_usage.usage; |
| int64 quota = unlimited ? kint64max : quota_and_usage.quota; |
| callback.Run(status, usage, quota); |
| } |
| |
| void CallQuotaCallback( |
| const QuotaCallback& callback, |
| StorageType type, |
| QuotaStatusCode status, |
| const QuotaAndUsage& quota_and_usage) { |
| callback.Run(status, type, quota_and_usage.quota); |
| } |
| |
| // This class is for posting GetUsage/GetQuota tasks, gathering |
| // results and dispatching GetAndQuota callbacks. |
| // This class is self-destructed. |
| class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask { |
| public: |
| typedef UsageAndQuotaDispatcherCallback Callback; |
| typedef std::deque<Callback> CallbackList; |
| |
| static UsageAndQuotaDispatcherTask* Create( |
| QuotaManager* manager, |
| bool global, |
| const HostAndType& host_and_type); |
| |
| // Returns true if it is the first call for this task; which means |
| // the caller needs to call Start(). |
| bool AddCallback(const Callback& callback) { |
| callbacks_.push_back(callback); |
| return (callbacks_.size() == 1); |
| } |
| |
| void DidGetGlobalUsage(StorageType type, int64 usage, int64 unlimited_usage) { |
| DCHECK_EQ(this->type(), type); |
| DCHECK_GE(usage, unlimited_usage); |
| if (quota_status_ == kQuotaStatusUnknown) |
| quota_status_ = kQuotaStatusOk; |
| global_usage_ = usage; |
| global_unlimited_usage_ = unlimited_usage; |
| CheckCompleted(); |
| } |
| |
| void DidGetHostUsage(const std::string& host, StorageType type, int64 usage) { |
| DCHECK_EQ(this->host(), host); |
| DCHECK_EQ(this->type(), type); |
| host_usage_ = usage; |
| CheckCompleted(); |
| } |
| |
| void DidGetHostQuota(QuotaStatusCode status, |
| const std::string& host, |
| StorageType type, |
| int64 host_quota) { |
| DCHECK_EQ(this->host(), host); |
| DCHECK_EQ(this->type(), type); |
| if (quota_status_ == kQuotaStatusUnknown || quota_status_ == kQuotaStatusOk) |
| quota_status_ = status; |
| host_quota_ = host_quota; |
| CheckCompleted(); |
| } |
| |
| void DidGetAvailableSpace(QuotaStatusCode status, int64 space) { |
| DCHECK_GE(space, 0); |
| if (quota_status_ == kQuotaStatusUnknown || quota_status_ == kQuotaStatusOk) |
| quota_status_ = status; |
| available_space_ = space; |
| CheckCompleted(); |
| } |
| |
| bool IsStartable() const { |
| return !started_ && !callbacks_.empty(); |
| } |
| |
| protected: |
| UsageAndQuotaDispatcherTask( |
| QuotaManager* manager, |
| const HostAndType& host_and_type) |
| : QuotaTask(manager), |
| host_and_type_(host_and_type), |
| started_(false), |
| host_quota_(-1), |
| global_usage_(-1), |
| global_unlimited_usage_(-1), |
| host_usage_(-1), |
| available_space_(-1), |
| quota_status_(kQuotaStatusUnknown), |
| waiting_callbacks_(1), |
| weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} |
| |
| virtual ~UsageAndQuotaDispatcherTask() {} |
| |
| // Subclasses must implement them. |
| virtual void RunBody() = 0; |
| virtual void DispatchCallbacks() = 0; |
| |
| virtual void Run() OVERRIDE { |
| DCHECK(!started_); |
| started_ = true; |
| RunBody(); |
| // We initialize waiting_callbacks to 1 so that we won't run |
| // the completion callback until here even some of the callbacks |
| // are dispatched synchronously. |
| CheckCompleted(); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| CallCallbacksAndClear(kQuotaErrorAbort, 0, 0, 0, 0); |
| DeleteSoon(); |
| } |
| |
| virtual void Completed() OVERRIDE { |
| DeleteSoon(); |
| } |
| |
| void CallCallbacksAndClear( |
| QuotaStatusCode status, |
| int64 usage, int64 unlimited_usage, int64 quota, |
| int64 available_space) { |
| QuotaAndUsage qau = { usage, unlimited_usage, quota, available_space }; |
| for (CallbackList::iterator iter = callbacks_.begin(); |
| iter != callbacks_.end(); ++iter) { |
| (*iter).Run(status, qau); |
| } |
| callbacks_.clear(); |
| } |
| |
| QuotaManager* manager() const { |
| return static_cast<QuotaManager*>(observer()); |
| } |
| |
| std::string host() const { return host_and_type_.first; } |
| virtual StorageType type() const { return host_and_type_.second; } |
| int64 host_quota() const { return host_quota_; } |
| int64 global_usage() const { return global_usage_; } |
| int64 global_unlimited_usage() const { return global_unlimited_usage_; } |
| int64 host_usage() const { return host_usage_; } |
| int64 available_space() const { return available_space_; } |
| QuotaStatusCode quota_status() const { return quota_status_; } |
| CallbackList& callbacks() { return callbacks_; } |
| |
| // The main logic that determines the temporary global quota. |
| int64 temporary_global_quota() const { |
| DCHECK_EQ(type(), kStorageTypeTemporary); |
| DCHECK(manager()); |
| DCHECK_GE(global_usage(), global_unlimited_usage()); |
| if (manager()->temporary_quota_override_ > 0) { |
| // If the user has specified an explicit temporary quota, use the value. |
| return manager()->temporary_quota_override_; |
| } |
| int64 limited_usage = global_usage() - global_unlimited_usage(); |
| int64 avail_space = available_space(); |
| if (avail_space < kint64max - limited_usage) { |
| // We basically calculate the temporary quota by |
| // [available_space + space_used_for_temp] * kTempQuotaRatio, |
| // but make sure we'll have no overflow. |
| avail_space += limited_usage; |
| } |
| return avail_space * kTemporaryQuotaRatioToAvail; |
| } |
| |
| // Subclasses must call following methods to create a new 'waitable' |
| // callback, which decrements waiting_callbacks when it is called. |
| GlobalUsageCallback NewWaitableGlobalUsageCallback() { |
| ++waiting_callbacks_; |
| return base::Bind(&UsageAndQuotaDispatcherTask::DidGetGlobalUsage, |
| weak_factory_.GetWeakPtr()); |
| } |
| HostUsageCallback NewWaitableHostUsageCallback() { |
| ++waiting_callbacks_; |
| return base::Bind(&UsageAndQuotaDispatcherTask::DidGetHostUsage, |
| weak_factory_.GetWeakPtr()); |
| } |
| HostQuotaCallback NewWaitableHostQuotaCallback() { |
| ++waiting_callbacks_; |
| return base::Bind(&UsageAndQuotaDispatcherTask::DidGetHostQuota, |
| weak_factory_.GetWeakPtr()); |
| } |
| AvailableSpaceCallback NewWaitableAvailableSpaceCallback() { |
| ++waiting_callbacks_; |
| return base::Bind(&UsageAndQuotaDispatcherTask::DidGetAvailableSpace, |
| weak_factory_.GetWeakPtr()); |
| } |
| |
| |
| private: |
| void CheckCompleted() { |
| if (--waiting_callbacks_ <= 0) { |
| DispatchCallbacks(); |
| DCHECK(callbacks_.empty()); |
| |
| UsageAndQuotaDispatcherTaskMap& dispatcher_map = |
| manager()->usage_and_quota_dispatchers_; |
| DCHECK(dispatcher_map.find(host_and_type_) != dispatcher_map.end()); |
| dispatcher_map.erase(host_and_type_); |
| CallCompleted(); |
| } |
| } |
| |
| const std::string host_; |
| const HostAndType host_and_type_; |
| bool started_; |
| int64 host_quota_; |
| int64 global_usage_; |
| int64 global_unlimited_usage_; |
| int64 host_usage_; |
| int64 available_space_; |
| QuotaStatusCode quota_status_; |
| CallbackList callbacks_; |
| int waiting_callbacks_; |
| base::WeakPtrFactory<UsageAndQuotaDispatcherTask> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaDispatcherTask); |
| }; |
| |
| class QuotaManager::GetUsageInfoTask : public QuotaTask { |
| private: |
| typedef QuotaManager::GetUsageInfoTask self_type; |
| |
| public: |
| GetUsageInfoTask( |
| QuotaManager* manager, |
| const GetUsageInfoCallback& callback) |
| : QuotaTask(manager), |
| callback_(callback), |
| weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| } |
| protected: |
| virtual void Run() OVERRIDE { |
| remaining_trackers_ = 2; |
| // This will populate cached hosts and usage info. |
| manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage( |
| base::Bind(&GetUsageInfoTask::DidGetGlobalUsage, |
| weak_factory_.GetWeakPtr())); |
| manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage( |
| base::Bind(&GetUsageInfoTask::DidGetGlobalUsage, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| virtual void Completed() OVERRIDE { |
| callback_.Run(entries_); |
| DeleteSoon(); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(UsageInfoEntries()); |
| DeleteSoon(); |
| } |
| |
| private: |
| void AddEntries(StorageType type, UsageTracker* tracker) { |
| std::map<std::string, int64> host_usage; |
| tracker->GetCachedHostsUsage(&host_usage); |
| for (std::map<std::string, int64>::const_iterator iter = host_usage.begin(); |
| iter != host_usage.end(); |
| ++iter) { |
| entries_.push_back(UsageInfo(iter->first, type, iter->second)); |
| } |
| if (--remaining_trackers_ == 0) |
| CallCompleted(); |
| } |
| |
| void DidGetGlobalUsage(StorageType type, int64, int64) { |
| AddEntries(type, manager()->GetUsageTracker(type)); |
| } |
| |
| QuotaManager* manager() const { |
| return static_cast<QuotaManager*>(observer()); |
| } |
| |
| GetUsageInfoCallback callback_; |
| UsageInfoEntries entries_; |
| base::WeakPtrFactory<GetUsageInfoTask> weak_factory_; |
| int remaining_trackers_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask); |
| }; |
| |
| class QuotaManager::UsageAndQuotaDispatcherTaskForTemporary |
| : public QuotaManager::UsageAndQuotaDispatcherTask { |
| public: |
| UsageAndQuotaDispatcherTaskForTemporary( |
| QuotaManager* manager, const HostAndType& host_and_type) |
| : UsageAndQuotaDispatcherTask(manager, host_and_type) {} |
| |
| protected: |
| virtual void RunBody() OVERRIDE { |
| manager()->temporary_usage_tracker_->GetGlobalUsage( |
| NewWaitableGlobalUsageCallback()); |
| manager()->temporary_usage_tracker_->GetHostUsage( |
| host(), NewWaitableHostUsageCallback()); |
| manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback()); |
| } |
| |
| virtual void DispatchCallbacks() OVERRIDE { |
| // Allow an individual host to utilize a fraction of the total |
| // pool available for temp storage. |
| int64 host_quota = temporary_global_quota() / kPerHostTemporaryPortion; |
| |
| // But if total temp usage is over-budget, stop letting new data in |
| // until we reclaim space. |
| DCHECK_GE(global_usage(), global_unlimited_usage()); |
| int64 limited_global_usage = global_usage() - global_unlimited_usage(); |
| if (limited_global_usage > temporary_global_quota()) |
| host_quota = std::min(host_quota, host_usage()); |
| |
| CallCallbacksAndClear(quota_status(), |
| host_usage(), host_usage(), host_quota, |
| available_space()); |
| } |
| }; |
| |
| class QuotaManager::UsageAndQuotaDispatcherTaskForPersistent |
| : public QuotaManager::UsageAndQuotaDispatcherTask { |
| public: |
| UsageAndQuotaDispatcherTaskForPersistent( |
| QuotaManager* manager, const HostAndType& host_and_type) |
| : UsageAndQuotaDispatcherTask(manager, host_and_type) {} |
| |
| protected: |
| virtual void RunBody() OVERRIDE { |
| manager()->persistent_usage_tracker_->GetHostUsage( |
| host(), NewWaitableHostUsageCallback()); |
| manager()->GetPersistentHostQuota( |
| host(), NewWaitableHostQuotaCallback()); |
| } |
| |
| virtual void DispatchCallbacks() OVERRIDE { |
| CallCallbacksAndClear(quota_status(), |
| host_usage(), host_usage(), host_quota(), |
| available_space()); |
| } |
| }; |
| |
| class QuotaManager::UsageAndQuotaDispatcherTaskForTemporaryGlobal |
| : public QuotaManager::UsageAndQuotaDispatcherTask { |
| public: |
| UsageAndQuotaDispatcherTaskForTemporaryGlobal( |
| QuotaManager* manager, const HostAndType& host_and_type) |
| : UsageAndQuotaDispatcherTask(manager, host_and_type) {} |
| |
| protected: |
| virtual void RunBody() OVERRIDE { |
| manager()->temporary_usage_tracker_->GetGlobalUsage( |
| NewWaitableGlobalUsageCallback()); |
| manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback()); |
| } |
| |
| virtual void DispatchCallbacks() OVERRIDE { |
| CallCallbacksAndClear(quota_status(), |
| global_usage(), global_unlimited_usage(), |
| temporary_global_quota(), |
| available_space()); |
| } |
| |
| virtual StorageType type() const { return kStorageTypeTemporary; } |
| }; |
| |
| // static |
| QuotaManager::UsageAndQuotaDispatcherTask* |
| QuotaManager::UsageAndQuotaDispatcherTask::Create( |
| QuotaManager* manager, bool global, |
| const QuotaManager::HostAndType& host_and_type) { |
| if (global) |
| return new UsageAndQuotaDispatcherTaskForTemporaryGlobal( |
| manager, host_and_type); |
| switch (host_and_type.second) { |
| case kStorageTypeTemporary: |
| return new UsageAndQuotaDispatcherTaskForTemporary( |
| manager, host_and_type); |
| case kStorageTypePersistent: |
| return new UsageAndQuotaDispatcherTaskForPersistent( |
| manager, host_and_type); |
| default: |
| NOTREACHED(); |
| } |
| return NULL; |
| } |
| |
| class QuotaManager::OriginDataDeleter : public QuotaTask { |
| public: |
| OriginDataDeleter(QuotaManager* manager, |
| const GURL& origin, |
| StorageType type, |
| int quota_client_mask, |
| const StatusCallback& callback) |
| : QuotaTask(manager), |
| origin_(origin), |
| type_(type), |
| quota_client_mask_(quota_client_mask), |
| error_count_(0), |
| remaining_clients_(-1), |
| skipped_clients_(0), |
| callback_(callback), |
| weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} |
| |
| protected: |
| virtual void Run() OVERRIDE { |
| error_count_ = 0; |
| remaining_clients_ = manager()->clients_.size(); |
| for (QuotaClientList::iterator iter = manager()->clients_.begin(); |
| iter != manager()->clients_.end(); ++iter) { |
| if (quota_client_mask_ & (*iter)->id()) { |
| (*iter)->DeleteOriginData( |
| origin_, type_, |
| base::Bind(&OriginDataDeleter::DidDeleteOriginData, |
| weak_factory_.GetWeakPtr())); |
| } else { |
| ++skipped_clients_; |
| if (--remaining_clients_ == 0) |
| CallCompleted(); |
| } |
| } |
| } |
| |
| virtual void Completed() OVERRIDE { |
| if (error_count_ == 0) { |
| // Only remove the entire origin if we didn't skip any client types. |
| if (skipped_clients_ == 0) |
| manager()->DeleteOriginFromDatabase(origin_, type_); |
| callback_.Run(kQuotaStatusOk); |
| } else { |
| callback_.Run(kQuotaErrorInvalidModification); |
| } |
| DeleteSoon(); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(kQuotaErrorAbort); |
| DeleteSoon(); |
| } |
| |
| void DidDeleteOriginData(QuotaStatusCode status) { |
| DCHECK_GT(remaining_clients_, 0); |
| |
| if (status != kQuotaStatusOk) |
| ++error_count_; |
| |
| if (--remaining_clients_ == 0) |
| CallCompleted(); |
| } |
| |
| QuotaManager* manager() const { |
| return static_cast<QuotaManager*>(observer()); |
| } |
| |
| GURL origin_; |
| StorageType type_; |
| int quota_client_mask_; |
| int error_count_; |
| int remaining_clients_; |
| int skipped_clients_; |
| StatusCallback callback_; |
| |
| base::WeakPtrFactory<OriginDataDeleter> weak_factory_; |
| DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter); |
| }; |
| |
| class QuotaManager::HostDataDeleter : public QuotaTask { |
| public: |
| HostDataDeleter(QuotaManager* manager, |
| const std::string& host, |
| StorageType type, |
| int quota_client_mask, |
| const StatusCallback& callback) |
| : QuotaTask(manager), |
| host_(host), |
| type_(type), |
| quota_client_mask_(quota_client_mask), |
| error_count_(0), |
| remaining_clients_(-1), |
| remaining_deleters_(-1), |
| callback_(callback), |
| weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} |
| |
| protected: |
| virtual void Run() OVERRIDE { |
| error_count_ = 0; |
| remaining_clients_ = manager()->clients_.size(); |
| for (QuotaClientList::iterator iter = manager()->clients_.begin(); |
| iter != manager()->clients_.end(); ++iter) { |
| (*iter)->GetOriginsForHost( |
| type_, host_, |
| base::Bind(&HostDataDeleter::DidGetOriginsForHost, |
| weak_factory_.GetWeakPtr())); |
| } |
| } |
| |
| virtual void Completed() OVERRIDE { |
| if (error_count_ == 0) { |
| callback_.Run(kQuotaStatusOk); |
| } else { |
| callback_.Run(kQuotaErrorInvalidModification); |
| } |
| DeleteSoon(); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(kQuotaErrorAbort); |
| DeleteSoon(); |
| } |
| |
| void DidGetOriginsForHost(const std::set<GURL>& origins, StorageType type) { |
| DCHECK_GT(remaining_clients_, 0); |
| |
| origins_.insert(origins.begin(), origins.end()); |
| |
| if (--remaining_clients_ == 0) { |
| if (!origins_.empty()) |
| ScheduleOriginsDeletion(); |
| else |
| CallCompleted(); |
| } |
| } |
| |
| void ScheduleOriginsDeletion() { |
| remaining_deleters_ = origins_.size(); |
| for (std::set<GURL>::const_iterator p = origins_.begin(); |
| p != origins_.end(); |
| ++p) { |
| OriginDataDeleter* deleter = |
| new OriginDataDeleter( |
| manager(), *p, type_, quota_client_mask_, |
| base::Bind(&HostDataDeleter::DidDeleteOriginData, |
| weak_factory_.GetWeakPtr())); |
| deleter->Start(); |
| } |
| } |
| |
| void DidDeleteOriginData(QuotaStatusCode status) { |
| DCHECK_GT(remaining_deleters_, 0); |
| |
| if (status != kQuotaStatusOk) |
| ++error_count_; |
| |
| if (--remaining_deleters_ == 0) |
| CallCompleted(); |
| } |
| |
| QuotaManager* manager() const { |
| return static_cast<QuotaManager*>(observer()); |
| } |
| |
| std::string host_; |
| StorageType type_; |
| int quota_client_mask_; |
| std::set<GURL> origins_; |
| int error_count_; |
| int remaining_clients_; |
| int remaining_deleters_; |
| StatusCallback callback_; |
| |
| base::WeakPtrFactory<HostDataDeleter> weak_factory_; |
| DISALLOW_COPY_AND_ASSIGN(HostDataDeleter); |
| }; |
| |
| class QuotaManager::DatabaseTaskBase : public QuotaThreadTask { |
| public: |
| explicit DatabaseTaskBase(QuotaManager* manager) |
| : QuotaThreadTask(manager, manager->db_thread_), |
| manager_(manager), |
| database_(manager->database_.get()), |
| db_disabled_(false) { |
| DCHECK(database_); |
| } |
| |
| protected: |
| virtual void DatabaseTaskCompleted() = 0; |
| |
| virtual void Completed() OVERRIDE { |
| manager_->db_disabled_ = db_disabled_; |
| DatabaseTaskCompleted(); |
| } |
| |
| bool db_disabled() const { return db_disabled_; } |
| void set_db_disabled(bool db_disabled) { |
| db_disabled_ = db_disabled; |
| } |
| |
| QuotaManager* manager() const { return manager_; } |
| QuotaDatabase* database() const { return database_; } |
| |
| private: |
| QuotaManager* manager_; |
| QuotaDatabase* database_; |
| bool db_disabled_; |
| }; |
| |
| class QuotaManager::InitializeTask : public QuotaManager::DatabaseTaskBase { |
| public: |
| InitializeTask(QuotaManager* manager) |
| : DatabaseTaskBase(manager), |
| temporary_quota_override_(-1), |
| desired_available_space_(-1) { |
| } |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| // See if we have overriding temporary quota configuration. |
| database()->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey, |
| &temporary_quota_override_); |
| database()->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey, |
| &desired_available_space_); |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| manager()->temporary_quota_override_ = temporary_quota_override_; |
| manager()->desired_available_space_ = desired_available_space_; |
| manager()->temporary_quota_initialized_ = true; |
| manager()->DidRunInitializeTask(); |
| } |
| |
| private: |
| int64 temporary_quota_override_; |
| int64 desired_available_space_; |
| }; |
| |
| class QuotaManager::UpdateTemporaryQuotaOverrideTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| UpdateTemporaryQuotaOverrideTask( |
| QuotaManager* manager, |
| int64 new_quota, |
| const QuotaCallback& callback) |
| : DatabaseTaskBase(manager), |
| new_quota_(new_quota), |
| callback_(callback) {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->SetQuotaConfigValue( |
| QuotaDatabase::kTemporaryQuotaOverrideKey, new_quota_)) { |
| set_db_disabled(true); |
| new_quota_ = -1; |
| return; |
| } |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| if (!db_disabled()) { |
| manager()->temporary_quota_override_ = new_quota_; |
| CallCallback(kQuotaStatusOk, kStorageTypeTemporary, new_quota_); |
| } else { |
| CallCallback(kQuotaErrorInvalidAccess, kStorageTypeTemporary, new_quota_); |
| } |
| } |
| |
| private: |
| void CallCallback(QuotaStatusCode status, StorageType type, int64 quota) { |
| if (!callback_.is_null()) { |
| callback_.Run(status, type, quota); |
| callback_.Reset(); |
| } |
| } |
| |
| int64 new_quota_; |
| QuotaCallback callback_; |
| }; |
| |
| class QuotaManager::GetPersistentHostQuotaTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| GetPersistentHostQuotaTask( |
| QuotaManager* manager, |
| const std::string& host, |
| const HostQuotaCallback& callback) |
| : DatabaseTaskBase(manager), |
| host_(host), |
| quota_(-1), |
| callback_(callback) { |
| } |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->GetHostQuota(host_, kStorageTypePersistent, "a_)) |
| quota_ = 0; |
| } |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(kQuotaStatusOk, |
| host_, kStorageTypePersistent, quota_); |
| } |
| private: |
| std::string host_; |
| int64 quota_; |
| HostQuotaCallback callback_; |
| }; |
| |
| class QuotaManager::UpdatePersistentHostQuotaTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| UpdatePersistentHostQuotaTask( |
| QuotaManager* manager, |
| const std::string& host, |
| int64 new_quota, |
| const HostQuotaCallback& callback) |
| : DatabaseTaskBase(manager), |
| host_(host), |
| new_quota_(new_quota), |
| callback_(callback) { |
| DCHECK_GE(new_quota_, 0); |
| } |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->SetHostQuota(host_, kStorageTypePersistent, new_quota_)) { |
| set_db_disabled(true); |
| new_quota_ = 0; |
| } |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(db_disabled() ? kQuotaErrorInvalidAccess : kQuotaStatusOk, |
| host_, kStorageTypePersistent, new_quota_); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Reset(); |
| } |
| |
| private: |
| std::string host_; |
| int64 new_quota_; |
| HostQuotaCallback callback_; |
| }; |
| |
| class QuotaManager::GetLRUOriginTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| GetLRUOriginTask( |
| QuotaManager* manager, |
| StorageType type, |
| const std::map<GURL, int>& origins_in_use, |
| const std::map<GURL, int>& origins_in_error, |
| const GetLRUOriginCallback& callback) |
| : DatabaseTaskBase(manager), |
| type_(type), |
| callback_(callback), |
| special_storage_policy_(manager->special_storage_policy_) { |
| for (std::map<GURL, int>::const_iterator p = origins_in_use.begin(); |
| p != origins_in_use.end(); |
| ++p) { |
| if (p->second > 0) |
| exceptions_.insert(p->first); |
| } |
| for (std::map<GURL, int>::const_iterator p = origins_in_error.begin(); |
| p != origins_in_error.end(); |
| ++p) { |
| if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted) |
| exceptions_.insert(p->first); |
| } |
| } |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| database()->GetLRUOrigin( |
| type_, exceptions_, special_storage_policy_, &url_); |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(url_); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Reset(); |
| } |
| |
| private: |
| StorageType type_; |
| std::set<GURL> exceptions_; |
| GetLRUOriginCallback callback_; |
| scoped_refptr<SpecialStoragePolicy> special_storage_policy_; |
| GURL url_; |
| }; |
| |
| class QuotaManager::DeleteOriginInfo |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| DeleteOriginInfo( |
| QuotaManager* manager, |
| const GURL& origin, |
| StorageType type) |
| : DatabaseTaskBase(manager), |
| origin_(origin), |
| type_(type) {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->DeleteOriginInfo(origin_, type_)) { |
| set_db_disabled(true); |
| } |
| } |
| virtual void DatabaseTaskCompleted() OVERRIDE {} |
| |
| private: |
| GURL origin_; |
| StorageType type_; |
| }; |
| |
| class QuotaManager::InitializeTemporaryOriginsInfoTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| InitializeTemporaryOriginsInfoTask( |
| QuotaManager* manager, |
| UsageTracker* temporary_usage_tracker) |
| : DatabaseTaskBase(manager), |
| has_registered_origins_(false) { |
| DCHECK(temporary_usage_tracker); |
| temporary_usage_tracker->GetCachedOrigins(&origins_); |
| } |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->IsOriginDatabaseBootstrapped()) { |
| // Register existing origins with 0 last time access. |
| if (!database()->RegisterInitialOriginInfo( |
| origins_, kStorageTypeTemporary)) { |
| set_db_disabled(true); |
| } else { |
| has_registered_origins_ = true; |
| database()->SetOriginDatabaseBootstrapped(true); |
| } |
| } |
| } |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| if (has_registered_origins_) |
| manager()->StartEviction(); |
| } |
| |
| private: |
| std::set<GURL> origins_; |
| bool has_registered_origins_; |
| }; |
| |
| class QuotaManager::AvailableSpaceQueryTask : public QuotaThreadTask { |
| public: |
| AvailableSpaceQueryTask( |
| QuotaManager* manager, |
| const AvailableSpaceCallback& callback) |
| : QuotaThreadTask(manager, manager->db_thread_), |
| profile_path_(manager->profile_path_), |
| space_(-1), |
| callback_(callback) { |
| } |
| virtual ~AvailableSpaceQueryTask() {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| space_ = base::SysInfo::AmountOfFreeDiskSpace(profile_path_); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Reset(); |
| } |
| |
| virtual void Completed() OVERRIDE { |
| callback_.Run(kQuotaStatusOk, space_); |
| } |
| |
| private: |
| FilePath profile_path_; |
| int64 space_; |
| AvailableSpaceCallback callback_; |
| }; |
| |
| class QuotaManager::UpdateAccessTimeTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| UpdateAccessTimeTask( |
| QuotaManager* manager, |
| const GURL& origin, |
| StorageType type, |
| base::Time accessed_time) |
| : DatabaseTaskBase(manager), |
| origin_(origin), |
| type_(type), |
| accessed_time_(accessed_time) {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->SetOriginLastAccessTime(origin_, type_, accessed_time_)) { |
| set_db_disabled(true); |
| } |
| } |
| virtual void DatabaseTaskCompleted() OVERRIDE {} |
| |
| private: |
| GURL origin_; |
| StorageType type_; |
| base::Time accessed_time_; |
| }; |
| |
| class QuotaManager::UpdateModifiedTimeTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| UpdateModifiedTimeTask( |
| QuotaManager* manager, |
| const GURL& origin, |
| StorageType type, |
| base::Time modified_time) |
| : DatabaseTaskBase(manager), |
| origin_(origin), |
| type_(type), |
| modified_time_(modified_time) {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->SetOriginLastModifiedTime( |
| origin_, type_, modified_time_)) { |
| set_db_disabled(true); |
| } |
| } |
| virtual void DatabaseTaskCompleted() OVERRIDE {} |
| |
| private: |
| GURL origin_; |
| StorageType type_; |
| base::Time modified_time_; |
| }; |
| |
| class QuotaManager::GetModifiedSinceTask |
| : public QuotaManager::DatabaseTaskBase { |
| public: |
| GetModifiedSinceTask( |
| QuotaManager* manager, |
| StorageType type, |
| base::Time modified_since, |
| GetOriginsCallback callback) |
| : DatabaseTaskBase(manager), |
| type_(type), |
| modified_since_(modified_since), |
| callback_(callback) {} |
| |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->GetOriginsModifiedSince( |
| type_, &origins_, modified_since_)) { |
| set_db_disabled(true); |
| } |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(origins_, type_); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(std::set<GURL>(), type_); |
| } |
| |
| private: |
| StorageType type_; |
| base::Time modified_since_; |
| std::set<GURL> origins_; |
| GetOriginsCallback callback_; |
| }; |
| |
| class QuotaManager::DumpQuotaTableTask |
| : public QuotaManager::DatabaseTaskBase { |
| private: |
| typedef QuotaManager::DumpQuotaTableTask self_type; |
| typedef QuotaManager::DumpQuotaTableCallback Callback; |
| typedef QuotaManager::QuotaTableEntry TableEntry; |
| typedef QuotaManager::QuotaTableEntries TableEntries; |
| typedef QuotaDatabase::QuotaTableCallback TableCallback; |
| |
| public: |
| DumpQuotaTableTask( |
| QuotaManager* manager, |
| const Callback& callback) |
| : DatabaseTaskBase(manager), |
| callback_(callback) { |
| } |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->DumpQuotaTable( |
| new TableCallback( |
| base::Bind(&self_type::AppendEntry, this)))) |
| set_db_disabled(true); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(TableEntries()); |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(entries_); |
| } |
| |
| private: |
| bool AppendEntry(const TableEntry& entry) { |
| entries_.push_back(entry); |
| return true; |
| } |
| |
| Callback callback_; |
| TableEntries entries_; |
| }; |
| |
| class QuotaManager::DumpOriginInfoTableTask |
| : public QuotaManager::DatabaseTaskBase { |
| private: |
| typedef QuotaManager::DumpOriginInfoTableTask self_type; |
| typedef QuotaManager::DumpOriginInfoTableCallback Callback; |
| typedef QuotaManager::OriginInfoTableEntry TableEntry; |
| typedef QuotaManager::OriginInfoTableEntries TableEntries; |
| typedef QuotaDatabase::OriginInfoTableCallback TableCallback; |
| |
| public: |
| DumpOriginInfoTableTask( |
| QuotaManager* manager, |
| const Callback& callback) |
| : DatabaseTaskBase(manager), |
| callback_(callback) { |
| } |
| protected: |
| virtual void RunOnTargetThread() OVERRIDE { |
| if (!database()->DumpOriginInfoTable( |
| new TableCallback( |
| base::Bind(&self_type::AppendEntry, this)))) |
| set_db_disabled(true); |
| } |
| |
| virtual void Aborted() OVERRIDE { |
| callback_.Run(TableEntries()); |
| } |
| |
| virtual void DatabaseTaskCompleted() OVERRIDE { |
| callback_.Run(entries_); |
| } |
| |
| private: |
| bool AppendEntry(const TableEntry& entry) { |
| entries_.push_back(entry); |
| return true; |
| } |
| |
| Callback callback_; |
| TableEntries entries_; |
| }; |
| |
| // QuotaManager --------------------------------------------------------------- |
| |
| QuotaManager::QuotaManager(bool is_incognito, |
| const FilePath& profile_path, |
| base::MessageLoopProxy* io_thread, |
| base::MessageLoopProxy* db_thread, |
| SpecialStoragePolicy* special_storage_policy) |
| : is_incognito_(is_incognito), |
| profile_path_(profile_path), |
| proxy_(new QuotaManagerProxy( |
| ALLOW_THIS_IN_INITIALIZER_LIST(this), io_thread)), |
| db_disabled_(false), |
| eviction_disabled_(false), |
| io_thread_(io_thread), |
| db_thread_(db_thread), |
| temporary_quota_initialized_(false), |
| temporary_quota_override_(-1), |
| desired_available_space_(-1), |
| special_storage_policy_(special_storage_policy), |
| weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| } |
| |
| QuotaManager::~QuotaManager() { |
| proxy_->manager_ = NULL; |
| std::for_each(clients_.begin(), clients_.end(), |
| std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed)); |
| if (database_.get()) |
| db_thread_->DeleteSoon(FROM_HERE, database_.release()); |
| } |
| |
| void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) { |
| LazyInitialize(); |
| GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback); |
| get_usage_info->Start(); |
| } |
| |
| void QuotaManager::GetUsageAndQuota( |
| const GURL& origin, StorageType type, |
| const GetUsageAndQuotaCallback& callback) { |
| GetUsageAndQuotaInternal(origin, type, false /* global */, |
| base::Bind(&CallGetUsageAndQuotaCallback, |
| callback, IsStorageUnlimited(origin))); |
| } |
| |
| void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) { |
| if (is_incognito_) { |
| callback.Run(kQuotaStatusOk, kIncognitoDefaultTemporaryQuota); |
| return; |
| } |
| make_scoped_refptr(new AvailableSpaceQueryTask(this, callback))->Start(); |
| } |
| |
| void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) { |
| if (temporary_quota_override_ > 0) { |
| callback.Run(kQuotaStatusOk, kStorageTypeTemporary, |
| temporary_quota_override_); |
| return; |
| } |
| GetUsageAndQuotaInternal( |
| GURL(), kStorageTypeTemporary, true /* global */, |
| base::Bind(&CallQuotaCallback, callback, kStorageTypeTemporary)); |
| } |
| |
| void QuotaManager::SetTemporaryGlobalOverrideQuota( |
| int64 new_quota, const QuotaCallback& callback) { |
| LazyInitialize(); |
| |
| if (new_quota < 0) { |
| if (!callback.is_null()) |
| callback.Run(kQuotaErrorInvalidModification, |
| kStorageTypeTemporary, -1); |
| return; |
| } |
| |
| if (db_disabled_) { |
| if (callback.is_null()) |
| callback.Run(kQuotaErrorInvalidAccess, |
| kStorageTypeTemporary, -1); |
| return; |
| } |
| |
| make_scoped_refptr(new UpdateTemporaryQuotaOverrideTask( |
| this, new_quota, callback))->Start(); |
| } |
| |
| void QuotaManager::GetPersistentHostQuota(const std::string& host, |
| const HostQuotaCallback& callback) { |
| LazyInitialize(); |
| if (host.empty()) { |
| // This could happen if we are called on file:///. |
| // TODO(kinuko) We may want to respect --allow-file-access-from-files |
| // command line switch. |
| callback.Run(kQuotaStatusOk, host, kStorageTypePersistent, 0); |
| return; |
| } |
| scoped_refptr<GetPersistentHostQuotaTask> task( |
| new GetPersistentHostQuotaTask(this, host, callback)); |
| task->Start(); |
| } |
| |
| void QuotaManager::SetPersistentHostQuota(const std::string& host, |
| int64 new_quota, |
| const HostQuotaCallback& callback) { |
| LazyInitialize(); |
| if (host.empty()) { |
| // This could happen if we are called on file:///. |
| callback.Run(kQuotaErrorNotSupported, host, kStorageTypePersistent, 0); |
| return; |
| } |
| if (new_quota < 0) { |
| callback.Run(kQuotaErrorInvalidModification, |
| host, kStorageTypePersistent, -1); |
| return; |
| } |
| |
| if (!db_disabled_) { |
| scoped_refptr<UpdatePersistentHostQuotaTask> task( |
| new UpdatePersistentHostQuotaTask( |
| this, host, new_quota, callback)); |
| task->Start(); |
| } else { |
| callback.Run(kQuotaErrorInvalidAccess, |
| host, kStorageTypePersistent, -1); |
| } |
| } |
| |
| void QuotaManager::GetGlobalUsage(StorageType type, |
| const GlobalUsageCallback& callback) { |
| LazyInitialize(); |
| GetUsageTracker(type)->GetGlobalUsage(callback); |
| } |
| |
| void QuotaManager::GetHostUsage(const std::string& host, |
| StorageType type, |
| const HostUsageCallback& callback) { |
| LazyInitialize(); |
| GetUsageTracker(type)->GetHostUsage(host, callback); |
| } |
| |
| void QuotaManager::GetStatistics( |
| std::map<std::string, std::string>* statistics) { |
| DCHECK(statistics); |
| if (temporary_storage_evictor_.get()) { |
| std::map<std::string, int64> stats; |
| temporary_storage_evictor_->GetStatistics(&stats); |
| for (std::map<std::string, int64>::iterator p = stats.begin(); |
| p != stats.end(); |
| ++p) |
| (*statistics)[p->first] = base::Int64ToString(p->second); |
| } |
| } |
| |
| void QuotaManager::GetOriginsModifiedSince(StorageType type, |
| base::Time modified_since, |
| const GetOriginsCallback& callback) { |
| LazyInitialize(); |
| make_scoped_refptr(new GetModifiedSinceTask( |
| this, type, modified_since, callback))->Start(); |
| } |
| |
| void QuotaManager::LazyInitialize() { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| if (database_.get()) { |
| // Initialization seems to be done already. |
| return; |
| } |
| |
| // Use an empty path to open an in-memory only databse for incognito. |
| database_.reset(new QuotaDatabase(is_incognito_ ? FilePath() : |
| profile_path_.AppendASCII(kDatabaseName))); |
| |
| temporary_usage_tracker_.reset( |
| new UsageTracker(clients_, kStorageTypeTemporary, |
| special_storage_policy_)); |
| persistent_usage_tracker_.reset( |
| new UsageTracker(clients_, kStorageTypePersistent, |
| special_storage_policy_)); |
| |
| make_scoped_refptr(new InitializeTask(this))->Start(); |
| } |
| |
| void QuotaManager::RegisterClient(QuotaClient* client) { |
| DCHECK(!database_.get()); |
| clients_.push_back(client); |
| } |
| |
| void QuotaManager::NotifyStorageAccessed( |
| QuotaClient::ID client_id, |
| const GURL& origin, StorageType type) { |
| NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now()); |
| } |
| |
| void QuotaManager::NotifyStorageModified( |
| QuotaClient::ID client_id, |
| const GURL& origin, StorageType type, int64 delta) { |
| NotifyStorageModifiedInternal(client_id, origin, type, delta, |
| base::Time::Now()); |
| } |
| |
| void QuotaManager::NotifyOriginInUse(const GURL& origin) { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| origins_in_use_[origin]++; |
| } |
| |
| void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| DCHECK(IsOriginInUse(origin)); |
| int& count = origins_in_use_[origin]; |
| if (--count == 0) |
| origins_in_use_.erase(origin); |
| } |
| |
| void QuotaManager::DeleteOriginData( |
| const GURL& origin, StorageType type, int quota_client_mask, |
| const StatusCallback& callback) { |
| LazyInitialize(); |
| |
| if (origin.is_empty() || clients_.empty()) { |
| callback.Run(kQuotaStatusOk); |
| return; |
| } |
| |
| OriginDataDeleter* deleter = |
| new OriginDataDeleter(this, origin, type, quota_client_mask, callback); |
| deleter->Start(); |
| } |
| |
| void QuotaManager::DeleteHostData(const std::string& host, |
| StorageType type, |
| int quota_client_mask, |
| const StatusCallback& callback) { |
| |
| LazyInitialize(); |
| |
| if (host.empty() || clients_.empty()) { |
| callback.Run(kQuotaStatusOk); |
| return; |
| } |
| |
| HostDataDeleter* deleter = |
| new HostDataDeleter(this, host, type, quota_client_mask, callback); |
| deleter->Start(); |
| } |
| |
| bool QuotaManager::ResetUsageTracker(StorageType type) { |
| switch (type) { |
| case kStorageTypeTemporary: |
| if (temporary_usage_tracker_->IsWorking()) |
| return false; |
| temporary_usage_tracker_.reset( |
| new UsageTracker(clients_, kStorageTypeTemporary, |
| special_storage_policy_)); |
| return true; |
| case kStorageTypePersistent: |
| if (persistent_usage_tracker_->IsWorking()) |
| return false; |
| persistent_usage_tracker_.reset( |
| new UsageTracker(clients_, kStorageTypePersistent, |
| special_storage_policy_)); |
| return true; |
| default: |
| NOTREACHED(); |
| } |
| return true; |
| } |
| |
| UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const { |
| switch (type) { |
| case kStorageTypeTemporary: |
| return temporary_usage_tracker_.get(); |
| case kStorageTypePersistent: |
| return persistent_usage_tracker_.get(); |
| default: |
| NOTREACHED(); |
| } |
| return NULL; |
| } |
| |
| void QuotaManager::GetCachedOrigins( |
| StorageType type, std::set<GURL>* origins) { |
| DCHECK(origins); |
| LazyInitialize(); |
| switch (type) { |
| case kStorageTypeTemporary: |
| DCHECK(temporary_usage_tracker_.get()); |
| temporary_usage_tracker_->GetCachedOrigins(origins); |
| return; |
| case kStorageTypePersistent: |
| DCHECK(persistent_usage_tracker_.get()); |
| persistent_usage_tracker_->GetCachedOrigins(origins); |
| return; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void QuotaManager::NotifyStorageAccessedInternal( |
| QuotaClient::ID client_id, |
| const GURL& origin, StorageType type, |
| base::Time accessed_time) { |
| LazyInitialize(); |
| if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) { |
| // Record the accessed origins while GetLRUOrigin task is runing |
| // to filter out them from eviction. |
| access_notified_origins_.insert(origin); |
| } |
| |
| if (db_disabled_) |
| return; |
| make_scoped_refptr(new UpdateAccessTimeTask( |
| this, origin, type, accessed_time))->Start(); |
| } |
| |
| void QuotaManager::NotifyStorageModifiedInternal( |
| QuotaClient::ID client_id, |
| const GURL& origin, |
| StorageType type, |
| int64 delta, |
| base::Time modified_time) { |
| LazyInitialize(); |
| GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta); |
| make_scoped_refptr(new UpdateModifiedTimeTask( |
| this, origin, type, modified_time))->Start(); |
| } |
| |
| void QuotaManager::GetUsageAndQuotaInternal( |
| const GURL& origin, StorageType type, bool global, |
| const UsageAndQuotaDispatcherCallback& callback) { |
| LazyInitialize(); |
| |
| StorageType requested_type = type; |
| if (type == kStorageTypeUnknown) { |
| // Quota only supports temporary/persistent types. |
| callback.Run(kQuotaErrorNotSupported, QuotaAndUsage()); |
| return; |
| } |
| |
| // Special internal type for querying global usage and quota. |
| const int kStorageTypeTemporaryGlobal = kStorageTypeTemporary + 100; |
| if (global) { |
| DCHECK_EQ(kStorageTypeTemporary, type); |
| type = static_cast<StorageType>(kStorageTypeTemporaryGlobal); |
| } |
| |
| std::string host = net::GetHostOrSpecFromURL(origin); |
| HostAndType host_and_type = std::make_pair(host, type); |
| UsageAndQuotaDispatcherTaskMap::iterator found = |
| usage_and_quota_dispatchers_.find(host_and_type); |
| if (found == usage_and_quota_dispatchers_.end()) { |
| UsageAndQuotaDispatcherTask* dispatcher = |
| UsageAndQuotaDispatcherTask::Create(this, global, host_and_type); |
| found = usage_and_quota_dispatchers_.insert( |
| std::make_pair(host_and_type, dispatcher)).first; |
| } |
| // Start the dispatcher if it is the first one and temporary_quota_override |
| // is already initialized iff the requested type is temporary. |
| // (The first dispatcher task for temporary will be kicked in |
| // DidRunInitializeTask if temporary_quota_initialized_ is false here.) |
| if (found->second->AddCallback(callback) && |
| (requested_type != kStorageTypeTemporary || |
| temporary_quota_initialized_)) { |
| found->second->Start(); |
| } |
| } |
| |
| void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) { |
| make_scoped_refptr(new DumpQuotaTableTask(this, callback))->Start(); |
| } |
| |
| void QuotaManager::DumpOriginInfoTable( |
| const DumpOriginInfoTableCallback& callback) { |
| make_scoped_refptr(new DumpOriginInfoTableTask(this, callback))->Start(); |
| } |
| |
| |
| void QuotaManager::DeleteOriginFromDatabase( |
| const GURL& origin, StorageType type) { |
| LazyInitialize(); |
| if (db_disabled_) |
| return; |
| scoped_refptr<DeleteOriginInfo> task = |
| new DeleteOriginInfo(this, origin, type); |
| task->Start(); |
| } |
| |
| void QuotaManager::GetLRUOrigin( |
| StorageType type, |
| const GetLRUOriginCallback& callback) { |
| LazyInitialize(); |
| // This must not be called while there's an in-flight task. |
| DCHECK(lru_origin_callback_.is_null()); |
| lru_origin_callback_ = callback; |
| if (db_disabled_) { |
| lru_origin_callback_.Run(GURL()); |
| lru_origin_callback_.Reset(); |
| return; |
| } |
| scoped_refptr<GetLRUOriginTask> task(new GetLRUOriginTask( |
| this, type, origins_in_use_, origins_in_error_, |
| base::Bind(&QuotaManager::DidGetDatabaseLRUOrigin, |
| weak_factory_.GetWeakPtr()))); |
| task->Start(); |
| } |
| |
| void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| |
| // We only try evict origins that are not in use, so basically |
| // deletion attempt for eviction should not fail. Let's record |
| // the origin if we get error and exclude it from future eviction |
| // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted). |
| if (status != kQuotaStatusOk) |
| origins_in_error_[eviction_context_.evicted_origin]++; |
| |
| eviction_context_.evict_origin_data_callback.Run(status); |
| eviction_context_.evict_origin_data_callback.Reset(); |
| } |
| |
| void QuotaManager::EvictOriginData( |
| const GURL& origin, |
| StorageType type, |
| const EvictOriginDataCallback& callback) { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| DCHECK_EQ(type, kStorageTypeTemporary); |
| |
| eviction_context_.evicted_origin = origin; |
| eviction_context_.evicted_type = type; |
| eviction_context_.evict_origin_data_callback = callback; |
| |
| DeleteOriginData(origin, type, QuotaClient::kAllClientsMask, |
| base::Bind(&QuotaManager::DidOriginDataEvicted, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void QuotaManager::GetUsageAndQuotaForEviction( |
| const GetUsageAndQuotaForEvictionCallback& callback) { |
| DCHECK(io_thread_->BelongsToCurrentThread()); |
| GetUsageAndQuotaInternal( |
| GURL(), kStorageTypeTemporary, true /* global */, callback); |
| } |
| |
| void QuotaManager::StartEviction() { |
| DCHECK(!temporary_storage_evictor_.get()); |
| temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor( |
| this, kEvictionIntervalInMilliSeconds)); |
| if (desired_available_space_ >= 0) |
| temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction( |
| desired_available_space_); |
| temporary_storage_evictor_->Start(); |
| } |
| |
| void QuotaManager::ReportHistogram() { |
| GetGlobalUsage(kStorageTypeTemporary, |
| base::Bind( |
| &QuotaManager::DidGetTemporaryGlobalUsageForHistogram, |
| weak_factory_.GetWeakPtr())); |
| GetGlobalUsage(kStorageTypePersistent, |
| base::Bind( |
| &QuotaManager::DidGetPersistentGlobalUsageForHistogram, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void QuotaManager::DidGetTemporaryGlobalUsageForHistogram( |
| StorageType type, |
| int64 usage, |
| int64 unlimited_usage) { |
| UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage); |
| |
| std::set<GURL> origins; |
| GetCachedOrigins(type, &origins); |
| |
| size_t num_origins = origins.size(); |
| size_t protected_origins = 0; |
| size_t unlimited_origins = 0; |
| CountOriginType(origins, special_storage_policy_, |
| &protected_origins, &unlimited_origins); |
| |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins", |
| num_origins); |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins", |
| protected_origins); |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins", |
| unlimited_origins); |
| } |
| |
| void QuotaManager::DidGetPersistentGlobalUsageForHistogram( |
| StorageType type, |
| int64 usage, |
| int64 unlimited_usage) { |
| UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage); |
| |
| std::set<GURL> origins; |
| GetCachedOrigins(type, &origins); |
| |
| size_t num_origins = origins.size(); |
| size_t protected_origins = 0; |
| size_t unlimited_origins = 0; |
| CountOriginType(origins, special_storage_policy_, |
| &protected_origins, &unlimited_origins); |
| |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins", |
| num_origins); |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins", |
| protected_origins); |
| UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins", |
| unlimited_origins); |
| } |
| |
| void QuotaManager::DidRunInitializeTask() { |
| histogram_timer_.Start(FROM_HERE, |
| base::TimeDelta::FromMilliseconds( |
| kReportHistogramInterval), |
| this, &QuotaManager::ReportHistogram); |
| |
| DCHECK(temporary_quota_initialized_); |
| |
| // Kick the tasks that have been waiting for the |
| // temporary_quota_initialized_ to be initialized (if there're any). |
| for (UsageAndQuotaDispatcherTaskMap::iterator iter = |
| usage_and_quota_dispatchers_.begin(); |
| iter != usage_and_quota_dispatchers_.end(); ++iter) { |
| if (iter->second->IsStartable()) |
| iter->second->Start(); |
| } |
| |
| // Kick the first GetTemporaryGlobalQuota. This internally fetches (and |
| // caches) the usage of all origins and checks the available disk space. |
| GetTemporaryGlobalQuota( |
| base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void QuotaManager::DidGetInitialTemporaryGlobalQuota( |
| QuotaStatusCode status, StorageType type, int64 quota_unused) { |
| DCHECK_EQ(type, kStorageTypeTemporary); |
| |
| if (eviction_disabled_) |
| return; |
| |
| // This will call the StartEviction() when initial origin registration |
| // is completed. |
| make_scoped_refptr(new InitializeTemporaryOriginsInfoTask( |
| this, temporary_usage_tracker_.get()))->Start(); |
| } |
| |
| void QuotaManager::DidGetDatabaseLRUOrigin(const GURL& origin) { |
| // Make sure the returned origin is (still) not in the origin_in_use_ set |
| // and has not been accessed since we posted the task. |
| if (origins_in_use_.find(origin) != origins_in_use_.end() || |
| access_notified_origins_.find(origin) != access_notified_origins_.end()) |
| lru_origin_callback_.Run(GURL()); |
| else |
| lru_origin_callback_.Run(origin); |
| access_notified_origins_.clear(); |
| lru_origin_callback_.Reset(); |
| } |
| |
| void QuotaManager::DeleteOnCorrectThread() const { |
| if (!io_thread_->BelongsToCurrentThread() && |
| io_thread_->DeleteSoon(FROM_HERE, this)) { |
| return; |
| } |
| delete this; |
| } |
| |
| // QuotaManagerProxy ---------------------------------------------------------- |
| |
| void QuotaManagerProxy::RegisterClient(QuotaClient* client) { |
| if (!io_thread_->BelongsToCurrentThread() && |
| io_thread_->PostTask( |
| FROM_HERE, |
| base::Bind(&QuotaManagerProxy::RegisterClient, this, client))) { |
| return; |
| } |
| |
| if (manager_) |
| manager_->RegisterClient(client); |
| else |
| client->OnQuotaManagerDestroyed(); |
| } |
| |
| void QuotaManagerProxy::NotifyStorageAccessed( |
| QuotaClient::ID client_id, |
| const GURL& origin, |
| StorageType type) { |
| if (!io_thread_->BelongsToCurrentThread()) { |
| io_thread_->PostTask( |
| FROM_HERE, |
| base::Bind(&QuotaManagerProxy::NotifyStorageAccessed, this, client_id, |
| origin, type)); |
| return; |
| } |
| |
| if (manager_) |
| manager_->NotifyStorageAccessed(client_id, origin, type); |
| } |
| |
| void QuotaManagerProxy::NotifyStorageModified( |
| QuotaClient::ID client_id, |
| const GURL& origin, |
| StorageType type, |
| int64 delta) { |
| if (!io_thread_->BelongsToCurrentThread()) { |
| io_thread_->PostTask( |
| FROM_HERE, |
| base::Bind(&QuotaManagerProxy::NotifyStorageModified, this, client_id, |
| origin, type, delta)); |
| return; |
| } |
| |
| if (manager_) |
| manager_->NotifyStorageModified(client_id, origin, type, delta); |
| } |
| |
| void QuotaManagerProxy::NotifyOriginInUse( |
| const GURL& origin) { |
| if (!io_thread_->BelongsToCurrentThread()) { |
| io_thread_->PostTask( |
| FROM_HERE, |
| base::Bind(&QuotaManagerProxy::NotifyOriginInUse, this, origin)); |
| return; |
| } |
| |
| if (manager_) |
| manager_->NotifyOriginInUse(origin); |
| } |
| |
| void QuotaManagerProxy::NotifyOriginNoLongerInUse( |
| const GURL& origin) { |
| if (!io_thread_->BelongsToCurrentThread()) { |
| io_thread_->PostTask( |
| FROM_HERE, |
| base::Bind(&QuotaManagerProxy::NotifyOriginNoLongerInUse, this, |
| origin)); |
| return; |
| } |
| if (manager_) |
| manager_->NotifyOriginNoLongerInUse(origin); |
| } |
| |
| QuotaManager* QuotaManagerProxy::quota_manager() const { |
| DCHECK(!io_thread_ || io_thread_->BelongsToCurrentThread()); |
| return manager_; |
| } |
| |
| QuotaManagerProxy::QuotaManagerProxy( |
| QuotaManager* manager, base::MessageLoopProxy* io_thread) |
| : manager_(manager), io_thread_(io_thread) { |
| } |
| |
| QuotaManagerProxy::~QuotaManagerProxy() { |
| } |
| |
| } // namespace quota |