diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 13:20:14 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 13:20:14 +0000 |
commit | 595765cf3c42fda899d308e6f875dd5627837989 (patch) | |
tree | ca06cb28f9a65f472011b0dc5f94d9b0ee9f653c /webkit | |
parent | 216f6923c677b509de56a4735f91eafdd21d6fc2 (diff) | |
download | chromium_src-595765cf3c42fda899d308e6f875dd5627837989.zip chromium_src-595765cf3c42fda899d308e6f875dd5627837989.tar.gz chromium_src-595765cf3c42fda899d308e6f875dd5627837989.tar.bz2 |
Add 1st cut of QuotaManager code
No persistent storage support yet.
Some notes:
- There are a lot of TODOs especially for persistent type storage handling.
- QuotaTask base class is for now only subclassed by QuotaInitializeTask, but it is planned to add more subclasses.
BUG=61676,79639
TEST=QuotaManagerTest.*
Review URL: http://codereview.chromium.org/6826052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83145 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/quota/mock_storage_client.cc | 140 | ||||
-rw-r--r-- | webkit/quota/mock_storage_client.h | 74 | ||||
-rw-r--r-- | webkit/quota/quota_client.h | 63 | ||||
-rw-r--r-- | webkit/quota/quota_database.h | 8 | ||||
-rw-r--r-- | webkit/quota/quota_manager.cc | 516 | ||||
-rw-r--r-- | webkit/quota/quota_manager.h | 149 | ||||
-rw-r--r-- | webkit/quota/quota_manager_unittest.cc | 295 | ||||
-rw-r--r-- | webkit/quota/quota_task.cc | 87 | ||||
-rw-r--r-- | webkit/quota/quota_task.h | 97 | ||||
-rw-r--r-- | webkit/quota/quota_types.h | 141 | ||||
-rw-r--r-- | webkit/quota/usage_tracker.cc | 400 | ||||
-rw-r--r-- | webkit/quota/usage_tracker.h | 107 | ||||
-rw-r--r-- | webkit/quota/webkit_quota.gypi | 8 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 3 |
14 files changed, 2081 insertions, 7 deletions
diff --git a/webkit/quota/mock_storage_client.cc b/webkit/quota/mock_storage_client.cc new file mode 100644 index 0000000..860ff92 --- /dev/null +++ b/webkit/quota/mock_storage_client.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2011 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/mock_storage_client.h" + +#include "base/atomic_sequence_num.h" +#include "base/basictypes.h" +#include "base/memory/singleton.h" +#include "base/message_loop_proxy.h" +#include "base/scoped_ptr.h" +#include "base/stl_util-inl.h" +#include "net/base/net_util.h" +#include "webkit/quota/quota_manager.h" + +using base::AtomicSequenceNumber; + +namespace quota { + +namespace { + +class MockStorageClientIDSequencer { + public: + static MockStorageClientIDSequencer* GetInstance() { + return Singleton<MockStorageClientIDSequencer>::get(); + } + + QuotaClient::ID NextMockID() { + return static_cast<QuotaClient::ID>( + QuotaClient::kMockStart + seq_.GetNext()); + } + + private: + MockStorageClientIDSequencer() { } + friend struct DefaultSingletonTraits<MockStorageClientIDSequencer>; + AtomicSequenceNumber seq_; + + DISALLOW_COPY_AND_ASSIGN(MockStorageClientIDSequencer); +}; + +} // anonymous namespace + +MockStorageClient::MockStorageClient(QuotaManager* qm) + : quota_manager_(qm), + id_(MockStorageClientIDSequencer::GetInstance()->NextMockID()), + runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +MockStorageClient::~MockStorageClient() { + STLDeleteContainerPointers(usage_callbacks_.begin(), usage_callbacks_.end()); + STLDeleteContainerPointers( + origins_callbacks_.begin(), origins_callbacks_.end()); +} + +void MockStorageClient::AddMockOriginData( + const GURL& origin_url, StorageType type, int64 size) { + origin_data_.insert(std::make_pair(origin_url, MockOriginData(type, size))); +} + +void MockStorageClient::ModifyMockOriginDataSize( + const GURL& origin_url, StorageType type, int64 delta) { + std::map<GURL, MockOriginData>::iterator find = origin_data_.find(origin_url); + if (find == origin_data_.end() || find->second.type != type) { + DCHECK(delta >= 0); + AddMockOriginData(origin_url, type, delta); + return; + } + quota_manager_->NotifyStorageModified(id(), origin_url, type, delta); +} + +void MockStorageClient::GetOriginUsage(const GURL& origin_url, + StorageType type, + GetUsageCallback* callback) { + usage_callbacks_.insert(callback); + base::MessageLoopProxy::CreateForCurrentThread()->PostTask( + FROM_HERE, runnable_factory_.NewRunnableMethod( + &MockStorageClient::RunGetOriginUsage, + origin_url, type, callback)); +} + +void MockStorageClient::GetOriginsForType( + StorageType type, GetOriginsCallback* callback) { + origins_callbacks_.insert(callback); + base::MessageLoopProxy::CreateForCurrentThread()->PostTask( + FROM_HERE, runnable_factory_.NewRunnableMethod( + &MockStorageClient::RunGetOriginsForType, + type, callback)); +} + +void MockStorageClient::GetOriginsForHost( + StorageType type, const std::string& host, + GetOriginsCallback* callback) { + origins_callbacks_.insert(callback); + base::MessageLoopProxy::CreateForCurrentThread()->PostTask( + FROM_HERE, runnable_factory_.NewRunnableMethod( + &MockStorageClient::RunGetOriginsForHost, + type, host, callback)); +} + +void MockStorageClient::RunGetOriginUsage( + const GURL& origin_url, StorageType type, GetUsageCallback* callback_ptr) { + usage_callbacks_.erase(callback_ptr); + scoped_ptr<GetUsageCallback> callback(callback_ptr); + std::map<GURL, MockOriginData>::iterator find = origin_data_.find(origin_url); + if (find == origin_data_.end()) { + callback->Run(0); + } else { + callback->Run(find->second.usage); + } +} + +void MockStorageClient::RunGetOriginsForType( + StorageType type, GetOriginsCallback* callback_ptr) { + scoped_ptr<GetOriginsCallback> callback(callback_ptr); + origins_callbacks_.erase(callback_ptr); + std::set<GURL> origins; + for (std::map<GURL, MockOriginData>::iterator iter = origin_data_.begin(); + iter != origin_data_.end(); ++iter) { + if (type == iter->second.type) + origins.insert(iter->first); + } + callback->Run(origins); +} + +void MockStorageClient::RunGetOriginsForHost( + StorageType type, const std::string& host, + GetOriginsCallback* callback_ptr) { + scoped_ptr<GetOriginsCallback> callback(callback_ptr); + origins_callbacks_.erase(callback_ptr); + std::set<GURL> origins; + for (std::map<GURL, MockOriginData>::iterator iter = origin_data_.begin(); + iter != origin_data_.end(); ++iter) { + std::string host_or_spec = net::GetHostOrSpecFromURL(iter->first); + if (type == iter->second.type && host == host_or_spec) + origins.insert(iter->first); + } + callback->Run(origins); +} + +} // namespace quota diff --git a/webkit/quota/mock_storage_client.h b/webkit/quota/mock_storage_client.h new file mode 100644 index 0000000..d7a27cd --- /dev/null +++ b/webkit/quota/mock_storage_client.h @@ -0,0 +1,74 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_MOCK_STORAGE_CLIENT_H_ +#define WEBKIT_QUOTA_MOCK_STORAGE_CLIENT_H_ + +#include <map> + +#include "base/compiler_specific.h" +#include "base/task.h" +#include "googleurl/src/gurl.h" +#include "webkit/quota/quota_client.h" + +namespace quota { + +class QuotaManager; + +// Mock storage class for testing. +class MockStorageClient : public QuotaClient { + public: + MockStorageClient(QuotaManager* qm); + virtual ~MockStorageClient(); + + virtual ID id() const OVERRIDE { return id_; } + + // To add or modify mock data in this client. + void AddMockOriginData(const GURL& origin_url, StorageType type, int64 size); + void ModifyMockOriginDataSize( + const GURL& origin_url, StorageType type, int64 delta); + + typedef QuotaClient::GetUsageCallback GetUsageCallback; + typedef QuotaClient::GetOriginsCallback GetOriginsCallback; + + // QuotaClient methods. + virtual void GetOriginUsage(const GURL& origin_url, + StorageType type, + GetUsageCallback* callback) OVERRIDE; + virtual void GetOriginsForType(StorageType type, + GetOriginsCallback* callback) OVERRIDE; + virtual void GetOriginsForHost(StorageType type, const std::string& host, + GetOriginsCallback* callback) OVERRIDE; + + private: + void RunGetOriginUsage(const GURL& origin_url, + StorageType type, + GetUsageCallback* callback); + void RunGetOriginsForType(StorageType type, + GetOriginsCallback* callback); + void RunGetOriginsForHost(StorageType type, + const std::string& host, + GetOriginsCallback* callback); + + QuotaManager* quota_manager_; + const ID id_; + + struct MockOriginData { + MockOriginData(StorageType type, int64 usage) : type(type), usage(usage) { } + StorageType type; + int64 usage; + }; + std::map<GURL, MockOriginData> origin_data_; + + std::set<GetUsageCallback*> usage_callbacks_; + std::set<GetOriginsCallback*> origins_callbacks_; + + ScopedRunnableMethodFactory<MockStorageClient> runnable_factory_; + + DISALLOW_COPY_AND_ASSIGN(MockStorageClient); +}; + +} // namespace quota + +#endif // WEBKIT_QUOTA_MOCK_STORAGE_H_ diff --git a/webkit/quota/quota_client.h b/webkit/quota/quota_client.h new file mode 100644 index 0000000..1852e27 --- /dev/null +++ b/webkit/quota/quota_client.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_QUOTA_CLIENT_H_ +#define WEBKIT_QUOTA_QUOTA_CLIENT_H_ + +#include <set> +#include <list> + +#include "base/callback.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "webkit/quota/quota_types.h" + +namespace quota { + +// An abstract interface for quota manager clients. +// Each storage API must provide an implementation of this interface and +// register it to the quota manager. +// All the methods are assumed to be called on the IO thread in the browser. +class QuotaClient { + public: + typedef Callback1<int64>::Type GetUsageCallback; + typedef Callback1<const std::set<GURL>&>::Type GetOriginsCallback; + + virtual ~QuotaClient() {} + + enum ID { + kUnknown, + kFileSystem, + kDatabase, + kAppcache, + kIndexedDatabase, + kMockStart, // This needs to be the end of the enum. + }; + + virtual ID id() const = 0; + + // Called by the QuotaManager. + // Gets the amount of data stored in the storage specified by + // |origin_url| and |type|. + virtual void GetOriginUsage(const GURL& origin_url, + StorageType type, + GetUsageCallback* callback) = 0; + + // Called by the QuotaManager. + // Returns a list of origins that has data in the |type| storage. + virtual void GetOriginsForType(StorageType type, + GetOriginsCallback* callback) = 0; + + // Called by the QuotaManager. + // Returns a list of origins that match the |host|. + virtual void GetOriginsForHost(StorageType type, + const std::string& host, + GetOriginsCallback* callback) = 0; +}; + +typedef std::list<QuotaClient*> QuotaClientList; + +} // namespace quota + +#endif // WEBKIT_QUOTA_QUOTA_CLIENT_H_ diff --git a/webkit/quota/quota_database.h b/webkit/quota/quota_database.h index aabb9ac..98aced9 100644 --- a/webkit/quota/quota_database.h +++ b/webkit/quota/quota_database.h @@ -12,6 +12,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/time.h" +#include "webkit/quota/quota_types.h" namespace sql { class Connection; @@ -23,13 +24,6 @@ class GURL; namespace quota { -// TODO(kinuko): put this in a separated header file when we have more modules. -enum StorageType { - kStorageTypeUnknown, - kStorageTypeTemporary, - kStorageTypePersistent, -}; - // All the methods of this class must run on the DB thread. class QuotaDatabase { public: diff --git a/webkit/quota/quota_manager.cc b/webkit/quota/quota_manager.cc new file mode 100644 index 0000000..647131c --- /dev/null +++ b/webkit/quota/quota_manager.cc @@ -0,0 +1,516 @@ +// Copyright (c) 2011 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 <deque> +#include <algorithm> + +#include "base/callback.h" +#include "base/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop_proxy.h" +#include "base/stl_util-inl.h" +#include "base/sys_info.h" +#include "net/base/net_util.h" +#include "webkit/quota/quota_database.h" +#include "webkit/quota/quota_types.h" +#include "webkit/quota/usage_tracker.h" + +using base::ScopedCallbackFactory; + +namespace quota { + +namespace { +// 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) { + int64 free_space = base::SysInfo::AmountOfFreeDiskSpace(path); + // 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; + } + // 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 MBytes = 1024 * 1024; +} // 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 * MBytes; +const int64 QuotaManager::kTemporaryStorageQuotaMaxSize = 1 * 1024 * MBytes; +const char QuotaManager::kDatabaseName[] = "QuotaManager"; + +const int64 QuotaManager::kIncognitoDefaultTemporaryQuota = 5 * MBytes; + +// 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; + + static UsageAndQuotaDispatcherTask* Create( + QuotaManager* manager, const std::string& host, StorageType type); + + // Returns true if it is the first call for this task; which means + // the caller needs to call Start(). + bool AddCallback(GetUsageAndQuotaCallback* callback) { + callbacks_.push_back(callback); + return (callbacks_.size() == 1); + } + + void DidGetGlobalUsage(int64 usage) { + global_usage_ = usage; + CheckCompleted(); + } + + void DidGetHostUsage(const std::string& host_unused, int64 usage) { + host_usage_ = usage; + CheckCompleted(); + } + + void DidGetGlobalQuota(int64 quota) { + quota_ = quota; + CheckCompleted(); + } + + void DidGetHostQuota(const std::string& host_unused, int64 quota) { + quota_ = quota; + CheckCompleted(); + } + + protected: + UsageAndQuotaDispatcherTask( + QuotaManager* manager, + const std::string& host, + StorageType type) + : QuotaTask(manager), + host_(host), + type_(type), + quota_(-1), + global_usage_(-1), + host_usage_(-1), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} + + virtual ~UsageAndQuotaDispatcherTask() { + STLDeleteContainerPointers(callbacks_.begin(), callbacks_.end()); + } + + virtual bool IsCompleted() const = 0; + + virtual void DispatchCallback(GetUsageAndQuotaCallback* callback) = 0; + + virtual void Aborted() OVERRIDE { + for (CallbackList::iterator iter = callbacks_.begin(); + iter != callbacks_.end(); + ++iter) { + (*iter)->Run(kQuotaErrorAbort, 0, 0); + delete *iter; + } + callbacks_.clear(); + delete this; + } + + virtual void Completed() OVERRIDE { delete this; } + QuotaManager* manager() const { + return static_cast<QuotaManager*>(observer()); + } + + std::string host() const { return host_; } + StorageType type() const { return type_; } + int64 quota() const { return quota_; } + int64 global_usage() const { return global_usage_; } + int64 host_usage() const { return host_usage_; } + + UsageCallback* NewGlobalUsageCallback() { + return callback_factory_.NewCallback( + &UsageAndQuotaDispatcherTask::DidGetGlobalUsage); + } + + HostUsageCallback* NewHostUsageCallback() { + return callback_factory_.NewCallback( + &UsageAndQuotaDispatcherTask::DidGetHostUsage); + } + + QuotaCallback* NewGlobalQuotaCallback() { + return callback_factory_.NewCallback( + &UsageAndQuotaDispatcherTask::DidGetGlobalQuota); + } + + HostQuotaCallback* NewHostQuotaCallback() { + return callback_factory_.NewCallback( + &UsageAndQuotaDispatcherTask::DidGetHostQuota); + } + + private: + void CheckCompleted() { + if (IsCompleted()) { + // Dispatches callbacks. + for (CallbackList::iterator iter = callbacks_.begin(); + iter != callbacks_.end(); + ++iter) { + DispatchCallback(*iter); + delete *iter; + } + callbacks_.clear(); + 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_)); + CallCompleted(); + } + } + + const std::string host_; + const StorageType type_; + int64 quota_; + int64 global_usage_; + int64 host_usage_; + CallbackList callbacks_; + ScopedCallbackFactory<UsageAndQuotaDispatcherTask> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaDispatcherTask); +}; + +class QuotaManager::UsageAndQuotaDispatcherTaskForTemporary + : public QuotaManager::UsageAndQuotaDispatcherTask { + public: + UsageAndQuotaDispatcherTaskForTemporary( + QuotaManager* manager, const std::string host) + : UsageAndQuotaDispatcherTask(manager, host, kStorageTypeTemporary) {} + + protected: + virtual void Run() OVERRIDE { + manager()->temporary_usage_tracker_->GetGlobalUsage( + NewGlobalUsageCallback()); + manager()->temporary_usage_tracker_->GetHostUsage( + host(), NewHostUsageCallback()); + manager()->GetTemporaryGlobalQuota(NewGlobalQuotaCallback()); + } + + virtual bool IsCompleted() const OVERRIDE { + return (quota() >= 0 && global_usage() >= 0 && host_usage() >= 0); + } + + virtual void DispatchCallback(GetUsageAndQuotaCallback* callback) OVERRIDE { + // TODO(kinuko): For now it returns pessimistic quota. Change this + // to return {usage, quota - nonevictable_usage} once eviction is + // supported. + int64 other_usage = global_usage() - host_usage(); + callback->Run(kQuotaStatusOk, host_usage(), quota() - other_usage); + } +}; + +class QuotaManager::UsageAndQuotaDispatcherTaskForPersistent + : public QuotaManager::UsageAndQuotaDispatcherTask { + public: + UsageAndQuotaDispatcherTaskForPersistent( + QuotaManager* manager, const std::string host) + : UsageAndQuotaDispatcherTask(manager, host, kStorageTypePersistent) {} + + protected: + virtual void Run() OVERRIDE { + manager()->persistent_usage_tracker_->GetHostUsage( + host(), NewHostUsageCallback()); + manager()->GetPersistentHostQuota( + host(), NewHostQuotaCallback()); + } + + virtual bool IsCompleted() const OVERRIDE { + return (quota() >= 0 && host_usage() >= 0); + } + + virtual void DispatchCallback(GetUsageAndQuotaCallback* callback) OVERRIDE { + callback->Run(kQuotaStatusOk, host_usage(), quota()); + } +}; + +// static +QuotaManager::UsageAndQuotaDispatcherTask* +QuotaManager::UsageAndQuotaDispatcherTask::Create( + QuotaManager* manager, const std::string& host, StorageType type) { + switch (type) { + case kStorageTypeTemporary: + return new UsageAndQuotaDispatcherTaskForTemporary( + manager, host); + case kStorageTypePersistent: + return new UsageAndQuotaDispatcherTaskForPersistent( + manager, host); + default: + NOTREACHED(); + } + return NULL; +} + +class QuotaManager::InitializeTask : public QuotaThreadTask { + public: + InitializeTask( + QuotaManager* manager, + QuotaDatabase* database, + scoped_refptr<base::MessageLoopProxy> db_message_loop, + const FilePath& profile_path) + : QuotaThreadTask(manager, db_message_loop), + manager_(manager), + database_(database), + profile_path_(profile_path), + temporary_storage_quota_(-1), + db_disabled_(false) { + DCHECK(database_); + } + + 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_); + if (!database_->SetGlobalQuota( + kStorageTypeTemporary, temporary_storage_quota_)) { + db_disabled_ = true; + } + } + } + + virtual void Completed() OVERRIDE { + DCHECK(manager_); + if (manager_->temporary_global_quota_ < 0) + manager_->DidGetTemporaryGlobalQuota(temporary_storage_quota_); + manager_->db_initialized_ = !db_disabled_; + manager_->db_disabled_ = db_disabled_; + } + + private: + QuotaManager* manager_; + QuotaDatabase* database_; + FilePath profile_path_; + int64 temporary_storage_quota_; + bool db_disabled_; +}; + +class QuotaManager::TemporaryGlobalQuotaUpdateTask : public QuotaThreadTask { + public: + TemporaryGlobalQuotaUpdateTask( + QuotaManager* manager, + QuotaDatabase* database, + scoped_refptr<base::MessageLoopProxy> db_message_loop, + int64 new_quota) + : QuotaThreadTask(manager, db_message_loop), + manager_(manager), + database_(database), + new_quota_(new_quota), + db_disabled_(false) { + DCHECK(database_); + } + + protected: + virtual void RunOnTargetThread() OVERRIDE { + if (!database_->SetGlobalQuota(kStorageTypeTemporary, new_quota_)) + db_disabled_ = true; + } + + virtual void Completed() OVERRIDE { + DCHECK(manager_); + manager_->db_disabled_ = db_disabled_; + } + + private: + QuotaManager* manager_; + QuotaDatabase* database_; + int64 new_quota_; + bool db_disabled_; +}; + +QuotaManager::QuotaManager(bool is_incognito, + const FilePath& profile_path, + scoped_refptr<base::MessageLoopProxy> io_thread, + scoped_refptr<base::MessageLoopProxy> db_thread) + : is_incognito_(is_incognito), + profile_path_(profile_path), + db_initialized_(false), + db_disabled_(false), + io_thread_(io_thread), + db_thread_(db_thread), + temporary_global_quota_(-1), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +QuotaManager::~QuotaManager() { + STLDeleteContainerPointers(clients_.begin(), clients_.end()); +} + +void QuotaManager::RegisterClient(QuotaClient* client) { + DCHECK(!database_.get()); + clients_.push_back(client); +} + +void QuotaManager::GetUsageAndQuota( + const GURL& origin, StorageType type, + GetUsageAndQuotaCallback* callback_ptr) { + scoped_ptr<GetUsageAndQuotaCallback> callback(callback_ptr); + LazyInitialize(); + if (is_incognito_) { + int64 quota = 0; + if (type == kStorageTypeTemporary) + quota = clients_.size() * kIncognitoDefaultTemporaryQuota; + // TODO(kinuko): This does not return useful usage value for now. + callback->Run(kQuotaStatusOk, 0, quota); + return; + } + + 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())) + found->second->Start(); +} + +void QuotaManager::RequestQuota( + const GURL& origin, StorageType type, + int64 requested_size, + RequestQuotaCallback* callback) { + LazyInitialize(); + // TODO(kinuko): implement me. + callback->Run(kQuotaErrorNotSupported, 0); + delete callback; +} + +void QuotaManager::NotifyStorageModified( + QuotaClient::ID client_id, + const GURL& origin, StorageType type, int64 delta) { + LazyInitialize(); + UsageTracker* tracker = GetUsageTracker(type); + DCHECK(tracker); + tracker->UpdateUsageCache(client_id, origin, delta); +} + +void QuotaManager::GetTemporaryGlobalQuota(QuotaCallback* callback) { + LazyInitialize(); + if (temporary_global_quota_ >= 0) { + // TODO(kinuko): The in-memory quota value should be periodically + // updated not to exceed the current available space in the hard drive. + callback->Run(temporary_global_quota_); + delete callback; + return; + } + // They are called upon completion of InitializeTask. + temporary_global_quota_callbacks_.Add(callback); +} + +void QuotaManager::SetTemporaryGlobalQuota(int64 new_quota) { + LazyInitialize(); + DCHECK(new_quota >= 0); + DidGetTemporaryGlobalQuota(new_quota); + if (!db_disabled_) { + scoped_refptr<TemporaryGlobalQuotaUpdateTask> task( + new TemporaryGlobalQuotaUpdateTask( + this, database_.get(), db_thread_, new_quota)); + task->Start(); + } +} + +void QuotaManager::GetPersistentHostQuota(const std::string& host, + HostQuotaCallback* callback) { + LazyInitialize(); + std::map<std::string, int64>::iterator found = + persistent_host_quota_.find(host); + if (found != persistent_host_quota_.end()) { + callback->Run(host, found->second); + delete callback; + return; + } + if (persistent_host_quota_callbacks_.Add(host, callback)) { + // This is the first call for this host. + // TODO(kinuko): Dispatch a task to get the host quota for the host + // once QuotaDatabase is updated to accept hosts instead of origins. + } +} + +void QuotaManager::SetPersistentHostQuota(const std::string& host, + int64 new_quota) { + LazyInitialize(); + // TODO(kinuko): Implement once QuotaDatabase is updated. +} + +void QuotaManager::LazyInitialize() { + DCHECK(io_thread_->BelongsToCurrentThread()); + if (database_.get()) { + // Initialization seems to be done already. + return; + } + + if (is_incognito_) + return; + + database_.reset(new QuotaDatabase(profile_path_.AppendASCII(kDatabaseName))); + + temporary_usage_tracker_.reset( + new UsageTracker(clients_, kStorageTypeTemporary)); + persistent_usage_tracker_.reset( + new UsageTracker(clients_, kStorageTypePersistent)); + + scoped_refptr<InitializeTask> task( + new InitializeTask(this, database_.get(), db_thread_, profile_path_)); + task->Start(); +} + +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::DidGetTemporaryGlobalQuota(int64 quota) { + temporary_global_quota_ = quota; + temporary_global_quota_callbacks_.Run(quota); +} + +void QuotaManager::DidGetPersistentHostQuota(const std::string& host, + int64 quota) { + DCHECK(persistent_host_quota_.find(host) == persistent_host_quota_.end()); + persistent_host_quota_[host] = quota; + persistent_host_quota_callbacks_.Run(host, quota); +} + +void QuotaManager::DeleteOnCorrectThread() const { + if (database_.get()) { + db_thread_->DeleteSoon(FROM_HERE, database_.release()); + } + if (!io_thread_->BelongsToCurrentThread()) { + io_thread_->DeleteSoon(FROM_HERE, this); + return; + } + delete this; +} + +} // namespace quota diff --git a/webkit/quota/quota_manager.h b/webkit/quota/quota_manager.h new file mode 100644 index 0000000..a9a458c --- /dev/null +++ b/webkit/quota/quota_manager.h @@ -0,0 +1,149 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_QUOTA_MANAGER_H_ +#define WEBKIT_QUOTA_QUOTA_MANAGER_H_ +#pragma once + +#include <deque> +#include <list> +#include <map> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/file_path.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/ref_counted.h" +#include "webkit/quota/quota_client.h" +#include "webkit/quota/quota_task.h" +#include "webkit/quota/quota_types.h" + +class FilePath; + +namespace quota { + +class QuotaDatabase; +class UsageTracker; + +struct QuotaManagerDeleter; + +// The quota manager class. This class is instantiated per profile and +// held by the profile. +class QuotaManager : public QuotaTaskObserver, + public base::RefCountedThreadSafe< + QuotaManager, QuotaManagerDeleter> { + public: + typedef Callback3<QuotaStatusCode, + int64 /* usage */, + int64 /* quota */>::Type GetUsageAndQuotaCallback; + typedef Callback2<QuotaStatusCode, + int64 /* granted_quota */>::Type RequestQuotaCallback; + + QuotaManager(bool is_incognito, + const FilePath& profile_path, + scoped_refptr<base::MessageLoopProxy> io_thread, + scoped_refptr<base::MessageLoopProxy> db_thread); + + virtual ~QuotaManager(); + + // The client's ownership is transferred to the manager. + void RegisterClient(QuotaClient* client); + + // Called by clients or webapps on the IO thread. + void GetUsageAndQuota(const GURL& origin, StorageType type, + GetUsageAndQuotaCallback* callback); + + // Called by webapps on the IO thread. + void RequestQuota(const GURL& origin, StorageType type, + int64 requested_size, + RequestQuotaCallback* callback); + + // Called by clients on the IO thread. + // QuotaClients must call this method whenever they have made any + // modifications that change the amount of data stored in their storage. + void NotifyStorageModified(QuotaClient::ID client_id, + const GURL& origin, + StorageType type, + int64 delta); + + // Called by UI and internal modules. + void GetTemporaryGlobalQuota(QuotaCallback* callback); + void SetTemporaryGlobalQuota(int64 new_quota); + void GetPersistentHostQuota(const std::string& host, + HostQuotaCallback* callback); + void SetPersistentHostQuota(const std::string& host, int64 new_quota); + + // TODO(kinuko): Add more APIs for UI: + // - Get temporary global/per-host usage + // - Get persistent global/per-host usage + + const static int64 kTemporaryStorageQuotaDefaultSize; + const static int64 kTemporaryStorageQuotaMaxSize; + const static char kDatabaseName[]; + + const static int64 kIncognitoDefaultTemporaryQuota; + + private: + class InitializeTask; + class TemporaryGlobalQuotaUpdateTask; + + class UsageAndQuotaDispatcherTask; + class UsageAndQuotaDispatcherTaskForTemporary; + class UsageAndQuotaDispatcherTaskForPersistent; + + typedef std::pair<std::string, StorageType> HostAndType; + typedef std::map<HostAndType, UsageAndQuotaDispatcherTask*> + UsageAndQuotaDispatcherTaskMap; + + // This initialization method is lazily called on the IO thread + // when the first quota manager API is called. + // Initialize must be called after all quota clients are added to the + // manager by RegisterStorage. + void LazyInitialize(); + + UsageTracker* GetUsageTracker(StorageType type) const; + + void DidGetTemporaryGlobalQuota(int64 quota); + void DidGetPersistentHostQuota(const std::string& host, int64 quota); + + friend struct QuotaManagerDeleter; + void DeleteOnCorrectThread() const; + + const bool is_incognito_; + const FilePath profile_path_; + + bool db_initialized_; + bool db_disabled_; + scoped_refptr<base::MessageLoopProxy> io_thread_; + scoped_refptr<base::MessageLoopProxy> db_thread_; + mutable scoped_ptr<QuotaDatabase> database_; + + QuotaClientList clients_; + + scoped_ptr<UsageTracker> temporary_usage_tracker_; + scoped_ptr<UsageTracker> persistent_usage_tracker_; + + UsageAndQuotaDispatcherTaskMap usage_and_quota_dispatchers_; + + int64 temporary_global_quota_; + QuotaCallbackQueue temporary_global_quota_callbacks_; + + std::map<std::string, int64> persistent_host_quota_; + HostQuotaCallbackMap persistent_host_quota_callbacks_; + + base::ScopedCallbackFactory<QuotaManager> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuotaManager); +}; + +struct QuotaManagerDeleter { + static void Destruct(const QuotaManager* manager) { + manager->DeleteOnCorrectThread(); + } +}; + +} // namespace quota + +#endif // WEBKIT_QUOTA_QUOTA_MANAGER_H_ diff --git a/webkit/quota/quota_manager_unittest.cc b/webkit/quota/quota_manager_unittest.cc new file mode 100644 index 0000000..47a4c27 --- /dev/null +++ b/webkit/quota/quota_manager_unittest.cc @@ -0,0 +1,295 @@ +// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h" + +#include <vector> + +#include "base/file_util.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.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_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 { + +namespace { +struct MockOriginData { + const char* origin; + StorageType type; + int64 usage; +}; +} // anonymous namespace + +class QuotaManagerTest : public testing::Test { + public: + QuotaManagerTest() + : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + } + + void SetUp() { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + quota_manager_ = new QuotaManager( + false /* is_incognito */, + data_dir_.path(), + MessageLoopProxy::CreateForCurrentThread(), + MessageLoopProxy::CreateForCurrentThread()); + additional_callback_count_ = 0; + } + + void TearDown() { + // Make sure the quota manager cleans up correctly. + quota_manager_ = NULL; + MessageLoop::current()->RunAllPending(); + } + + protected: + MockStorageClient* CreateClient( + const MockOriginData* mock_data, size_t mock_data_size) { + MockStorageClient* client( + new MockStorageClient(quota_manager_.get())); + for (size_t i = 0; i < mock_data_size; ++i) { + client->AddMockOriginData(GURL(mock_data[i].origin), + mock_data[i].type, + mock_data[i].usage); + } + return client; + } + + void RegisterClient(MockStorageClient* client) { + quota_manager_->RegisterClient(client); + } + + void GetUsageAndQuota(const GURL& origin, StorageType type) { + status_ = kQuotaStatusUnknown; + usage_ = -1; + quota_ = -1; + quota_manager_->GetUsageAndQuota(origin, type, + callback_factory_.NewCallback( + &QuotaManagerTest::DidGetUsageAndQuota)); + } + + void RunAdditionalUsageAndQuotaTask(const GURL& origin, StorageType type) { + quota_manager_->GetUsageAndQuota(origin, type, + callback_factory_.NewCallback( + &QuotaManagerTest::DidGetUsageAndQuotaAdditional)); + } + + void DidGetUsageAndQuota(QuotaStatusCode status, int64 usage, int64 quota) { + status_ = status; + usage_ = usage; + quota_ = quota; + } + + void set_additional_callback_count(int c) { additional_callback_count_ = c; } + int additional_callback_count() const { return additional_callback_count_; } + void DidGetUsageAndQuotaAdditional( + QuotaStatusCode status, int64 usage, int64 quota) { + ++additional_callback_count_; + } + + QuotaManager* quota_manager() const { return quota_manager_.get(); } + void set_quota_manager(QuotaManager* quota_manager) { + quota_manager_ = quota_manager; + } + + QuotaStatusCode status() const { return status_; } + int64 usage() const { return usage_; } + int64 quota() const { return quota_; } + + private: + ScopedTempDir data_dir_; + base::ScopedCallbackFactory<QuotaManagerTest> callback_factory_; + + scoped_refptr<QuotaManager> quota_manager_; + + QuotaStatusCode status_; + int64 usage_; + int64 quota_; + + int additional_callback_count_; + + DISALLOW_COPY_AND_ASSIGN(QuotaManagerTest); +}; + +TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Simple) { + const static MockOriginData kData[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://foo.com/", kStorageTypePersistent, 80 }, + }; + RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10, usage()); + EXPECT_GT(quota(), 0); + EXPECT_LE(quota(), QuotaManager::kTemporaryStorageQuotaMaxSize); + int64 quota_returned_for_foo = quota(); + + GetUsageAndQuota(GURL("http://bar.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(0, usage()); + EXPECT_EQ(quota_returned_for_foo - 10, quota()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsage_NoClient) { + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(0, usage()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsage_EmptyClient) { + RegisterClient(CreateClient(NULL, 0)); + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(0, usage()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) { + const static MockOriginData kData[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://foo.com:8080/", kStorageTypeTemporary, 20 }, + { "http://bar.com/", kStorageTypeTemporary, 5 }, + { "https://bar.com/", kStorageTypeTemporary, 7 }, + { "http://baz.com/", kStorageTypeTemporary, 30 }, + { "http://foo.com/", kStorageTypePersistent, 40 }, + }; + RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); + + // This time explicitly sets a temporary global quota. + quota_manager()->SetTemporaryGlobalQuota(100); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 20, usage()); + + // The returned quota must be equal to (global_quota - other_origins_usage). + EXPECT_EQ(100 - (5 + 7 + 30), quota()); + + GetUsageAndQuota(GURL("http://bar.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(5 + 7, usage()); + EXPECT_EQ(100 - (10 + 20 + 30), quota()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsage_MultipleClients) { + const static MockOriginData kData1[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://bar.com/", kStorageTypeTemporary, 20 }, + }; + const static MockOriginData kData2[] = { + { "https://foo.com/", kStorageTypeTemporary, 30 }, + { "http://example.com/", kStorageTypePersistent, 40 }, + }; + RegisterClient(CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1))); + RegisterClient(CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2))); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 30, usage()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsage_WithModify) { + const static MockOriginData kData[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://foo.com:1/", kStorageTypeTemporary, 20 }, + }; + MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData)); + RegisterClient(client); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 20, usage()); + + client->ModifyMockOriginDataSize( + GURL("http://foo.com/"), kStorageTypeTemporary, 20); + client->ModifyMockOriginDataSize( + GURL("http://foo.com:1/"), kStorageTypeTemporary, -5); + client->ModifyMockOriginDataSize( + GURL("http://bar.com/"), kStorageTypeTemporary, 33); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 20 + 20 - 5, usage()); + + GetUsageAndQuota(GURL("http://bar.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(33, usage()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) { + const static MockOriginData kData[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://foo.com:8080/", kStorageTypeTemporary, 20 }, + { "http://bar.com/", kStorageTypeTemporary, 13 }, + { "http://foo.com/", kStorageTypePersistent, 40 }, + }; + RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); + quota_manager()->SetTemporaryGlobalQuota(100); + + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 20, usage()); + EXPECT_EQ(100 - 13, quota()); + + set_additional_callback_count(0); + RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"), + kStorageTypeTemporary); + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), + kStorageTypeTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaStatusOk, status()); + EXPECT_EQ(10 + 20, usage()); + EXPECT_EQ(100 - 13, quota()); + EXPECT_EQ(2, additional_callback_count()); +} + +TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) { + const static MockOriginData kData[] = { + { "http://foo.com/", kStorageTypeTemporary, 10 }, + { "http://foo.com:8080/", kStorageTypeTemporary, 20 }, + { "http://bar.com/", kStorageTypeTemporary, 13 }, + { "http://foo.com/", kStorageTypePersistent, 40 }, + }; + RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData))); + quota_manager()->SetTemporaryGlobalQuota(100); + + set_additional_callback_count(0); + GetUsageAndQuota(GURL("http://foo.com/"), kStorageTypeTemporary); + RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"), + kStorageTypeTemporary); + RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), + kStorageTypeTemporary); + + // Nuke before waiting for callbacks. + set_quota_manager(NULL); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(kQuotaErrorAbort, status()); +} + +} // namespace quota diff --git a/webkit/quota/quota_task.cc b/webkit/quota/quota_task.cc new file mode 100644 index 0000000..974e9b4 --- /dev/null +++ b/webkit/quota/quota_task.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2011 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_task.h" + +#include <algorithm> +#include <functional> + +#include "base/message_loop_proxy.h" + +using base::MessageLoopProxy; + +namespace quota { + +// QuotaTask --------------------------------------------------------------- + +QuotaTask::QuotaTask(QuotaTaskObserver* observer) + : observer_(observer), + original_message_loop_(MessageLoopProxy::CreateForCurrentThread()) { +} + +QuotaTask::~QuotaTask() { +} + +void QuotaTask::Start() { + DCHECK(observer_); + observer()->RegisterTask(this); + Run(); +} + +void QuotaTask::CallCompleted() { + DCHECK(original_message_loop_->BelongsToCurrentThread()); + if (observer_) { + observer_->UnregisterTask(this); + Completed(); + } +} + +void QuotaTask::Abort() { + DCHECK(original_message_loop_->BelongsToCurrentThread()); + observer_ = NULL; + Aborted(); +} + +// QuotaThreadTask --------------------------------------------------------- + +QuotaThreadTask::QuotaThreadTask( + QuotaTaskObserver* observer, + scoped_refptr<MessageLoopProxy> target_message_loop) + : QuotaTask(observer), + target_message_loop_(target_message_loop) { +} + +QuotaThreadTask::~QuotaThreadTask() { +} + +void QuotaThreadTask::Run() { + target_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &QuotaThreadTask::CallRunOnTargetThread)); +} + +void QuotaThreadTask::CallRunOnTargetThread() { + DCHECK(target_message_loop_->BelongsToCurrentThread()); + RunOnTargetThread(); + original_message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(this, &QuotaThreadTask::CallCompleted)); +} + +// QuotaTaskObserver ------------------------------------------------------- + +QuotaTaskObserver::~QuotaTaskObserver() { + std::for_each(running_quota_tasks_.begin(), + running_quota_tasks_.end(), + std::mem_fun(&QuotaTask::Abort)); +} + +void QuotaTaskObserver::RegisterTask(QuotaTask* task) { + running_quota_tasks_.insert(task); +} + +void QuotaTaskObserver::UnregisterTask(QuotaTask* task) { + DCHECK(running_quota_tasks_.find(task) != running_quota_tasks_.end()); + running_quota_tasks_.erase(task); +} + +} // namespace quota diff --git a/webkit/quota/quota_task.h b/webkit/quota/quota_task.h new file mode 100644 index 0000000..23d7225 --- /dev/null +++ b/webkit/quota/quota_task.h @@ -0,0 +1,97 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_QUOTA_TASK_H_ +#define WEBKIT_QUOTA_QUOTA_TASK_H_ +#pragma once + +#include <set> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" + +namespace base { +class MessageLoopProxy; +} + +namespace quota { + +class QuotaTaskObserver; + +// A base class for quota tasks. +class QuotaTask { + public: + void Start(); + + protected: + QuotaTask(QuotaTaskObserver* observer); + virtual ~QuotaTask(); + + // The task body. + virtual void Run() = 0; + + // Called upon completion, on the original message loop. + virtual void Completed() = 0; + + // Called when the task is aborted. + virtual void Aborted() {} + + void CallCompleted(); + QuotaTaskObserver* observer() const { return observer_; } + scoped_refptr<base::MessageLoopProxy> original_message_loop() const { + return original_message_loop_; + } + + private: + friend class QuotaTaskObserver; + void Abort(); + QuotaTaskObserver* observer_; + scoped_refptr<base::MessageLoopProxy> original_message_loop_; +}; + +// For tasks that post tasks to the other thread. +class QuotaThreadTask : public QuotaTask, + public base::RefCountedThreadSafe<QuotaThreadTask> { + public: + QuotaThreadTask(QuotaTaskObserver* observer, + scoped_refptr<base::MessageLoopProxy> target_message_loop); + + protected: + virtual ~QuotaThreadTask(); + + // Called on the target message loop. + virtual void RunOnTargetThread() = 0; + + virtual void Run() OVERRIDE; + scoped_refptr<base::MessageLoopProxy> target_message_loop() const { + return target_message_loop_; + } + + private: + friend class base::RefCountedThreadSafe<QuotaThreadTask>; + friend class QuotaTaskObserver; + void CallRunOnTargetThread(); + + scoped_refptr<base::MessageLoopProxy> target_message_loop_; +}; + +class QuotaTaskObserver { + public: + virtual ~QuotaTaskObserver(); + + protected: + friend class QuotaTask; + friend class QuotaThreadTask; + + void RegisterTask(QuotaTask* task); + void UnregisterTask(QuotaTask* task); + + typedef std::set<QuotaTask*> TaskSet; + TaskSet running_quota_tasks_; +}; + +} + +#endif // WEBKIT_QUOTA_QUOTA_TASK_H_ diff --git a/webkit/quota/quota_types.h b/webkit/quota/quota_types.h new file mode 100644 index 0000000..9c67b34 --- /dev/null +++ b/webkit/quota/quota_types.h @@ -0,0 +1,141 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_QUOTA_TYPES_H_ +#define WEBKIT_QUOTA_QUOTA_TYPES_H_ +#pragma once + +#include <deque> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/stl_util-inl.h" + +namespace quota { + +enum StorageType { + kStorageTypeTemporary, + kStorageTypePersistent, + kStorageTypeUnknown, +}; + +enum QuotaStatusCode { + kQuotaStatusOk = 0, + kQuotaErrorNotSupported = 9, + kQuotaErrorAbort = 20, + kQuotaStatusUnknown = -1, +}; + +// Common callback types that are used throughout in the quota module. +typedef Callback1<int64>::Type UsageCallback; +typedef Callback1<int64>::Type QuotaCallback; +typedef Callback2<const std::string& /* host */, + int64>::Type HostUsageCallback; +typedef Callback2<const std::string& /* host */, + int64>::Type HostQuotaCallback; + +// Simple template wrapper for a callback queue. +template <typename CallbackType> +class CallbackQueueBase { + public: + typedef typename std::deque<CallbackType> Queue; + typedef typename Queue::iterator iterator; + + virtual ~CallbackQueueBase() { + STLDeleteContainerPointers(callbacks_.begin(), callbacks_.end()); + } + + // Returns true if the given |callback| is the first one added to the queue. + bool Add(CallbackType callback) { + callbacks_.push_back(callback); + return (callbacks_.size() == 1); + } + + bool HasCallbacks() const { + return !callbacks_.empty(); + } + + protected: + std::deque<CallbackType> callbacks_; +}; + +template <typename CallbackType1, typename A1> +class CallbackQueue1 : public CallbackQueueBase<CallbackType1> { + public: + typedef typename CallbackQueueBase<CallbackType1>::Queue Queue; + // Runs the callbacks added to the queue and clears the queue. + void Run(A1 arg) { + for (typename Queue::iterator iter = this->callbacks_.begin(); + iter != this->callbacks_.end(); ++iter) { + (*iter)->Run(arg); + delete *iter; + } + this->callbacks_.clear(); + } +}; + +template <typename CallbackType2, typename A1, typename A2> +class CallbackQueue2 : public CallbackQueueBase<CallbackType2> { + public: + typedef typename CallbackQueueBase<CallbackType2>::Queue Queue; + // Runs the callbacks added to the queue and clears the queue. + void Run(A1 arg1, A2 arg2) { + for (typename Queue::iterator iter = this->callbacks_.begin(); + iter != this->callbacks_.end(); ++iter) { + (*iter)->Run(arg1, arg2); + delete *iter; + } + this->callbacks_.clear(); + } +}; + +typedef CallbackQueue1<UsageCallback*, int64> UsageCallbackQueue; +typedef CallbackQueue1<QuotaCallback*, int64> QuotaCallbackQueue; + +template <typename CallbackType2, typename KEY = std::string, + typename ARG = int64> +class CallbackQueueMap { + public: + typedef CallbackQueue2<CallbackType2, const KEY&, ARG> Queue; + typedef std::map<KEY, Queue> CallbackMap; + typedef typename CallbackMap::iterator iterator; + + bool Add(const KEY& key, CallbackType2 callback) { + return callback_map_[key].Add(callback); + } + + bool HasCallbacks(const KEY& key) const { + return (callback_map_.find(key) != callback_map_.end()); + } + + // Runs the callbacks added for the given |key| and clears the key + // from the map. + void Run(const KEY& key, ARG arg) { + if (!HasCallbacks(key)) + return; + Queue& queue = callback_map_[key]; + queue.Run(key, arg); + callback_map_.erase(key); + } + + iterator Begin() { return callback_map_.begin(); } + iterator End() { return callback_map_.end(); } + static void RunAt(iterator iter, ARG arg) { + iter->second.Run(iter->first, arg); + } + + void Clear() { callback_map_.clear(); } + + private: + CallbackMap callback_map_; +}; + +typedef CallbackQueueMap<HostUsageCallback*> HostUsageCallbackMap; +typedef CallbackQueueMap<HostQuotaCallback*> HostQuotaCallbackMap; + +} // namespace quota + +#endif // WEBKIT_QUOTA_QUOTA_TYPES_H_ diff --git a/webkit/quota/usage_tracker.cc b/webkit/quota/usage_tracker.cc new file mode 100644 index 0000000..043373e --- /dev/null +++ b/webkit/quota/usage_tracker.cc @@ -0,0 +1,400 @@ +// Copyright (c) 2011 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/usage_tracker.h" + +#include <deque> + +#include "base/message_loop_proxy.h" +#include "base/stl_util-inl.h" +#include "net/base/net_util.h" + +namespace quota { + +// A task class for getting the total amount of data used for a collection of +// origins. This class is self-destructed. +class ClientUsageTracker::GatherUsageTaskBase : public QuotaTask { + public: + GatherUsageTaskBase( + UsageTracker* tracker, + QuotaClient* client) + : QuotaTask(tracker), + client_(client), + tracker_(tracker), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + DCHECK(tracker_); + DCHECK(client_); + client_tracker_ = tracker_->GetClientTracker(client_->id()); + DCHECK(client_tracker_); + } + virtual ~GatherUsageTaskBase() {} + + // Get total usage for the given |origins|. + void GetUsageForOrigins(const std::set<GURL>& origins) { + DCHECK(original_message_loop()->BelongsToCurrentThread()); + std::set<GURL> origins_to_process; + // We do not get usage for origins for which we have valid usage cache. + client_tracker()->DetermineOriginsToGetUsage(origins, &origins_to_process); + if (origins_to_process.empty()) { + // Nothing to be done. + CallCompleted(); + delete this; + } + for (std::set<GURL>::const_iterator iter = origins_to_process.begin(); + iter != origins_to_process.end(); + iter++) { + pending_origins_.push_back(*iter); + client_->GetOriginUsage( + *iter, + tracker_->type(), + callback_factory_.NewCallback(&GatherUsageTaskBase::DidGetUsage)); + } + } + + bool IsOriginDone(const GURL& origin) const { + DCHECK(original_message_loop()->BelongsToCurrentThread()); + return origin_usage_map_.find(origin) != origin_usage_map_.end(); + } + + protected: + virtual void Aborted() OVERRIDE { + delete this; + } + + UsageTracker* tracker() const { return tracker_; } + ClientUsageTracker* client_tracker() const { return client_tracker_; } + const std::map<GURL, int64>& origin_usage_map() const { + return origin_usage_map_; + } + + private: + void DidGetUsage(int64 usage) { + // This code assumes DidGetUsage callbacks are called in the same + // order as we dispatched GetOriginUsage calls. + DCHECK(original_message_loop()->BelongsToCurrentThread()); + DCHECK(!pending_origins_.empty()); + origin_usage_map_.insert(std::make_pair( + pending_origins_.front(), usage)); + pending_origins_.pop_front(); + if (pending_origins_.empty()) { + // We're done. + CallCompleted(); + delete this; + } + } + + QuotaClient* client_; + UsageTracker* tracker_; + ClientUsageTracker* client_tracker_; + std::deque<GURL> pending_origins_; + std::map<GURL, int64> origin_usage_map_; + base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(GatherUsageTaskBase); +}; + +// A task class for getting the total amount of data used for a given storage +// type. This class is self-destructed. +class ClientUsageTracker::GatherGlobalUsageTask + : public GatherUsageTaskBase { + public: + GatherGlobalUsageTask( + UsageTracker* tracker, + QuotaClient* client) + : GatherUsageTaskBase(tracker, client), + client_(client), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + DCHECK(tracker); + DCHECK(client); + } + virtual ~GatherGlobalUsageTask() {} + + protected: + virtual void Run() OVERRIDE { + client_->GetOriginsForType(tracker()->type(), + callback_factory_.NewCallback( + &GatherUsageTaskBase::GetUsageForOrigins)); + } + + virtual void Completed() OVERRIDE { + client_tracker()->DidGetGlobalUsage(origin_usage_map()); + } + +private: + QuotaClient* client_; + base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(GatherGlobalUsageTask); +}; + +// A task class for getting the total amount of data used for a given host. +// This class is self-destructed. +class ClientUsageTracker::GatherHostUsageTask + : public GatherUsageTaskBase { + public: + GatherHostUsageTask( + UsageTracker* tracker, + QuotaClient* client, + const std::string& host) + : GatherUsageTaskBase(tracker, client), + client_(client), + host_(host), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + DCHECK(client_); + } + virtual ~GatherHostUsageTask() {} + + protected: + virtual void Run() OVERRIDE { + client_->GetOriginsForHost(tracker()->type(), host_, + callback_factory_.NewCallback( + &GatherUsageTaskBase::GetUsageForOrigins)); + } + + virtual void Completed() OVERRIDE { + client_tracker()->DidGetHostUsage(host_, origin_usage_map()); + } + + private: + QuotaClient* client_; + std::string host_; + base::ScopedCallbackFactory<GatherUsageTaskBase> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(GatherHostUsageTask); +}; + +// UsageTracker ---------------------------------------------------------- + +UsageTracker::UsageTracker(const QuotaClientList& clients, StorageType type) + : type_(type), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + for (QuotaClientList::const_iterator iter = clients.begin(); + iter != clients.end(); + ++iter) { + client_tracker_map_.insert(std::make_pair( + (*iter)->id(), new ClientUsageTracker(this, *iter))); + } +} + +UsageTracker::~UsageTracker() { + STLDeleteValues(&client_tracker_map_); +} + +ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) { + ClientTrackerMap::iterator found = client_tracker_map_.find(client_id); + if (found != client_tracker_map_.end()) + return found->second; + return NULL; +} + +void UsageTracker::GetGlobalUsage(UsageCallback* callback) { + if (client_tracker_map_.size() == 0) { + // No clients registered. + callback->Run(0); + delete callback; + return; + } + if (global_usage_callbacks_.Add(callback)) { + // This is the first call. Asks each ClientUsageTracker to collect + // usage information. + global_usage_.pending_clients = client_tracker_map_.size(); + global_usage_.usage = 0; + for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); + iter != client_tracker_map_.end(); + ++iter) { + iter->second->GetGlobalUsage(callback_factory_.NewCallback( + &UsageTracker::DidGetClientGlobalUsage)); + } + } +} + +void UsageTracker::GetHostUsage( + const std::string& host, HostUsageCallback* callback) { + if (client_tracker_map_.size() == 0) { + // No clients registered. + callback->Run(host, 0); + delete callback; + return; + } + if (host_usage_callbacks_.Add(host, callback)) { + // This is the first call for the given host. + DCHECK(outstanding_host_usage_.find(host) == outstanding_host_usage_.end()); + outstanding_host_usage_[host].pending_clients = client_tracker_map_.size(); + for (ClientTrackerMap::iterator iter = client_tracker_map_.begin(); + iter != client_tracker_map_.end(); + ++iter) { + iter->second->GetHostUsage(host, callback_factory_.NewCallback( + &UsageTracker::DidGetClientHostUsage)); + } + } +} + +void UsageTracker::UpdateUsageCache( + QuotaClient::ID client_id, const GURL& origin, int64 delta) { + ClientUsageTracker* client_tracker = GetClientTracker(client_id); + DCHECK(client_tracker); + client_tracker->UpdateUsageCache(origin, delta); +} + +void UsageTracker::DidGetClientGlobalUsage(int64 usage) { + global_usage_.usage += usage; + if (--global_usage_.pending_clients == 0) { + // All the clients have returned their usage data. Dispatches the + // pending callbacks. + global_usage_callbacks_.Run(global_usage_.usage); + } +} + +void UsageTracker::DidGetClientHostUsage(const std::string& host, int64 usage) { + TrackingInfo& info = outstanding_host_usage_[host]; + info.usage += usage; + if (--info.pending_clients == 0) { + // All the clients have returned their usage data. Dispatches the + // pending callbacks. + host_usage_callbacks_.Run(host, info.usage); + outstanding_host_usage_.erase(host); + } +} + +// ClientUsageTracker ---------------------------------------------------- + +ClientUsageTracker::ClientUsageTracker( + UsageTracker* tracker, QuotaClient* client) + : tracker_(tracker), + client_(client), + global_usage_(0), + global_usage_retrieved_(false), + global_usage_task_(NULL) { + DCHECK(tracker_); + DCHECK(client_); +} + +ClientUsageTracker::~ClientUsageTracker() { +} + +void ClientUsageTracker::GetGlobalUsage(UsageCallback* callback) { + if (global_usage_retrieved_) { + callback->Run(global_usage_); + delete callback; + return; + } + DCHECK(!global_usage_callback_.HasCallbacks()); + global_usage_callback_.Add(callback); + global_usage_task_ = new GatherGlobalUsageTask(tracker_, client_); + global_usage_task_->Start(); +} + +void ClientUsageTracker::GetHostUsage( + const std::string& host, HostUsageCallback* callback) { + std::map<std::string, int64>::iterator found = host_usage_map_.find(host); + if (found != host_usage_map_.end()) { + // TODO(kinuko): Drop host_usage_map_ cache periodically. + callback->Run(host, found->second); + delete callback; + return; + } + DCHECK(!host_usage_callbacks_.HasCallbacks(host)); + DCHECK(host_usage_tasks_.find(host) == host_usage_tasks_.end()); + host_usage_callbacks_.Add(host, callback); + if (global_usage_task_) + return; + GatherHostUsageTask* task = new GatherHostUsageTask(tracker_, client_, host); + task->Start(); + host_usage_tasks_[host] = task; +} + +void ClientUsageTracker::DetermineOriginsToGetUsage( + const std::set<GURL>& origins, std::set<GURL>* origins_to_process) { + DCHECK(origins_to_process); + for (std::set<GURL>::const_iterator iter = origins.begin(); + iter != origins.end(); ++iter) { + if (cached_origins_.find(*iter) == cached_origins_.end()) + origins_to_process->insert(*iter); + } +} + +void ClientUsageTracker::UpdateUsageCache( + const GURL& origin, int64 delta) { + std::string host = net::GetHostOrSpecFromURL(origin); + if (cached_origins_.find(origin) != cached_origins_.end()) { + host_usage_map_[host] += delta; + global_usage_ += delta; + return; + } + if (global_usage_retrieved_ || + host_usage_map_.find(host) != host_usage_map_.end()) { + // This might be for a new origin. + cached_origins_.insert(origin); + host_usage_map_[host] = delta; + global_usage_ += delta; + return; + } + // See if the origin has been processed in outstanding gather tasks + // and add up the delta if it has. + if (global_usage_task_ && global_usage_task_->IsOriginDone(origin)) { + host_usage_map_[host] += delta; + global_usage_ += delta; + return; + } + if (host_usage_tasks_.find(host) != host_usage_tasks_.end() && + host_usage_tasks_[host]->IsOriginDone(origin)) { + host_usage_map_[host] += delta; + } + // Otherwise we have not cached usage info for the origin yet. + // Succeeding GetUsage tasks would eventually catch the change. +} + +void ClientUsageTracker::DidGetGlobalUsage( + const std::map<GURL, int64>& origin_usage_map) { + DCHECK(global_usage_task_ != NULL); + global_usage_task_ = NULL; + // TODO(kinuko): Record when it has retrieved the global usage. + global_usage_retrieved_ = true; + for (std::map<GURL, int64>::const_iterator iter = origin_usage_map.begin(); + iter != origin_usage_map.end(); + ++iter) { + if (cached_origins_.insert(iter->first).second) { + global_usage_ += iter->second; + std::string host = net::GetHostOrSpecFromURL(iter->first); + host_usage_map_[host] += iter->second; + } + } + + // Dispatches the global usage callback. + DCHECK(global_usage_callback_.HasCallbacks()); + global_usage_callback_.Run(global_usage_); + + // Dispatches host usage callbacks. + for (HostUsageCallbackMap::iterator iter = host_usage_callbacks_.Begin(); + iter != host_usage_callbacks_.End(); + ++iter) { + std::map<std::string, int64>::iterator found = + host_usage_map_.find(iter->first); + if (found == host_usage_map_.end()) + HostUsageCallbackMap::RunAt(iter, 0); + else + HostUsageCallbackMap::RunAt(iter, found->second); + } + host_usage_callbacks_.Clear(); +} + +void ClientUsageTracker::DidGetHostUsage( + const std::string& host, + const std::map<GURL, int64>& origin_usage_map) { + DCHECK(host_usage_tasks_.find(host) != host_usage_tasks_.end()); + host_usage_tasks_.erase(host); + for (std::map<GURL, int64>::const_iterator iter = origin_usage_map.begin(); + iter != origin_usage_map.end(); + ++iter) { + if (cached_origins_.insert(iter->first).second) { + global_usage_ += iter->second; + host_usage_map_[host] += iter->second; + } + } + + // Dispatches the host usage callback. + host_usage_callbacks_.Run(host, host_usage_map_[host]); +} + +} // namespace quota diff --git a/webkit/quota/usage_tracker.h b/webkit/quota/usage_tracker.h new file mode 100644 index 0000000..c2856ca --- /dev/null +++ b/webkit/quota/usage_tracker.h @@ -0,0 +1,107 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_QUOTA_USAGE_TRACKER_H_ +#define WEBKIT_QUOTA_USAGE_TRACKER_H_ +#pragma once + +#include <list> +#include <map> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/memory/scoped_ptr.h" +#include "googleurl/src/gurl.h" +#include "webkit/quota/quota_client.h" +#include "webkit/quota/quota_task.h" +#include "webkit/quota/quota_types.h" + +namespace quota { + +class ClientUsageTracker; + +// A helper class that gathers and tracks the amount of data stored in +// all quota clients. +// An instance of this class is created per storage type. +class UsageTracker : public QuotaTaskObserver { + public: + UsageTracker(const QuotaClientList& clients, StorageType type); + ~UsageTracker(); + + StorageType type() const { return type_; } + ClientUsageTracker* GetClientTracker(QuotaClient::ID client_id); + + void GetGlobalUsage(UsageCallback* callback); + void GetHostUsage(const std::string& host, HostUsageCallback* callback); + void UpdateUsageCache(QuotaClient::ID client_id, + const GURL& origin, + int64 delta); + + private: + struct TrackingInfo { + TrackingInfo() : pending_clients(0), usage(0) {} + int pending_clients; + int64 usage; + }; + + typedef std::map<QuotaClient::ID, ClientUsageTracker*> ClientTrackerMap; + + friend class ClientUsageTracker; + void DidGetClientGlobalUsage(int64 usage); + void DidGetClientHostUsage(const std::string& host, int64 usage); + + StorageType type_; + ClientTrackerMap client_tracker_map_; + TrackingInfo global_usage_; + std::map<std::string, TrackingInfo> outstanding_host_usage_; + + UsageCallbackQueue global_usage_callbacks_; + HostUsageCallbackMap host_usage_callbacks_; + + base::ScopedCallbackFactory<UsageTracker> callback_factory_; + DISALLOW_COPY_AND_ASSIGN(UsageTracker); +}; + +// This class holds per-client usage tracking information and caches per-host +// usage data. An instance of this class is created per client. +class ClientUsageTracker { + public: + ClientUsageTracker(UsageTracker* tracking_info, QuotaClient* client); + ~ClientUsageTracker(); + + void GetGlobalUsage(UsageCallback* callback); + void GetHostUsage(const std::string& host, HostUsageCallback* callback); + void DetermineOriginsToGetUsage(const std::set<GURL>& origins, + std::set<GURL>* origins_to_process); + void UpdateUsageCache(const GURL& origin, int64 delta); + + private: + class GatherUsageTaskBase; + class GatherGlobalUsageTask; + class GatherHostUsageTask; + + void DidGetGlobalUsage(const std::map<GURL, int64>& origin_usage_map); + void DidGetHostUsage(const std::string& host, + const std::map<GURL, int64>& origin_usage_map); + + UsageTracker* tracker_; + QuotaClient* client_; + std::set<GURL> cached_origins_; + + int64 global_usage_; + bool global_usage_retrieved_; + GatherGlobalUsageTask* global_usage_task_; + UsageCallbackQueue global_usage_callback_; + + std::map<std::string, int64> host_usage_map_; + std::map<std::string, GatherHostUsageTask*> host_usage_tasks_; + HostUsageCallbackMap host_usage_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(ClientUsageTracker); +}; + +} // namespace quota + +#endif // WEBKIT_QUOTA_USAGE_TRACKER_H_ diff --git a/webkit/quota/webkit_quota.gypi b/webkit/quota/webkit_quota.gypi index 491c494..101daf7 100644 --- a/webkit/quota/webkit_quota.gypi +++ b/webkit/quota/webkit_quota.gypi @@ -14,10 +14,18 @@ '<(DEPTH)/net/net.gyp:net', ], 'sources': [ + 'quota_client.h', 'quota_database.cc', 'quota_database.h', + 'quota_manager.cc', + 'quota_manager.h', + 'quota_task.cc', + 'quota_task.h', + 'quota_types.h', 'special_storage_policy.cc', 'special_storage_policy.h', + 'usage_tracker.cc', + 'usage_tracker.h', ], 'conditions': [ ['inside_chromium_build==0', { diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index bdbc356..3f90d44 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -422,7 +422,10 @@ '../../plugins/ppapi/ppapi_unittest.h', '../../plugins/ppapi/resource_tracker_unittest.cc', '../../plugins/ppapi/url_request_info_unittest.cc', + '../../quota/mock_storage_client.cc', + '../../quota/mock_storage_client.h', '../../quota/quota_database_unittest.cc', + '../../quota/quota_manager_unittest.cc', '../webcore_unit_tests/BMPImageDecoder_unittest.cpp', '../webcore_unit_tests/ICOImageDecoder_unittest.cpp', 'event_listener_unittest.cc', |