diff options
-rw-r--r-- | content/browser/file_system/file_system_browsertest.cc | 15 | ||||
-rw-r--r-- | content/browser/in_process_webkit/indexed_db_browsertest.cc | 14 | ||||
-rw-r--r-- | webkit/quota/quota_database.cc | 28 | ||||
-rw-r--r-- | webkit/quota/quota_database.h | 8 | ||||
-rw-r--r-- | webkit/quota/quota_database_unittest.cc | 44 | ||||
-rw-r--r-- | webkit/quota/quota_manager.cc | 623 | ||||
-rw-r--r-- | webkit/quota/quota_manager.h | 91 | ||||
-rw-r--r-- | webkit/quota/quota_manager_unittest.cc | 55 | ||||
-rw-r--r-- | webkit/quota/quota_temporary_storage_evictor.cc | 36 | ||||
-rw-r--r-- | webkit/quota/quota_temporary_storage_evictor.h | 11 | ||||
-rw-r--r-- | webkit/quota/quota_temporary_storage_evictor_unittest.cc | 12 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 6 |
12 files changed, 462 insertions, 481 deletions
diff --git a/content/browser/file_system/file_system_browsertest.cc b/content/browser/file_system/file_system_browsertest.cc index 52a28b4..06715014 100644 --- a/content/browser/file_system/file_system_browsertest.cc +++ b/content/browser/file_system/file_system_browsertest.cc @@ -63,19 +63,6 @@ class FileSystemBrowserTestWithLowQuota : public FileSystemBrowserTest { kTemporaryStorageQuotaMaxSize, browser()->profile()->GetQuotaManager()); } - class SetTempQuotaCallback : public quota::QuotaCallback { - public: - void Run(quota::QuotaStatusCode, quota::StorageType, int64) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - } - - void RunWithParams(const Tuple3<quota::QuotaStatusCode, - quota::StorageType, - int64>& params) { - Run(params.a, params.b, params.c); - } - }; - static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, @@ -84,7 +71,7 @@ class FileSystemBrowserTestWithLowQuota : public FileSystemBrowserTest { return; } DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - qm->SetTemporaryGlobalQuota(bytes, new SetTempQuotaCallback); + qm->SetTemporaryGlobalOverrideQuota(bytes, NULL); // Don't return until the quota has been set. scoped_refptr<base::ThreadTestHelper> helper( new base::ThreadTestHelper( diff --git a/content/browser/in_process_webkit/indexed_db_browsertest.cc b/content/browser/in_process_webkit/indexed_db_browsertest.cc index 636973a..62b0a28 100644 --- a/content/browser/in_process_webkit/indexed_db_browsertest.cc +++ b/content/browser/in_process_webkit/indexed_db_browsertest.cc @@ -225,18 +225,6 @@ class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest { kTemporaryStorageQuotaMaxSize, browser()->profile()->GetQuotaManager()); } - class SetTempQuotaCallback : public quota::QuotaCallback { - public: - void Run(quota::QuotaStatusCode, quota::StorageType, int64) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - } - - void RunWithParams(const Tuple3<quota::QuotaStatusCode, - quota::StorageType, int64>& params) { - Run(params.a, params.b, params.c); - } - }; - static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, @@ -245,7 +233,7 @@ class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest { return; } DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - qm->SetTemporaryGlobalQuota(bytes, new SetTempQuotaCallback); + qm->SetTemporaryGlobalOverrideQuota(bytes, NULL); // Don't return until the quota has been set. scoped_refptr<base::ThreadTestHelper> helper( new base::ThreadTestHelper( diff --git a/webkit/quota/quota_database.cc b/webkit/quota/quota_database.cc index d34451e..0563535 100644 --- a/webkit/quota/quota_database.cc +++ b/webkit/quota/quota_database.cc @@ -23,12 +23,11 @@ namespace { // Definitions for database schema. -const int kCurrentVersion = 3; +const int kCurrentVersion = 4; const int kCompatibleVersion = 2; const char kHostQuotaTable[] = "HostQuotaTable"; const char kOriginInfoTable[] = "OriginInfoTable"; -const char kGlobalQuotaKeyPrefix[] = "GlobalQuota-"; const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped"; class HistogramUniquifier { @@ -48,13 +47,10 @@ bool PrepareCachedStatement( return true; } -std::string GetGlobalQuotaKey(quota::StorageType type) { - if (type == quota::kStorageTypeTemporary) - return std::string(kGlobalQuotaKeyPrefix) + "temporary"; - else if (type == quota::kStorageTypePersistent) - return std::string(kGlobalQuotaKeyPrefix) + "persistent"; - NOTREACHED() << "Unknown storage type " << type; - return std::string(); +bool VerifyValidQuotaConfig(const char* key) { + return (key != NULL && + (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) || + !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey))); } const int kCommitIntervalMs = 30000; @@ -62,6 +58,10 @@ const int kCommitIntervalMs = 30000; } // anonymous namespace // static +const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace"; +const char QuotaDatabase::kTemporaryQuotaOverrideKey[] = + "TemporaryQuotaOverride"; + const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = { { kHostQuotaTable, "(host TEXT NOT NULL," @@ -334,16 +334,18 @@ bool QuotaDatabase::DeleteOriginInfo( return true; } -bool QuotaDatabase::GetGlobalQuota(StorageType type, int64* quota) { +bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) { if (!LazyOpen(false)) return false; - return meta_table_->GetValue(GetGlobalQuotaKey(type).c_str(), quota); + DCHECK(VerifyValidQuotaConfig(key)); + return meta_table_->GetValue(key, value); } -bool QuotaDatabase::SetGlobalQuota(StorageType type, int64 quota) { +bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) { if (!LazyOpen(true)) return false; - return meta_table_->SetValue(GetGlobalQuotaKey(type).c_str(), quota); + DCHECK(VerifyValidQuotaConfig(key)); + return meta_table_->SetValue(key, value); } bool QuotaDatabase::GetLRUOrigin( diff --git a/webkit/quota/quota_database.h b/webkit/quota/quota_database.h index ec437f1..7a816f6 100644 --- a/webkit/quota/quota_database.h +++ b/webkit/quota/quota_database.h @@ -33,6 +33,10 @@ class SpecialStoragePolicy; // All the methods of this class must run on the DB thread. class QuotaDatabase { public: + // Constants for {Get,Set}QuotaConfigValue keys. + static const char kDesiredAvailableSpaceKey[]; + static const char kTemporaryQuotaOverrideKey[]; + // If 'path' is empty, an in memory database will be used. explicit QuotaDatabase(const FilePath& path); ~QuotaDatabase(); @@ -59,8 +63,8 @@ class QuotaDatabase { bool DeleteOriginInfo(const GURL& origin, StorageType type); - bool GetGlobalQuota(StorageType type, int64* quota); - bool SetGlobalQuota(StorageType type, int64 quota); + bool GetQuotaConfigValue(const char* key, int64* value); + bool SetQuotaConfigValue(const char* key, int64 value); // Sets |origin| to the least recently used origin of origins not included // in |exceptions| and not granted the special unlimited storage right. diff --git a/webkit/quota/quota_database_unittest.cc b/webkit/quota/quota_database_unittest.cc index 05001a7..7045f7f 100644 --- a/webkit/quota/quota_database_unittest.cc +++ b/webkit/quota/quota_database_unittest.cc @@ -106,26 +106,30 @@ class QuotaDatabaseTest : public testing::Test { QuotaDatabase db(kDbFile); ASSERT_TRUE(db.LazyOpen(true)); - const int kQuota1 = 9999; - const int kQuota2 = 86420; - - int64 quota = -1; - EXPECT_FALSE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); - EXPECT_FALSE(db.GetGlobalQuota(kStorageTypePersistent, "a)); - - EXPECT_TRUE(db.SetGlobalQuota(kStorageTypeTemporary, kQuota1)); - EXPECT_TRUE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); - EXPECT_EQ(kQuota1, quota); - - EXPECT_TRUE(db.SetGlobalQuota(kStorageTypeTemporary, kQuota1 + 1024)); - EXPECT_TRUE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); - EXPECT_EQ(kQuota1 + 1024, quota); - - EXPECT_FALSE(db.GetGlobalQuota(kStorageTypePersistent, "a)); - - EXPECT_TRUE(db.SetGlobalQuota(kStorageTypePersistent, kQuota2)); - EXPECT_TRUE(db.GetGlobalQuota(kStorageTypePersistent, "a)); - EXPECT_EQ(kQuota2, quota); + const char* kTempQuotaKey = QuotaDatabase::kTemporaryQuotaOverrideKey; + const char* kAvailSpaceKey = QuotaDatabase::kDesiredAvailableSpaceKey; + + int64 value = 0; + const int64 kValue1 = 456; + const int64 kValue2 = 123000; + EXPECT_FALSE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); + EXPECT_FALSE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); + + EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue1)); + EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); + EXPECT_EQ(kValue1, value); + + EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue2)); + EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value)); + EXPECT_EQ(kValue2, value); + + EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue1)); + EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); + EXPECT_EQ(kValue1, value); + + EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue2)); + EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value)); + EXPECT_EQ(kValue2, value); } void OriginLastAccessTimeLRU(const FilePath& kDbFile) { diff --git a/webkit/quota/quota_manager.cc b/webkit/quota/quota_manager.cc index 4ade94f..ad71c02 100644 --- a/webkit/quota/quota_manager.cc +++ b/webkit/quota/quota_manager.cc @@ -36,38 +36,12 @@ namespace quota { namespace { -const char kEnableQuotaEviction[] = "enable-quota-eviction"; const int64 kMBytes = 1024 * 1024; const int kMinutesInMilliSeconds = 60 * 1000; -// Returns the initial size of the temporary storage quota. -// (This just gives a default initial size; once its initial size is determined -// it won't automatically be adjusted.) -int64 GetInitialTemporaryStorageQuotaSize(const FilePath& path, - bool is_incognito) { - int64 free_space = base::SysInfo::AmountOfFreeDiskSpace(path); - UMA_HISTOGRAM_MBYTES("Quota.FreeDiskSpaceForProfile", free_space); - - // Returns 0 (disables the temporary storage) if the available space is - // less than the twice of the default quota size. - if (free_space < QuotaManager::kTemporaryStorageQuotaDefaultSize * 2) - return 0; - - if (is_incognito) - return QuotaManager::kIncognitoDefaultTemporaryQuota; - - // Returns the default quota size while it is more than 5% of the - // available space. - if (free_space < QuotaManager::kTemporaryStorageQuotaDefaultSize * 20) - return QuotaManager::kTemporaryStorageQuotaDefaultSize; - - // Returns the 5% of the available space while it does not exceed the - // maximum quota size (1GB). - if (free_space < QuotaManager::kTemporaryStorageQuotaMaxSize * 20) - return free_space / 20; - - return QuotaManager::kTemporaryStorageQuotaMaxSize; -} +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, @@ -91,12 +65,6 @@ void CountOriginType(const std::set<GURL>& origins, } // anonymous namespace -// TODO(kinuko): We will need to have different sizes for different platforms -// (e.g. larger for desktop etc) and may want to have them in preferences. -const int64 QuotaManager::kTemporaryStorageQuotaDefaultSize = 50 * kMBytes; -const int64 QuotaManager::kTemporaryStorageQuotaMaxSize = 1 * 1024 * kMBytes; -const int64 QuotaManager::kIncognitoDefaultTemporaryQuota = 50 * kMBytes; - const int QuotaManager::kPerHostTemporaryPortion = 5; // 20% const char QuotaManager::kDatabaseName[] = "QuotaManager"; @@ -106,94 +74,114 @@ const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3; const int QuotaManager::kEvictionIntervalInMilliSeconds = 30 * kMinutesInMilliSeconds; -const base::TimeDelta QuotaManager::kReportHistogramInterval = - base::TimeDelta::FromMilliseconds(60 * 60 * 1000); // 1 hour +// Callback translators. +void CallGetUsageAndQuotaCallback( + 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); + delete callback; +} + +void CallQuotaCallback( + QuotaCallback* callback, + StorageType type, + QuotaStatusCode status, + const QuotaAndUsage& quota_and_usage) { + callback->Run(status, type, quota_and_usage.quota); + delete callback; +} // 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 std::deque<GetUsageAndQuotaCallback*> CallbackList; + typedef UsageAndQuotaDispatcherCallback Callback; + typedef std::deque<Callback> CallbackList; static UsageAndQuotaDispatcherTask* Create( - QuotaManager* manager, const std::string& host, StorageType type); + 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(GetUsageAndQuotaCallback* callback, bool unlimited) { - if (unlimited) - unlimited_callbacks_.push_back(callback); - else - callbacks_.push_back(callback); - return (callbacks_.size() + unlimited_callbacks_.size() == 1); + bool AddCallback(const Callback& callback) { + callbacks_.push_back(callback); + return (callbacks_.size() == 1); } - void DidGetGlobalUsage(StorageType type, int64 usage, - int64 unlimited_usage) { - DCHECK_EQ(type_, type); + 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(host_, host); - DCHECK_EQ(type_, type); + 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 DidGetGlobalQuota(QuotaStatusCode status, - StorageType type, - int64 quota) { - DCHECK_EQ(type_, type); - quota_status_ = status; - quota_ = quota; - CheckCompleted(); - } - void DidGetHostQuota(QuotaStatusCode status, const std::string& host, StorageType type, - int64 quota) { - DCHECK_EQ(host_, host); - DCHECK_EQ(type_, type); - quota_status_ = status; - quota_ = quota; + 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 std::string& host, - StorageType type) + const HostAndType& host_and_type) : QuotaTask(manager), - host_(host), - type_(type), - quota_(-1), + 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), callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} - virtual ~UsageAndQuotaDispatcherTask() { - STLDeleteContainerPointers(callbacks_.begin(), callbacks_.end()); - STLDeleteContainerPointers(unlimited_callbacks_.begin(), - unlimited_callbacks_.end()); - } + 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 @@ -202,8 +190,7 @@ class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask { } virtual void Aborted() OVERRIDE { - CallCallbacksAndClear(&callbacks_, kQuotaErrorAbort, 0, 0); - CallCallbacksAndClear(&unlimited_callbacks_, kQuotaErrorAbort, 0, 0); + CallCallbacksAndClear(kQuotaErrorAbort, 0, 0, 0, 0); DeleteSoon(); } @@ -212,29 +199,50 @@ class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask { } void CallCallbacksAndClear( - CallbackList* callbacks, QuotaStatusCode status, - int64 usage, int64 quota) { - for (CallbackList::iterator iter = callbacks->begin(); - iter != callbacks->end(); ++iter) { - (*iter)->Run(status, usage, quota); - delete *iter; + 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(); + callbacks_.clear(); } QuotaManager* manager() const { return static_cast<QuotaManager*>(observer()); } - std::string host() const { return host_; } - StorageType type() const { return type_; } - int64 quota() const { return quota_; } + 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_; } - CallbackList& unlimited_callbacks() { return unlimited_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. @@ -248,15 +256,15 @@ class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask { return callback_factory_.NewCallback( &UsageAndQuotaDispatcherTask::DidGetHostUsage); } - QuotaCallback* NewWaitableGlobalQuotaCallback() { + HostQuotaCallback* NewWaitableHostQuotaCallback() { ++waiting_callbacks_; return callback_factory_.NewCallback( - &UsageAndQuotaDispatcherTask::DidGetGlobalQuota); + &UsageAndQuotaDispatcherTask::DidGetHostQuota); } - HostQuotaCallback* NewWaitableHostQuotaCallback() { + AvailableSpaceCallback* NewWaitableAvailableSpaceCallback() { ++waiting_callbacks_; return callback_factory_.NewCallback( - &UsageAndQuotaDispatcherTask::DidGetHostQuota); + &UsageAndQuotaDispatcherTask::DidGetAvailableSpace); } private: @@ -264,26 +272,25 @@ class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask { if (--waiting_callbacks_ <= 0) { DispatchCallbacks(); DCHECK(callbacks_.empty()); - DCHECK(unlimited_callbacks_.empty()); UsageAndQuotaDispatcherTaskMap& dispatcher_map = manager()->usage_and_quota_dispatchers_; - DCHECK(dispatcher_map.find(std::make_pair(host_, type_)) != - dispatcher_map.end()); - dispatcher_map.erase(std::make_pair(host_, type_)); + DCHECK(dispatcher_map.find(host_and_type_) != dispatcher_map.end()); + dispatcher_map.erase(host_and_type_); CallCompleted(); } } const std::string host_; - const StorageType type_; - int64 quota_; + 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_; - CallbackList unlimited_callbacks_; int waiting_callbacks_; ScopedCallbackFactory<UsageAndQuotaDispatcherTask> callback_factory_; @@ -355,8 +362,8 @@ class QuotaManager::UsageAndQuotaDispatcherTaskForTemporary : public QuotaManager::UsageAndQuotaDispatcherTask { public: UsageAndQuotaDispatcherTaskForTemporary( - QuotaManager* manager, const std::string& host) - : UsageAndQuotaDispatcherTask(manager, host, kStorageTypeTemporary) {} + QuotaManager* manager, const HostAndType& host_and_type) + : UsageAndQuotaDispatcherTask(manager, host_and_type) {} protected: virtual void RunBody() OVERRIDE { @@ -364,34 +371,24 @@ class QuotaManager::UsageAndQuotaDispatcherTaskForTemporary NewWaitableGlobalUsageCallback()); manager()->temporary_usage_tracker_->GetHostUsage( host(), NewWaitableHostUsageCallback()); - manager()->GetTemporaryGlobalQuota(NewWaitableGlobalQuotaCallback()); + manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback()); } virtual void DispatchCallbacks() OVERRIDE { - // Due to a mismatch of models between same-origin (required by stds) vs - // per-host (as this info is viewed in the UI) vs the extension systems - // notion of webextents (the basis of granting unlimited rights), - // we can end up with both 'unlimited' and 'limited' callbacks to invoke. - // Should be rare, but it can happen. - - CallCallbacksAndClear(&unlimited_callbacks(), quota_status(), - host_usage(), kint64max); - - if (!callbacks().empty()) { - // Allow an individual host to utilize a fraction of the total - // pool available for temp storage. - int64 host_quota = 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 > quota()) - host_quota = std::min(host_quota, host_usage()); - - CallCallbacksAndClear(&callbacks(), quota_status(), - host_usage(), host_quota); - } + // 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()); } }; @@ -399,8 +396,8 @@ class QuotaManager::UsageAndQuotaDispatcherTaskForPersistent : public QuotaManager::UsageAndQuotaDispatcherTask { public: UsageAndQuotaDispatcherTaskForPersistent( - QuotaManager* manager, const std::string& host) - : UsageAndQuotaDispatcherTask(manager, host, kStorageTypePersistent) {} + QuotaManager* manager, const HostAndType& host_and_type) + : UsageAndQuotaDispatcherTask(manager, host_and_type) {} protected: virtual void RunBody() OVERRIDE { @@ -411,24 +408,51 @@ class QuotaManager::UsageAndQuotaDispatcherTaskForPersistent } virtual void DispatchCallbacks() OVERRIDE { - CallCallbacksAndClear(&callbacks(), quota_status(), - host_usage(), quota()); - CallCallbacksAndClear(&unlimited_callbacks(), quota_status(), - host_usage(), kint64max); + 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, const std::string& host, StorageType type) { - switch (type) { + 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); + manager, host_and_type); case kStorageTypePersistent: return new UsageAndQuotaDispatcherTaskForPersistent( - manager, host); + manager, host_and_type); default: NOTREACHED(); } @@ -534,83 +558,72 @@ class QuotaManager::DatabaseTaskBase : public QuotaThreadTask { class QuotaManager::InitializeTask : public QuotaManager::DatabaseTaskBase { public: - InitializeTask( - QuotaManager* manager, - const FilePath& profile_path, - bool is_incognito) + InitializeTask(QuotaManager* manager) : DatabaseTaskBase(manager), - profile_path_(profile_path), - is_incognito_(is_incognito), - need_initialize_origins_(false), - temporary_storage_quota_(-1) { + temporary_quota_override_(-1), + desired_available_space_(-1) { + DCHECK(manager); } protected: virtual void RunOnTargetThread() OVERRIDE { - // Initializes the global temporary quota. - if (!database()->GetGlobalQuota( - kStorageTypeTemporary, &temporary_storage_quota_)) { - // If the temporary storage quota size has not been initialized, - // make up one and store it in the database. - temporary_storage_quota_ = GetInitialTemporaryStorageQuotaSize( - profile_path_, is_incognito_); - UMA_HISTOGRAM_MBYTES("Quota.InitialTemporaryGlobalStorageQuota", - temporary_storage_quota_); - if (!database()->SetGlobalQuota( - kStorageTypeTemporary, temporary_storage_quota_)) { - set_db_disabled(true); - } - } - if (!db_disabled()) - need_initialize_origins_ = !database()->IsOriginDatabaseBootstrapped(); + // 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()->need_initialize_origins_ = need_initialize_origins_; - manager()->DidInitializeTemporaryGlobalQuota(temporary_storage_quota_); - manager()->histogram_timer_.Start(FROM_HERE, - QuotaManager::kReportHistogramInterval, - manager(), - &QuotaManager::ReportHistogram); + manager()->temporary_quota_override_ = temporary_quota_override_; + manager()->desired_available_space_ = desired_available_space_; + manager()->temporary_quota_initialized_ = true; + manager()->DidRunInitializeTask(); } private: - FilePath profile_path_; - bool is_incognito_; - bool need_initialize_origins_; - int64 temporary_storage_quota_; + int64 temporary_quota_override_; + int64 desired_available_space_; }; -class QuotaManager::UpdateTemporaryGlobalQuotaTask +class QuotaManager::UpdateTemporaryQuotaOverrideTask : public QuotaManager::DatabaseTaskBase { public: - UpdateTemporaryGlobalQuotaTask( + UpdateTemporaryQuotaOverrideTask( QuotaManager* manager, int64 new_quota, QuotaCallback* callback) : DatabaseTaskBase(manager), new_quota_(new_quota), - callback_(callback) { - DCHECK_GE(new_quota, 0); - } + callback_(callback) {} protected: virtual void RunOnTargetThread() OVERRIDE { - if (!database()->SetGlobalQuota(kStorageTypeTemporary, new_quota_)) { + if (!database()->SetQuotaConfigValue( + QuotaDatabase::kTemporaryQuotaOverrideKey, new_quota_)) { set_db_disabled(true); - new_quota_ = 0; + new_quota_ = -1; + return; } } virtual void DatabaseTaskCompleted() OVERRIDE { - callback_->Run(db_disabled() ? kQuotaErrorInvalidAccess : kQuotaStatusOk, - kStorageTypeTemporary, new_quota_); if (!db_disabled()) { - manager()->temporary_global_quota_ = new_quota_; + 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_.get()) { + callback_->Run(status, type, quota); + callback_.reset(); + } + } + int64 new_quota_; scoped_ptr<QuotaCallback> callback_; }; @@ -708,9 +721,8 @@ class QuotaManager::GetLRUOriginTask protected: virtual void RunOnTargetThread() OVERRIDE { - if (!database()->GetLRUOrigin(type_, exceptions_, - special_storage_policy_, &url_)) - set_db_disabled(true); + database()->GetLRUOrigin( + type_, exceptions_, special_storage_policy_, &url_); } virtual void DatabaseTaskCompleted() OVERRIDE { @@ -792,13 +804,12 @@ class QuotaManager::AvailableSpaceQueryTask : public QuotaThreadTask { public: AvailableSpaceQueryTask( QuotaManager* manager, - scoped_refptr<base::MessageLoopProxy> db_message_loop, - const FilePath& profile_path, AvailableSpaceCallback* callback) - : QuotaThreadTask(manager, db_message_loop), - profile_path_(profile_path), + : QuotaThreadTask(manager, manager->db_thread_), + profile_path_(manager->profile_path_), space_(-1), - callback_(callback) {} + callback_(callback) { + } virtual ~AvailableSpaceQueryTask() {} protected: @@ -807,7 +818,7 @@ class QuotaManager::AvailableSpaceQueryTask : public QuotaThreadTask { } virtual void Aborted() OVERRIDE { - callback_->Run(kQuotaErrorAbort, -1); + callback_.reset(); } virtual void Completed() OVERRIDE { @@ -1010,8 +1021,9 @@ QuotaManager::QuotaManager(bool is_incognito, eviction_disabled_(false), io_thread_(io_thread), db_thread_(db_thread), - need_initialize_origins_(false), - temporary_global_quota_(-1), + temporary_quota_initialized_(false), + temporary_quota_override_(-1), + desired_available_space_(-1), special_storage_policy_(special_storage_policy), callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } @@ -1033,71 +1045,54 @@ void QuotaManager::GetUsageInfo(GetUsageInfoCallback* callback) { void QuotaManager::GetUsageAndQuota( const GURL& origin, StorageType type, - GetUsageAndQuotaCallback* callback_ptr) { - scoped_ptr<GetUsageAndQuotaCallback> callback(callback_ptr); - LazyInitialize(); - - if (type == kStorageTypeUnknown) { - // Quota only supports temporary/persistent types. - callback->Run(kQuotaErrorNotSupported, 0, 0); - return; - } - - // note: returns host usage and quota - std::string host = net::GetHostOrSpecFromURL(origin); - UsageAndQuotaDispatcherTaskMap::iterator found = - usage_and_quota_dispatchers_.find(std::make_pair(host, type)); - if (found == usage_and_quota_dispatchers_.end()) { - UsageAndQuotaDispatcherTask* dispatcher = - UsageAndQuotaDispatcherTask::Create(this, host, type); - found = usage_and_quota_dispatchers_.insert( - std::make_pair(std::make_pair(host, type), dispatcher)).first; - } - if (found->second->AddCallback( - callback.release(), IsStorageUnlimited(origin))) { - found->second->Start(); - } + GetUsageAndQuotaCallback* callback) { + GetUsageAndQuotaInternal(origin, type, false /* global */, + base::Bind(&CallGetUsageAndQuotaCallback, + callback, IsStorageUnlimited(origin))); } void QuotaManager::GetAvailableSpace(AvailableSpaceCallback* callback) { - scoped_refptr<AvailableSpaceQueryTask> task( - new AvailableSpaceQueryTask(this, db_thread_, profile_path_, callback)); - task->Start(); + if (is_incognito_) { + callback->Run(kQuotaStatusOk, kIncognitoDefaultTemporaryQuota); + delete callback; + return; + } + make_scoped_refptr(new AvailableSpaceQueryTask(this, callback))->Start(); } void QuotaManager::GetTemporaryGlobalQuota(QuotaCallback* callback) { - LazyInitialize(); - if (temporary_global_quota_ >= 0) { - // TODO(kinuko): We may want to adjust the quota when the current - // available space in the hard drive is getting tight. - callback->Run(kQuotaStatusOk, - kStorageTypeTemporary, temporary_global_quota_); + if (temporary_quota_override_ > 0) { + callback->Run(kQuotaStatusOk, kStorageTypeTemporary, + temporary_quota_override_); delete callback; return; } - // They are called upon completion of InitializeTask. - temporary_global_quota_callbacks_.Add(callback); + GetUsageAndQuotaInternal( + GURL(), kStorageTypeTemporary, true /* global */, + base::Bind(&CallQuotaCallback, callback, kStorageTypeTemporary)); } -void QuotaManager::SetTemporaryGlobalQuota(int64 new_quota, - QuotaCallback* callback) { +void QuotaManager::SetTemporaryGlobalOverrideQuota( + int64 new_quota, QuotaCallback* callback_ptr) { + scoped_ptr<QuotaCallback> callback(callback_ptr); LazyInitialize(); + if (new_quota < 0) { - callback->Run(kQuotaErrorInvalidModification, - kStorageTypeTemporary, -1); - delete callback; + if (callback.get()) + callback->Run(kQuotaErrorInvalidModification, + kStorageTypeTemporary, -1); return; } - if (!db_disabled_) { - scoped_refptr<UpdateTemporaryGlobalQuotaTask> task( - new UpdateTemporaryGlobalQuotaTask(this, new_quota, callback)); - task->Start(); - } else { - callback->Run(kQuotaErrorInvalidAccess, - kStorageTypeTemporary, -1); - delete callback; + if (db_disabled_) { + if (callback.get()) + callback->Run(kQuotaErrorInvalidAccess, + kStorageTypeTemporary, -1); + return; } + + make_scoped_refptr(new UpdateTemporaryQuotaOverrideTask( + this, new_quota, callback.release()))->Start(); } void QuotaManager::GetPersistentHostQuota(const std::string& host, @@ -1196,9 +1191,7 @@ void QuotaManager::LazyInitialize() { new UsageTracker(clients_, kStorageTypePersistent, special_storage_policy_)); - scoped_refptr<InitializeTask> task( - new InitializeTask(this, profile_path_, is_incognito_)); - task->Start(); + make_scoped_refptr(new InitializeTask(this))->Start(); } void QuotaManager::RegisterClient(QuotaClient* client) { @@ -1329,6 +1322,46 @@ void QuotaManager::NotifyStorageModifiedInternal( 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(DumpQuotaTableCallback* callback) { make_scoped_refptr(new DumpQuotaTableTask(this, callback))->Start(); } @@ -1398,58 +1431,20 @@ void QuotaManager::EvictOriginData( &QuotaManager::DidOriginDataEvicted)); } -void QuotaManager::DidGetAvailableSpaceForEviction( - QuotaStatusCode status, - int64 available_space) { - eviction_context_.get_usage_and_quota_callback->Run(status, - eviction_context_.usage, - eviction_context_.unlimited_usage, - eviction_context_.quota, available_space); - eviction_context_.get_usage_and_quota_callback.reset(); -} - -void QuotaManager::DidGetGlobalQuotaForEviction( - QuotaStatusCode status, - StorageType type, - int64 quota) { - DCHECK_EQ(type, kStorageTypeTemporary); - if (status != kQuotaStatusOk) { - eviction_context_.get_usage_and_quota_callback->Run( - status, 0, 0, 0, 0); - eviction_context_.get_usage_and_quota_callback.reset(); - return; - } - - eviction_context_.quota = quota; - GetAvailableSpace(callback_factory_. - NewCallback(&QuotaManager::DidGetAvailableSpaceForEviction)); -} - -void QuotaManager::DidGetGlobalUsageForEviction( - StorageType type, int64 usage, int64 unlimited_usage) { - DCHECK_EQ(type, kStorageTypeTemporary); - DCHECK_GE(usage, unlimited_usage); - eviction_context_.usage = usage; - eviction_context_.unlimited_usage = unlimited_usage; - GetTemporaryGlobalQuota(callback_factory_. - NewCallback(&QuotaManager::DidGetGlobalQuotaForEviction)); -} - void QuotaManager::GetUsageAndQuotaForEviction( - GetUsageAndQuotaForEvictionCallback* callback) { + const GetUsageAndQuotaForEvictionCallback& callback) { DCHECK(io_thread_->BelongsToCurrentThread()); - DCHECK(!eviction_context_.get_usage_and_quota_callback.get()); - - eviction_context_.get_usage_and_quota_callback.reset(callback); - // TODO(dmikurube): Make kStorageTypeTemporary an argument. - GetGlobalUsage(kStorageTypeTemporary, callback_factory_. - NewCallback(&QuotaManager::DidGetGlobalUsageForEviction)); + GetUsageAndQuotaInternal( + GURL(), kStorageTypeTemporary, true /* global */, callback); } void QuotaManager::StartEviction() { DCHECK(!temporary_storage_evictor_.get()); - temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(this, - kEvictionIntervalInMilliSeconds)); + 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(); } @@ -1508,38 +1503,40 @@ void QuotaManager::DidGetPersistentGlobalUsageForHistogram( unlimited_origins); } -void QuotaManager::DidInitializeTemporaryGlobalQuota(int64 quota) { - temporary_global_quota_ = quota; - temporary_global_quota_callbacks_.Run( - db_disabled_ ? kQuotaErrorInvalidAccess : kQuotaStatusOk, - kStorageTypeTemporary, quota); +void QuotaManager::DidRunInitializeTask() { + histogram_timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds( + kReportHistogramInterval), + this, &QuotaManager::ReportHistogram); - if (db_disabled_ || eviction_disabled_) - return; + DCHECK(temporary_quota_initialized_); - if (!need_initialize_origins_) { - StartEviction(); - return; + // 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(); } - // We seem to need to initialize the origin table in the database. - // Kick the first GetGlobalUsage for temporary storage to cache a list - // of origins that have data in temporary storage to register them - // in the database. (We'll need the global temporary usage anyway - // for eviction later.) - temporary_usage_tracker_->GetGlobalUsage(callback_factory_.NewCallback( - &QuotaManager::DidRunInitialGetTemporaryGlobalUsage)); + // Kick the first GetTemporaryGlobalQuota. This internally fetches (and + // caches) the usage of all origins and checks the available disk space. + GetTemporaryGlobalQuota(callback_factory_.NewCallback( + &QuotaManager::DidGetInitialTemporaryGlobalQuota)); } -void QuotaManager::DidRunInitialGetTemporaryGlobalUsage( - StorageType type, int64 usage_unused, int64 unlimited_usage_unused) { +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. - scoped_refptr<InitializeTemporaryOriginsInfoTask> task( - new InitializeTemporaryOriginsInfoTask( - this, temporary_usage_tracker_.get())); - task->Start(); + make_scoped_refptr(new InitializeTemporaryOriginsInfoTask( + this, temporary_usage_tracker_.get()))->Start(); } void QuotaManager::DidGetDatabaseLRUOrigin(const GURL& origin) { diff --git a/webkit/quota/quota_manager.h b/webkit/quota/quota_manager.h index 2d0de6a..df701d6 100644 --- a/webkit/quota/quota_manager.h +++ b/webkit/quota/quota_manager.h @@ -45,20 +45,24 @@ class QuotaTemporaryStorageEvictor; class UsageTracker; class MockQuotaManager; +struct QuotaAndUsage { + int64 usage; + int64 unlimited_usage; + int64 quota; + int64 available_disk_space; +}; + // An interface called by QuotaTemporaryStorageEvictor. class QuotaEvictionHandler { public: - virtual ~QuotaEvictionHandler() {} - typedef Callback1<const GURL&>::Type GetLRUOriginCallback; typedef StatusCallback EvictOriginDataCallback; - typedef Callback5<QuotaStatusCode, - int64 /* usage */, - int64 /* unlimited_usage */, - int64 /* quota */, - int64 /* physical_available */ >::Type + typedef base::Callback<void(QuotaStatusCode, + const QuotaAndUsage& quota_and_usage)> GetUsageAndQuotaForEvictionCallback; + virtual ~QuotaEvictionHandler() {} + // Returns the least recently used origin. It might return empty // GURL when there are no evictable origins. virtual void GetLRUOrigin( @@ -71,7 +75,7 @@ class QuotaEvictionHandler { EvictOriginDataCallback* callback) = 0; virtual void GetUsageAndQuotaForEviction( - GetUsageAndQuotaForEvictionCallback* callback) = 0; + const GetUsageAndQuotaForEvictionCallback& callback) = 0; }; struct UsageInfo { @@ -149,7 +153,11 @@ class QuotaManager : public QuotaTaskObserver, // Called by UI and internal modules. void GetAvailableSpace(AvailableSpaceCallback* callback); void GetTemporaryGlobalQuota(QuotaCallback* callback); - void SetTemporaryGlobalQuota(int64 new_quota, QuotaCallback* callback); + + // Ok to call with NULL callback. + void SetTemporaryGlobalOverrideQuota(int64 new_quota, + QuotaCallback* callback); + void GetPersistentHostQuota(const std::string& host, HostQuotaCallback* callback); void SetPersistentHostQuota(const std::string& host, @@ -172,11 +180,6 @@ class QuotaManager : public QuotaTaskObserver, bool ResetUsageTracker(StorageType type); - // Used to determine the total size of the temp pool. - static const int64 kTemporaryStorageQuotaDefaultSize; - static const int64 kTemporaryStorageQuotaMaxSize; - static const int64 kIncognitoDefaultTemporaryQuota; - // Determines the portion of the temp pool that can be // utilized by a single host (ie. 5 for 20%). static const int kPerHostTemporaryPortion; @@ -186,12 +189,11 @@ class QuotaManager : public QuotaTaskObserver, static const int kThresholdOfErrorsToBeBlacklisted; static const int kEvictionIntervalInMilliSeconds; - static const base::TimeDelta kReportHistogramInterval; private: class DatabaseTaskBase; class InitializeTask; - class UpdateTemporaryGlobalQuotaTask; + class UpdateTemporaryQuotaOverrideTask; class GetPersistentHostQuotaTask; class UpdatePersistentHostQuotaTask; class GetLRUOriginTask; @@ -205,6 +207,7 @@ class QuotaManager : public QuotaTaskObserver, class UsageAndQuotaDispatcherTask; class UsageAndQuotaDispatcherTaskForTemporary; class UsageAndQuotaDispatcherTaskForPersistent; + class UsageAndQuotaDispatcherTaskForTemporaryGlobal; class OriginDataDeleter; @@ -222,29 +225,20 @@ class QuotaManager : public QuotaTaskObserver, DumpOriginInfoTableCallback; struct EvictionContext { - EvictionContext() - : evicted_type(kStorageTypeUnknown), - usage(0), - unlimited_usage(0), - quota(0) {} + EvictionContext() : evicted_type(kStorageTypeUnknown) {} virtual ~EvictionContext() {} - GURL evicted_origin; StorageType evicted_type; - scoped_ptr<EvictOriginDataCallback> evict_origin_data_callback; - - scoped_ptr<GetUsageAndQuotaForEvictionCallback> - get_usage_and_quota_callback; - int64 usage; - int64 unlimited_usage; - int64 quota; }; typedef std::pair<std::string, StorageType> HostAndType; typedef std::map<HostAndType, UsageAndQuotaDispatcherTask*> UsageAndQuotaDispatcherTaskMap; + typedef QuotaEvictionHandler::GetUsageAndQuotaForEvictionCallback + UsageAndQuotaDispatcherCallback; + friend class quota_internals::QuotaInternalsProxy; friend struct QuotaManagerDeleter; friend class MockStorageClient; @@ -283,6 +277,13 @@ class QuotaManager : public QuotaTaskObserver, int64 delta, base::Time modified_time); + // |origin| can be empty if |global| is true. + void GetUsageAndQuotaInternal( + const GURL& origin, + StorageType type, + bool global, + const UsageAndQuotaDispatcherCallback& callback); + void DumpQuotaTable(DumpQuotaTableCallback* callback); void DumpOriginInfoTable(DumpOriginInfoTableCallback* callback); @@ -291,16 +292,12 @@ class QuotaManager : public QuotaTaskObserver, void DeleteOriginFromDatabase(const GURL& origin, StorageType type); void DidOriginDataEvicted(QuotaStatusCode status); - void DidGetAvailableSpaceForEviction( - QuotaStatusCode status, - int64 available_space); - void DidGetGlobalQuotaForEviction( - QuotaStatusCode status, - StorageType type, - int64 quota); - void DidGetGlobalUsageForEviction(StorageType type, - int64 usage, - int64 unlimited_usage); + void DidGetGlobalUsageAndQuotaForEviction(QuotaStatusCode status, + StorageType type, + int64 usage, + int64 unlimited_usage, + int64 quota, + int64 available_space); void ReportHistogram(); void DidGetTemporaryGlobalUsageForHistogram(StorageType type, @@ -319,11 +316,12 @@ class QuotaManager : public QuotaTaskObserver, StorageType type, EvictOriginDataCallback* callback) OVERRIDE; virtual void GetUsageAndQuotaForEviction( - GetUsageAndQuotaForEvictionCallback* callback) OVERRIDE; + const GetUsageAndQuotaForEvictionCallback& callback) OVERRIDE; - void DidInitializeTemporaryGlobalQuota(int64 quota); - void DidRunInitialGetTemporaryGlobalUsage(StorageType type, int64 usage, - int64 unlimited_usage); + void DidRunInitializeTask(); + void DidGetInitialTemporaryGlobalQuota(QuotaStatusCode status, + StorageType type, + int64 quota_unused); void DidGetDatabaseLRUOrigin(const GURL& origin); void DeleteOnCorrectThread() const; @@ -338,7 +336,6 @@ class QuotaManager : public QuotaTaskObserver, scoped_refptr<base::MessageLoopProxy> db_thread_; mutable scoped_ptr<QuotaDatabase> database_; - bool need_initialize_origins_; scoped_ptr<GetLRUOriginCallback> lru_origin_callback_; std::set<GURL> access_notified_origins_; @@ -354,8 +351,10 @@ class QuotaManager : public QuotaTaskObserver, UsageAndQuotaDispatcherTaskMap usage_and_quota_dispatchers_; - int64 temporary_global_quota_; - QuotaCallbackQueue temporary_global_quota_callbacks_; + bool temporary_quota_initialized_; + int64 temporary_quota_override_; + + int64 desired_available_space_; // Map from origin to count. std::map<GURL, int> origins_in_use_; diff --git a/webkit/quota/quota_manager_unittest.cc b/webkit/quota/quota_manager_unittest.cc index e9f4ab3..3a34e9f 100644 --- a/webkit/quota/quota_manager_unittest.cc +++ b/webkit/quota/quota_manager_unittest.cc @@ -6,9 +6,11 @@ #include <sstream> #include <vector> +#include "base/bind.h" #include "base/file_util.h" #include "base/memory/scoped_callback_factory.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" #include "base/scoped_temp_dir.h" @@ -17,16 +19,12 @@ #include "base/time.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageQuotaError.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageQuotaType.h" #include "webkit/quota/mock_special_storage_policy.h" #include "webkit/quota/mock_storage_client.h" #include "webkit/quota/quota_database.h" #include "webkit/quota/quota_manager.h" using base::MessageLoopProxy; -using WebKit::WebStorageQuotaError; -using WebKit::WebStorageQuotaType; namespace quota { @@ -43,6 +41,7 @@ class QuotaManagerTest : public testing::Test { public: QuotaManagerTest() : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), mock_time_counter_(0) { } @@ -103,7 +102,8 @@ class QuotaManagerTest : public testing::Test { void SetTemporaryGlobalQuota(int64 new_quota) { quota_status_ = kQuotaStatusUnknown; quota_ = -1; - quota_manager_->SetTemporaryGlobalQuota(new_quota, + quota_manager_->SetTemporaryGlobalOverrideQuota( + new_quota, callback_factory_.NewCallback( &QuotaManagerTest::DidGetQuota)); } @@ -193,8 +193,8 @@ class QuotaManagerTest : public testing::Test { quota_ = -1; available_space_ = -1; quota_manager_->GetUsageAndQuotaForEviction( - callback_factory_.NewCallback( - &QuotaManagerTest::DidGetUsageAndQuotaForEviction)); + base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaForEviction, + weak_factory_.GetWeakPtr())); } void GetCachedOrigins(StorageType type, std::set<GURL>* origins) { @@ -305,12 +305,12 @@ class QuotaManagerTest : public testing::Test { } void DidGetUsageAndQuotaForEviction(QuotaStatusCode status, - int64 usage, int64 unlimited_usage, int64 quota, int64 available_space) { + const QuotaAndUsage& quota_and_usage) { quota_status_ = status; - usage_ = usage; - unlimited_usage_ = unlimited_usage; - quota_ = quota; - available_space_ = available_space; + usage_ = quota_and_usage.usage; + unlimited_usage_ = quota_and_usage.unlimited_usage; + quota_ = quota_and_usage.quota; + available_space_ = quota_and_usage.available_disk_space; } void DidGetLRUOrigin(const GURL& origin) { @@ -375,6 +375,7 @@ class QuotaManagerTest : public testing::Test { ScopedTempDir data_dir_; base::ScopedCallbackFactory<QuotaManagerTest> callback_factory_; + base::WeakPtrFactory<QuotaManagerTest> weak_factory_; scoped_refptr<QuotaManager> quota_manager_; scoped_refptr<MockSpecialStoragePolicy> mock_special_storage_policy_; @@ -440,7 +441,7 @@ TEST_F(QuotaManagerTest, GetUsageInfo) { TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) { static const MockOriginData kData[] = { - { "http://foo.com/", kTemp, 10 }, + { "http://foo.com/", kTemp, 10 }, { "http://foo.com/", kPerm, 80 }, }; RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); @@ -456,7 +457,6 @@ TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) { EXPECT_EQ(kQuotaStatusOk, status()); EXPECT_EQ(10, usage()); EXPECT_LE(0, quota()); - EXPECT_GE(QuotaManager::kTemporaryStorageQuotaMaxSize, quota()); int64 quota_returned_for_foo = quota(); GetUsageAndQuota(GURL("http://bar.com/"), kTemp); @@ -534,7 +534,7 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) { { "http://bar.com/", kTemp, 5 }, { "https://bar.com/", kTemp, 7 }, { "http://baz.com/", kTemp, 30 }, - { "http://foo.com/", kPerm, 40 }, + { "http://foo.com/", kPerm, 40 }, }; RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); @@ -655,9 +655,9 @@ TEST_F(QuotaManagerTest, GetTemporaryUsage_WithModify) { TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { static const MockOriginData kData[] = { - { "http://foo.com/", kTemp, 10 }, - { "http://foo.com:8080/", kTemp, 20 }, - { "http://bar.com/", kTemp, 13 }, + { "http://foo.com/", kTemp, 10 }, + { "http://foo.com:8080/", kTemp, 20 }, + { "http://bar.com/", kTemp, 13 }, { "http://foo.com/", kPerm, 40 }, }; RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); @@ -689,9 +689,9 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) { static const MockOriginData kData[] = { - { "http://foo.com/", kTemp, 10 }, - { "http://foo.com:8080/", kTemp, 20 }, - { "http://bar.com/", kTemp, 13 }, + { "http://foo.com/", kTemp, 10 }, + { "http://foo.com:8080/", kTemp, 20 }, + { "http://bar.com/", kTemp, 13 }, { "http://foo.com/", kPerm, 40 }, }; RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); @@ -1259,10 +1259,10 @@ TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) { TEST_F(QuotaManagerTest, GetUsageAndQuotaForEviction) { static const MockOriginData kData[] = { - { "http://foo.com/", kTemp, 1 }, - { "http://foo.com:1/", kTemp, 20 }, + { "http://foo.com/", kTemp, 1 }, + { "http://foo.com:1/", kTemp, 20 }, { "http://foo.com/", kPerm, 300 }, - { "http://unlimited/", kTemp, 4000 }, + { "http://unlimited/", kTemp, 4000 }, }; mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/")); @@ -1388,16 +1388,17 @@ TEST_F(QuotaManagerTest, GetCachedOrigins) { GetCachedOrigins(kTemp, &origins); EXPECT_TRUE(origins.empty()); - // Make the cache hot. + // No matter how we make queries the quota manager tries to cache all + // the origins at startup. GetHostUsage("a.com", kTemp); MessageLoop::current()->RunAllPending(); GetCachedOrigins(kTemp, &origins); - EXPECT_EQ(2U, origins.size()); + EXPECT_EQ(3U, origins.size()); GetHostUsage("b.com", kTemp); MessageLoop::current()->RunAllPending(); GetCachedOrigins(kTemp, &origins); - EXPECT_EQ(2U, origins.size()); + EXPECT_EQ(3U, origins.size()); GetCachedOrigins(kPerm, &origins); EXPECT_TRUE(origins.empty()); diff --git a/webkit/quota/quota_temporary_storage_evictor.cc b/webkit/quota/quota_temporary_storage_evictor.cc index f4b6750..1ad329a 100644 --- a/webkit/quota/quota_temporary_storage_evictor.cc +++ b/webkit/quota/quota_temporary_storage_evictor.cc @@ -4,6 +4,7 @@ #include "webkit/quota/quota_temporary_storage_evictor.h" +#include "base/bind.h" #include "base/metrics/histogram.h" #include "googleurl/src/gurl.h" #include "webkit/quota/quota_manager.h" @@ -21,17 +22,16 @@ namespace { const int64 kMBytes = 1024 * 1024; +const double kUsageRatioToStartEviction = 0.7; +const int kThresholdOfErrorsToStopEviction = 5; +const base::TimeDelta kHistogramReportInterval = + base::TimeDelta::FromMilliseconds(60 * 60 * 1000); // 1 hour } namespace quota { -const double QuotaTemporaryStorageEvictor::kUsageRatioToStartEviction = 0.7; const int QuotaTemporaryStorageEvictor:: kMinAvailableDiskSpaceToStartEvictionNotSpecified = -1; -const int QuotaTemporaryStorageEvictor::kThresholdOfErrorsToStopEviction = 5; - -const base::TimeDelta QuotaTemporaryStorageEvictor::kHistogramReportInterval = - base::TimeDelta::FromMilliseconds(60 * 60 * 1000); // 1 hour QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor( QuotaEvictionHandler* quota_eviction_handler, @@ -41,7 +41,8 @@ QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor( quota_eviction_handler_(quota_eviction_handler), interval_ms_(interval_ms), repeated_eviction_(true), - callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { DCHECK(quota_eviction_handler); } @@ -143,32 +144,33 @@ void QuotaTemporaryStorageEvictor::ConsiderEviction() { OnEvictionRoundStarted(); // Get usage and disk space, then continue. - quota_eviction_handler_->GetUsageAndQuotaForEviction(callback_factory_. - NewCallback( - &QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction)); + quota_eviction_handler_->GetUsageAndQuotaForEviction( + base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction, + weak_factory_.GetWeakPtr())); } void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction( QuotaStatusCode status, - int64 usage, - int64 unlimited_usage, - int64 quota, - int64 available_disk_space) { + const QuotaAndUsage& qau) { DCHECK(CalledOnValidThread()); - DCHECK_GE(usage, unlimited_usage); // unlimited_usage is a subset of usage - usage -= unlimited_usage; + // unlimited_usage is a subset of usage + DCHECK_GE(qau.usage, qau.unlimited_usage); + + int64 usage = qau.usage - qau.unlimited_usage; if (status != kQuotaStatusOk) ++statistics_.num_errors_on_getting_usage_and_quota; int64 usage_overage = std::max( static_cast<int64>(0), - usage - static_cast<int64>(quota * kUsageRatioToStartEviction)); + usage - static_cast<int64>(qau.quota * kUsageRatioToStartEviction)); + // min_available_disk_space_to_start_eviction_ might be < 0 if no value + // is explicitly configured yet. int64 diskspace_shortage = std::max( static_cast<int64>(0), - min_available_disk_space_to_start_eviction_ - available_disk_space); + min_available_disk_space_to_start_eviction_ - qau.available_disk_space); if (!round_statistics_.is_initialized) { round_statistics_.usage_overage_at_round = usage_overage; diff --git a/webkit/quota/quota_temporary_storage_evictor.h b/webkit/quota/quota_temporary_storage_evictor.h index e65dd80..a537171 100644 --- a/webkit/quota/quota_temporary_storage_evictor.h +++ b/webkit/quota/quota_temporary_storage_evictor.h @@ -10,6 +10,7 @@ #include <string> #include "base/memory/scoped_callback_factory.h" +#include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/timer.h" #include "webkit/quota/quota_types.h" @@ -23,6 +24,7 @@ class MessageLoopProxy; namespace quota { class QuotaEvictionHandler; +struct QuotaAndUsage; class QuotaTemporaryStorageEvictor : public base::NonThreadSafe { public: @@ -100,10 +102,7 @@ class QuotaTemporaryStorageEvictor : public base::NonThreadSafe { void ConsiderEviction(); void OnGotUsageAndQuotaForEviction( QuotaStatusCode status, - int64 usage, - int64 unlimited_usage, - int64 quota, - int64 available_disk_space); + const QuotaAndUsage& quota_and_usage); void OnGotLRUOrigin(const GURL& origin); void OnEvictionComplete(QuotaStatusCode status); @@ -115,10 +114,7 @@ class QuotaTemporaryStorageEvictor : public base::NonThreadSafe { repeated_eviction_ = repeated_eviction; } - static const double kUsageRatioToStartEviction; static const int kMinAvailableDiskSpaceToStartEvictionNotSpecified; - static const int kThresholdOfErrorsToStopEviction; - static const base::TimeDelta kHistogramReportInterval; int64 min_available_disk_space_to_start_eviction_; @@ -138,6 +134,7 @@ class QuotaTemporaryStorageEvictor : public base::NonThreadSafe { base::RepeatingTimer<QuotaTemporaryStorageEvictor> histogram_timer_; base::ScopedCallbackFactory<QuotaTemporaryStorageEvictor> callback_factory_; + base::WeakPtrFactory<QuotaTemporaryStorageEvictor> weak_factory_; DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictor); }; diff --git a/webkit/quota/quota_temporary_storage_evictor_unittest.cc b/webkit/quota/quota_temporary_storage_evictor_unittest.cc index 6e857c7..398ba50 100644 --- a/webkit/quota/quota_temporary_storage_evictor_unittest.cc +++ b/webkit/quota/quota_temporary_storage_evictor_unittest.cc @@ -7,6 +7,7 @@ #include <list> #include <map> +#include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" @@ -48,17 +49,16 @@ class MockQuotaEvictionHandler : public quota::QuotaEvictionHandler { } virtual void GetUsageAndQuotaForEviction( - GetUsageAndQuotaForEvictionCallback* callback) OVERRIDE { + const GetUsageAndQuotaForEvictionCallback& callback) OVERRIDE { if (error_on_get_usage_and_quota_) { - callback->Run(quota::kQuotaErrorInvalidAccess, 0, 0, 0, 0); - delete callback; + callback.Run(quota::kQuotaErrorInvalidAccess, QuotaAndUsage()); return; } if (task_for_get_usage_and_quota_.get()) task_for_get_usage_and_quota_->Run(); - callback->Run(quota::kQuotaStatusOk, GetUsage(), unlimited_usage_, - quota_, available_space_); - delete callback; + QuotaAndUsage quota_and_usage = { + GetUsage(), unlimited_usage_, quota_, available_space_ }; + callback.Run(quota::kQuotaStatusOk, quota_and_usage); } virtual void GetLRUOrigin( diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 886f7f6..ff8c757 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -448,13 +448,13 @@ '../../plugins/ppapi/resource_tracker_unittest.cc', '../../plugins/ppapi/time_conversion_unittest.cc', '../../plugins/ppapi/url_request_info_unittest.cc', + '../../quota/mock_quota_manager.cc', + '../../quota/mock_quota_manager.h', + '../../quota/mock_quota_manager_unittest.cc', '../../quota/mock_special_storage_policy.cc', '../../quota/mock_special_storage_policy.h', '../../quota/mock_storage_client.cc', '../../quota/mock_storage_client.h', - '../../quota/mock_quota_manager.cc', - '../../quota/mock_quota_manager.h', - '../../quota/mock_quota_manager_unittest.cc', '../../quota/quota_database_unittest.cc', '../../quota/quota_manager_unittest.cc', '../../quota/quota_temporary_storage_evictor_unittest.cc', |