blob: 640de20866416e321c5f0de560f10fa1bfd8c48c [file] [log] [blame]
// 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, &quota_))
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