diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-09 06:29:59 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-09 06:29:59 +0000 |
commit | c62983a7fcbd13cb5aade8da6d4f27c44dd5c069 (patch) | |
tree | 7532ffa1f81bbf1eed6dbf5c9009c4c4ecd89f4e | |
parent | d36f941bbea3e9fa6d13829626a1911008b37f17 (diff) | |
download | chromium_src-c62983a7fcbd13cb5aade8da6d4f27c44dd5c069.zip chromium_src-c62983a7fcbd13cb5aade8da6d4f27c44dd5c069.tar.gz chromium_src-c62983a7fcbd13cb5aade8da6d4f27c44dd5c069.tar.bz2 |
Implement SandboxQuotaClient for Quota support in sandboxed filesystem
- Refactored FileSystemUsageTracker as a SandboxQuotaClient
- Added a few more tests
BUG=61676
TEST=SandboxQuotaClientTest.*
Review URL: http://codereview.chromium.org/6883002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84604 0039d316-1c4b-4281-b951-d872f2087c98
37 files changed, 1294 insertions, 768 deletions
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index aa55653..11b050c 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -66,6 +66,7 @@ #include "testing/platform_test.h" #include "webkit/database/database_tracker.h" #include "webkit/database/database_util.h" +#include "webkit/quota/quota_manager.h" namespace keys = extension_manifest_keys; @@ -361,9 +362,13 @@ class ExtensionTestingProfile : public TestingProfile { } virtual fileapi::FileSystemContext* GetFileSystemContext() { - if (!file_system_context_) + if (!file_system_context_) { + quota::QuotaManager* quota_manager = GetQuotaManager(); file_system_context_ = CreateFileSystemContext( - GetPath(), IsOffTheRecord(), GetExtensionSpecialStoragePolicy()); + GetPath(), IsOffTheRecord(), + GetExtensionSpecialStoragePolicy(), + quota_manager ? quota_manager->proxy() : NULL); + } return file_system_context_; } diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index a2c0d6f..bcb812a 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -435,10 +435,7 @@ class OffTheRecordProfileImpl : public Profile, } virtual fileapi::FileSystemContext* GetFileSystemContext() { - if (!file_system_context_) - file_system_context_ = CreateFileSystemContext( - GetPath(), IsOffTheRecord(), GetExtensionSpecialStoragePolicy()); - DCHECK(file_system_context_.get()); + CreateQuotaManagerAndClients(); return file_system_context_.get(); } @@ -447,13 +444,7 @@ class OffTheRecordProfileImpl : public Profile, } virtual quota::QuotaManager* GetQuotaManager() { - if (!quota_manager_.get()) { - quota_manager_ = new quota::QuotaManager( - IsOffTheRecord(), - GetPath(), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); - } + CreateQuotaManagerAndClients(); return quota_manager_.get(); } @@ -705,6 +696,30 @@ class OffTheRecordProfileImpl : public Profile, } private: + void CreateQuotaManagerAndClients() { + if (quota_manager_.get()) { + DCHECK(file_system_context_.get()); + return; + } + + // All of the clients have to be created and registered with the + // QuotaManager prior to the QuotaManger being used. So we do them + // all together here prior to handing out a reference to anything + // that utlizes the QuotaManager. + quota_manager_ = new quota::QuotaManager( + IsOffTheRecord(), + GetPath(), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); + + // Each consumer is responsible for registering its QuotaClient during + // its construction. + file_system_context_ = CreateFileSystemContext( + GetPath(), IsOffTheRecord(), + GetExtensionSpecialStoragePolicy(), + quota_manager_->proxy()); + } + NotificationRegistrar registrar_; // The real underlying profile. diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 216eed9..ae2a519 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -1170,21 +1170,12 @@ PersonalDataManager* ProfileImpl::GetPersonalDataManager() { } fileapi::FileSystemContext* ProfileImpl::GetFileSystemContext() { - if (!file_system_context_.get()) - file_system_context_ = CreateFileSystemContext( - GetPath(), IsOffTheRecord(), GetExtensionSpecialStoragePolicy()); - DCHECK(file_system_context_.get()); + CreateQuotaManagerAndClients(); return file_system_context_.get(); } quota::QuotaManager* ProfileImpl::GetQuotaManager() { - if (!quota_manager_.get()) { - quota_manager_ = new quota::QuotaManager( - IsOffTheRecord(), - GetPath(), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); - } + CreateQuotaManagerAndClients(); return quota_manager_.get(); } @@ -1279,6 +1270,30 @@ ExtensionPrefValueMap* ProfileImpl::GetExtensionPrefValueMap() { return extension_pref_value_map_.get(); } +void ProfileImpl::CreateQuotaManagerAndClients() { + if (quota_manager_.get()) { + DCHECK(file_system_context_.get()); + return; + } + + // All of the clients have to be created and registered with the + // QuotaManager prior to the QuotaManger being used. So we do them + // all together here prior to handing out a reference to anything + // that utlizes the QuotaManager. + quota_manager_ = new quota::QuotaManager( + IsOffTheRecord(), + GetPath(), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)); + + // Each consumer is responsible for registering its QuotaClient during + // its construction. + file_system_context_ = CreateFileSystemContext( + GetPath(), IsOffTheRecord(), + GetExtensionSpecialStoragePolicy(), + quota_manager_->proxy()); +} + WebKitContext* ProfileImpl::GetWebKitContext() { if (!webkit_context_.get()) { webkit_context_ = new WebKitContext( diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 5450bb6c..0809482 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -179,6 +179,8 @@ class ProfileImpl : public Profile, ExtensionPrefValueMap* GetExtensionPrefValueMap(); + void CreateQuotaManagerAndClients(); + NotificationRegistrar registrar_; PrefChangeRegistrar pref_change_registrar_; diff --git a/content/browser/file_system/browser_file_system_helper.cc b/content/browser/file_system/browser_file_system_helper.cc index db4bf3f..5515ebc 100644 --- a/content/browser/file_system/browser_file_system_helper.cc +++ b/content/browser/file_system/browser_file_system_helper.cc @@ -8,14 +8,17 @@ #include "base/command_line.h" #include "content/common/content_switches.h" #include "content/browser/browser_thread.h" +#include "webkit/quota/quota_manager.h" scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext( const FilePath& profile_path, bool is_incognito, - quota::SpecialStoragePolicy* special_storage_policy) { + quota::SpecialStoragePolicy* special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy) { return new fileapi::FileSystemContext( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), special_storage_policy, + quota_manager_proxy, profile_path, is_incognito, CommandLine::ForCurrentProcess()->HasSwitch( diff --git a/content/browser/file_system/browser_file_system_helper.h b/content/browser/file_system/browser_file_system_helper.h index 072a8a1..9af8031 100644 --- a/content/browser/file_system/browser_file_system_helper.h +++ b/content/browser/file_system/browser_file_system_helper.h @@ -16,7 +16,9 @@ class SpecialStoragePolicy; // Helper method that returns FileSystemContext constructed for // the browser process. scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext( - const FilePath& profile_path, bool is_incognito, - quota::SpecialStoragePolicy* special_storage_policy); + const FilePath& profile_path, + bool is_incognito, + quota::SpecialStoragePolicy* special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy); #endif // CONTENT_BROWSER_FILE_SYSTEM_BROWSER_FILE_SYSTEM_HELPER_H_ diff --git a/webkit/fileapi/file_system_context.cc b/webkit/fileapi/file_system_context.cc index 7e0d62e..d27b87d 100644 --- a/webkit/fileapi/file_system_context.cc +++ b/webkit/fileapi/file_system_context.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -8,15 +8,32 @@ #include "base/message_loop_proxy.h" #include "googleurl/src/gurl.h" #include "webkit/fileapi/file_system_path_manager.h" -#include "webkit/fileapi/file_system_usage_tracker.h" #include "webkit/fileapi/sandbox_mount_point_provider.h" +#include "webkit/fileapi/sandbox_quota_client.h" +#include "webkit/quota/quota_manager.h" + +using quota::QuotaClient; namespace fileapi { +namespace { +QuotaClient* CreateQuotaClient( + scoped_refptr<base::MessageLoopProxy> file_message_loop, + FileSystemContext* context, + bool is_incognito) { + // TODO(kinuko): For now we assume only sandbox filesystem uses + // the quota feature. If we want to support multiple filesystem types + // that require different quota we'll need to add more QuotaClientID + // and more factory-like code around QuotaClient. + return new SandboxQuotaClient(file_message_loop, context, is_incognito); +} +} // anonymous namespace + FileSystemContext::FileSystemContext( scoped_refptr<base::MessageLoopProxy> file_message_loop, scoped_refptr<base::MessageLoopProxy> io_message_loop, scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy, const FilePath& profile_path, bool is_incognito, bool allow_file_access, @@ -25,16 +42,19 @@ FileSystemContext::FileSystemContext( : file_message_loop_(file_message_loop), io_message_loop_(io_message_loop), special_storage_policy_(special_storage_policy), + quota_manager_proxy_(quota_manager_proxy), allow_file_access_from_files_(allow_file_access), unlimited_quota_(unlimited_quota), - path_manager_(path_manager), - usage_tracker_(new FileSystemUsageTracker( - file_message_loop, profile_path, is_incognito)) { + path_manager_(path_manager) { if (!path_manager) { path_manager_.reset(new FileSystemPathManager( file_message_loop, profile_path, special_storage_policy, is_incognito, allow_file_access)); } + if (quota_manager_proxy) { + quota_manager_proxy->RegisterClient(CreateQuotaClient( + file_message_loop, this, is_incognito)); + } } FileSystemContext::~FileSystemContext() { @@ -55,11 +75,8 @@ void FileSystemContext::DeleteDataForOriginOnFileThread( DCHECK(path_manager_.get()); DCHECK(file_message_loop_->BelongsToCurrentThread()); - std::string origin_identifier = - SandboxMountPointProvider::GetOriginIdentifierFromURL(origin_url); - FilePath path_for_origin = sandbox_provider()->base_path().AppendASCII( - origin_identifier); - + FilePath path_for_origin = + sandbox_provider()->GetBaseDirectoryForOrigin(origin_url); file_util::Delete(path_for_origin, true /* recursive */); } diff --git a/webkit/fileapi/file_system_context.h b/webkit/fileapi/file_system_context.h index aa1804a..f80e5f0 100644 --- a/webkit/fileapi/file_system_context.h +++ b/webkit/fileapi/file_system_context.h @@ -16,6 +16,11 @@ namespace base { class MessageLoopProxy; } +namespace quota { +class QuotaClient; +class QuotaManagerProxy; +} + namespace fileapi { class FileSystemContext; @@ -34,6 +39,7 @@ class FileSystemContext scoped_refptr<base::MessageLoopProxy> file_message_loop, scoped_refptr<base::MessageLoopProxy> io_message_loop, scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy, + quota::QuotaManagerProxy* quota_manager_proxy, const FilePath& profile_path, bool is_incognito, bool allow_file_access_from_files, @@ -46,8 +52,10 @@ class FileSystemContext void DeleteDataForOriginOnFileThread(const GURL& origin_url); - FileSystemPathManager* path_manager() { return path_manager_.get(); } - FileSystemUsageTracker* usage_tracker() { return usage_tracker_.get(); } + FileSystemPathManager* path_manager() const { return path_manager_.get(); } + quota::QuotaManagerProxy* quota_manager_proxy() const { + return quota_manager_proxy_.get(); + } private: friend struct DefaultContextDeleter; @@ -58,11 +66,11 @@ class FileSystemContext scoped_refptr<base::MessageLoopProxy> io_message_loop_; scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_; + scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; const bool allow_file_access_from_files_; const bool unlimited_quota_; scoped_ptr<FileSystemPathManager> path_manager_; - scoped_ptr<FileSystemUsageTracker> usage_tracker_; DISALLOW_IMPLICIT_CONSTRUCTORS(FileSystemContext); }; diff --git a/webkit/fileapi/file_system_context_unittest.cc b/webkit/fileapi/file_system_context_unittest.cc index 30ec54e..025b7f3 100644 --- a/webkit/fileapi/file_system_context_unittest.cc +++ b/webkit/fileapi/file_system_context_unittest.cc @@ -12,6 +12,7 @@ #include "base/string_number_conversions.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webkit/quota/quota_manager.h" using namespace fileapi; @@ -46,6 +47,7 @@ scoped_refptr<FileSystemContext> NewFileSystemContext( return new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), special_storage_policy, + NULL /* quota manager */, FilePath(), false /* is_incognito */, allow_file_access, unlimited_quota, NULL); } diff --git a/webkit/fileapi/file_system_dir_url_request_job_unittest.cc b/webkit/fileapi/file_system_dir_url_request_job_unittest.cc index 6b2882b..888121b 100644 --- a/webkit/fileapi/file_system_dir_url_request_job_unittest.cc +++ b/webkit/fileapi/file_system_dir_url_request_job_unittest.cc @@ -68,7 +68,7 @@ class FileSystemDirURLRequestJobTest : public testing::Test { new FileSystemContext( base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - special_storage_policy_, + special_storage_policy_, NULL, FilePath(), false /* is_incognito */, false, true, new FileSystemPathManager( diff --git a/webkit/fileapi/file_system_operation_write_unittest.cc b/webkit/fileapi/file_system_operation_write_unittest.cc index 2f996ab..053cc68 100644 --- a/webkit/fileapi/file_system_operation_write_unittest.cc +++ b/webkit/fileapi/file_system_operation_write_unittest.cc @@ -188,7 +188,7 @@ FileSystemOperation* FileSystemOperationWriteTest::operation() { base::MessageLoopProxy::CreateForCurrentThread(), new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - NULL, FilePath(), false /* is_incognito */, + NULL, NULL, FilePath(), false /* is_incognito */, true, true, new MockFileSystemPathManager(filesystem_dir_)), LocalFileSystemFileUtil::GetInstance()); diff --git a/webkit/fileapi/file_system_url_request_job_unittest.cc b/webkit/fileapi/file_system_url_request_job_unittest.cc index 62e6d8f..c0e60a1 100644 --- a/webkit/fileapi/file_system_url_request_job_unittest.cc +++ b/webkit/fileapi/file_system_url_request_job_unittest.cc @@ -88,7 +88,7 @@ class FileSystemURLRequestJobTest : public testing::Test { new FileSystemContext( base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - special_storage_policy_, + special_storage_policy_, NULL, FilePath(), false /* is_incognito */, false, true, new FileSystemPathManager( diff --git a/webkit/fileapi/file_system_usage_tracker.cc b/webkit/fileapi/file_system_usage_tracker.cc deleted file mode 100644 index 687884f..0000000 --- a/webkit/fileapi/file_system_usage_tracker.cc +++ /dev/null @@ -1,183 +0,0 @@ -// 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/fileapi/file_system_usage_tracker.h" - -#include <algorithm> - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop_proxy.h" -#include "base/task.h" -#include "googleurl/src/gurl.h" -#include "webkit/fileapi/file_system_path_manager.h" -#include "webkit/fileapi/file_system_usage_cache.h" -#include "webkit/fileapi/sandbox_mount_point_provider.h" - -namespace fileapi { - -class FileSystemUsageTracker::GetUsageTask - : public base::RefCountedThreadSafe<GetUsageTask> { - public: - GetUsageTask( - FileSystemUsageTracker* tracker, - scoped_refptr<base::MessageLoopProxy> file_message_loop, - std::string fs_identifier, - const FilePath& origin_base_path) - : tracker_(tracker), - file_message_loop_(file_message_loop), - original_message_loop_( - base::MessageLoopProxy::CreateForCurrentThread()), - fs_identifier_(fs_identifier), - fs_usage_(0), - origin_base_path_(origin_base_path) { - } - - virtual ~GetUsageTask() {} - - void Start() { - DCHECK(tracker_); - tracker_->RegisterUsageTask(this); - file_message_loop_->PostTask( - FROM_HERE, NewRunnableMethod(this, &GetUsageTask::RunOnFileThread)); - } - - void Cancel() { - DCHECK(original_message_loop_->BelongsToCurrentThread()); - tracker_ = NULL; - } - - private: - void RunOnFileThread() { - DCHECK(file_message_loop_->BelongsToCurrentThread()); - - if (!file_util::DirectoryExists(origin_base_path_)) - fs_usage_ = 0; - else { - FilePath usage_file_path = origin_base_path_.AppendASCII( - FileSystemUsageCache::kUsageFileName); - - if (FileSystemUsageCache::GetDirty(usage_file_path) != 0) { - FilePath content_file_path = origin_base_path_; - if (FileSystemUsageCache::Exists(usage_file_path)) - FileSystemUsageCache::Delete(usage_file_path); - fs_usage_ = file_util::ComputeDirectorySize(content_file_path); - // fs_usage_ will include the size of .usage. - // The result of ComputeDirectorySize does not include it. - fs_usage_ += FileSystemUsageCache::kUsageFileSize; - FileSystemUsageCache::UpdateUsage(usage_file_path, fs_usage_); - } else { - fs_usage_ = FileSystemUsageCache::GetUsage(usage_file_path); - } - } - - original_message_loop_->PostTask( - FROM_HERE, NewRunnableMethod(this, &GetUsageTask::Completed)); - } - - void Completed() { - DCHECK(original_message_loop_->BelongsToCurrentThread()); - if (tracker_) { - tracker_->UnregisterUsageTask(this); - tracker_->DidGetOriginUsage(fs_identifier_, fs_usage_); - } - } - - FileSystemUsageTracker* tracker_; - scoped_refptr<base::MessageLoopProxy> file_message_loop_; - scoped_refptr<base::MessageLoopProxy> original_message_loop_; - std::string fs_identifier_; - int64 fs_usage_; - FilePath origin_base_path_; -}; - -FileSystemUsageTracker::FileSystemUsageTracker( - scoped_refptr<base::MessageLoopProxy> file_message_loop, - const FilePath& profile_path, - bool is_incognito) - : file_message_loop_(file_message_loop), - base_path_(profile_path.Append( - SandboxMountPointProvider::kFileSystemDirectory)), - is_incognito_(is_incognito) { - DCHECK(file_message_loop); -} - -FileSystemUsageTracker::~FileSystemUsageTracker() { - std::for_each(running_usage_tasks_.begin(), - running_usage_tasks_.end(), - std::mem_fun(&GetUsageTask::Cancel)); -} - -void FileSystemUsageTracker::GetOriginUsage( - const GURL& origin_url, - fileapi::FileSystemType type, - GetUsageCallback* callback_ptr) { - DCHECK(callback_ptr); - scoped_ptr<GetUsageCallback> callback(callback_ptr); - - if (is_incognito_) { - // We don't support FileSystem in incognito mode yet. - callback->Run(0); - return; - } - - std::string origin_identifier = - SandboxMountPointProvider::GetOriginIdentifierFromURL(origin_url); - std::string type_string = - FileSystemPathManager::GetFileSystemTypeString(type); - std::string fs_identifier = origin_identifier + ":" + type_string; - - if (pending_usage_callbacks_.find(fs_identifier) != - pending_usage_callbacks_.end()) { - // Another get usage task is running. Add the callback to - // the pending queue and return. - pending_usage_callbacks_[fs_identifier].push_back(callback.release()); - return; - } - - // Get the filesystem base path (i.e. "FileSystem/<origin>/<type>", - // without unique part). - FilePath origin_base_path = - SandboxMountPointProvider::GetFileSystemBaseDirectoryForOriginAndType( - base_path_, origin_identifier, type); - if (origin_base_path.empty()) { - // The directory does not exist. - callback->Run(0); - return; - } - - pending_usage_callbacks_[fs_identifier].push_back(callback.release()); - scoped_refptr<GetUsageTask> task( - new GetUsageTask(this, file_message_loop_, fs_identifier, - origin_base_path)); - task->Start(); -} - -void FileSystemUsageTracker::RegisterUsageTask(GetUsageTask* task) { - running_usage_tasks_.push_back(task); -} - -void FileSystemUsageTracker::UnregisterUsageTask(GetUsageTask* task) { - DCHECK(running_usage_tasks_.front() == task); - running_usage_tasks_.pop_front(); -} - -void FileSystemUsageTracker::DidGetOriginUsage( - const std::string& fs_identifier, int64 usage) { - PendingUsageCallbackMap::iterator cb_list_iter = - pending_usage_callbacks_.find(fs_identifier); - DCHECK(cb_list_iter != pending_usage_callbacks_.end()); - PendingCallbackList cb_list = cb_list_iter->second; - for (PendingCallbackList::iterator cb_iter = cb_list.begin(); - cb_iter != cb_list.end(); - ++cb_iter) { - scoped_ptr<GetUsageCallback> callback(*cb_iter); - callback->Run(usage); - } - pending_usage_callbacks_.erase(cb_list_iter); -} - -} // namespace fileapi diff --git a/webkit/fileapi/file_system_usage_tracker.h b/webkit/fileapi/file_system_usage_tracker.h deleted file mode 100644 index 6328693..0000000 --- a/webkit/fileapi/file_system_usage_tracker.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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_FILEAPI_FILE_SYSTEM_USAGE_TRACKER_H_ -#define WEBKIT_FILEAPI_FILE_SYSTEM_USAGE_TRACKER_H_ - -#include <deque> -#include <list> -#include <map> -#include <string> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/file_path.h" -#include "base/memory/ref_counted.h" -#include "webkit/fileapi/file_system_types.h" - -class GURL; - -namespace base { -class MessageLoopProxy; -} - -namespace fileapi { - -// Owned by the FileSystemContext, which is a per-profile instance, and has the -// same lifetime as the FileSystemContext. It's going to be created and -// destroyed on the IO thread in chrome. (The destruction on the same thread -// where it is created was guaranteed by its owner, FileSystemContext.) -class FileSystemUsageTracker { - public: - FileSystemUsageTracker( - scoped_refptr<base::MessageLoopProxy> file_message_loop, - const FilePath& profile_path, - bool is_incognito); - ~FileSystemUsageTracker(); - - // Get the amount of data stored in the filesystem specified by - // |origin_url| and |type|. - typedef Callback1<int64 /* usage */>::Type GetUsageCallback; - void GetOriginUsage(const GURL& origin_url, - fileapi::FileSystemType type, - GetUsageCallback* callback); - - private: - class GetUsageTask; - - void RegisterUsageTask(GetUsageTask* task); - void UnregisterUsageTask(GetUsageTask* task); - - void DidGetOriginUsage(const std::string& fs_name, int64 usage); - - scoped_refptr<base::MessageLoopProxy> file_message_loop_; - FilePath base_path_; - bool is_incognito_; - - typedef std::deque<GetUsageTask*> UsageTaskQueue; - UsageTaskQueue running_usage_tasks_; - - typedef std::list<GetUsageCallback*> PendingCallbackList; - typedef std::map<std::string, PendingCallbackList> PendingUsageCallbackMap; - PendingUsageCallbackMap pending_usage_callbacks_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemUsageTracker); -}; - -} // namespace fileapi - -#endif // WEBKIT_FILEAPI_FILE_SYSTEM_USAGE_TRACKER_H_ diff --git a/webkit/fileapi/file_system_usage_tracker_unittest.cc b/webkit/fileapi/file_system_usage_tracker_unittest.cc deleted file mode 100644 index e198363..0000000 --- a/webkit/fileapi/file_system_usage_tracker_unittest.cc +++ /dev/null @@ -1,279 +0,0 @@ -// 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/fileapi/file_system_usage_tracker.h" - -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/memory/scoped_callback_factory.h" -#include "base/memory/scoped_temp_dir.h" -#include "base/message_loop.h" -#include "base/message_loop_proxy.h" -#include "base/platform_file.h" -#include "googleurl/src/gurl.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "webkit/fileapi/file_system_types.h" -#include "webkit/fileapi/file_system_usage_cache.h" -#include "webkit/fileapi/sandbox_mount_point_provider.h" - -using namespace fileapi; - -namespace { - -static const GURL kDummyURL1("http://www.dummy.org"); -static const GURL kDummyURL2("http://www.example.com"); - -// Declared to shorten the variable names. -static const fileapi::FileSystemType kTemporary = - fileapi::kFileSystemTypeTemporary; -static const fileapi::FileSystemType kPersistent = - fileapi::kFileSystemTypePersistent; -static const int kUsageFileSize = FileSystemUsageCache::kUsageFileSize; - -} - -class FileSystemUsageTrackerTest : public testing::Test { - public: - FileSystemUsageTrackerTest() - : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { - } - - void SetUp() { - ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - } - - struct TestFile { - bool isDirectory; - const char* name; - int64 size; - GURL origin_url; - fileapi::FileSystemType type; - }; - - protected: - FileSystemUsageTracker* NewUsageTracker(bool is_incognito) { - return new FileSystemUsageTracker( - base::MessageLoopProxy::CreateForCurrentThread(), - data_dir_.path(), is_incognito); - } - - int64 GetOriginUsage(FileSystemUsageTracker* tracker, - const GURL& origin_url, - fileapi::FileSystemType type) { - tracker->GetOriginUsage(origin_url, type, - callback_factory_.NewCallback( - &FileSystemUsageTrackerTest::OnGetUsage)); - MessageLoop::current()->RunAllPending(); - return usage_; - } - - FilePath GetOriginBasePath(const GURL& origin_url, - fileapi::FileSystemType type) { - return - SandboxMountPointProvider::GetFileSystemBaseDirectoryForOriginAndType( - data_dir_.path().Append( - SandboxMountPointProvider::kFileSystemDirectory), - SandboxMountPointProvider::GetOriginIdentifierFromURL(origin_url), - type); - } - - bool CreateFileSystemDirectory(const char* dir_name, - const GURL& origin_url, - fileapi::FileSystemType type) { - FilePath origin_base_path = GetOriginBasePath(origin_url, type); - FilePath dir_path; - if (dir_name != NULL) - dir_path = origin_base_path.AppendASCII(dir_name); - else - dir_path = origin_base_path; - if (dir_path.empty()) - return false; - - return file_util::CreateDirectory(dir_path); - } - - bool CreateFileSystemFile(const char* file_name, - int64 file_size, - const GURL& origin_url, - fileapi::FileSystemType type) { - FilePath origin_base_path = GetOriginBasePath(origin_url, type); - FilePath file_path = origin_base_path.AppendASCII(file_name); - - if (file_path.empty()) - return false; - - int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS | - base::PLATFORM_FILE_WRITE; - base::PlatformFileError error_code; - base::PlatformFile file = - base::CreatePlatformFile(file_path, file_flags, NULL, &error_code); - if (error_code != base::PLATFORM_FILE_OK) - return false; - - bool succeeded; - succeeded = base::TruncatePlatformFile(file, file_size); - succeeded = succeeded && base::ClosePlatformFile(file); - return succeeded; - } - - void CreateFiles(const TestFile* files, int num_files) { - for (int i = 0; i < num_files; i++) { - if (files[i].isDirectory) { - ASSERT_TRUE(CreateFileSystemDirectory( - files[i].name, files[i].origin_url, files[i].type)); - } else { - ASSERT_TRUE(CreateFileSystemFile( - files[i].name, files[i].size, files[i].origin_url, files[i].type)); - } - } - } - - private: - void OnGetUsage(int64 usage) { - usage_ = usage; - } - - ScopedTempDir data_dir_; - base::ScopedCallbackFactory<FileSystemUsageTrackerTest> callback_factory_; - int64 usage_; - - DISALLOW_COPY_AND_ASSIGN(FileSystemUsageTrackerTest); -}; - -TEST_F(FileSystemUsageTrackerTest, NoFileSystemTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - - EXPECT_EQ(0, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); -} - -TEST_F(FileSystemUsageTrackerTest, NoFileTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemUsageTrackerTest, OneFileTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 4921, kDummyURL1, kTemporary}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(4921 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemUsageTrackerTest, TwoFilesTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 10310, kDummyURL1, kTemporary}, - {false, "bar", 41, kDummyURL1, kTemporary}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(10310 + 41 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemUsageTrackerTest, EmptyFilesTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {false, "foo", 0, kDummyURL1, kTemporary}, - {false, "bar", 0, kDummyURL1, kTemporary}, - {false, "baz", 0, kDummyURL1, kTemporary}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemUsageTrackerTest, SubDirectoryTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dirtest", 0, kDummyURL1, kTemporary}, - {false, "dirtest/foo", 11921, kDummyURL1, kTemporary}, - {false, "bar", 4814, kDummyURL1, kTemporary}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(11921 + 4814 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - } -} - -TEST_F(FileSystemUsageTrackerTest, MultiTypeTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dirtest", 0, kDummyURL1, kTemporary}, - {false, "dirtest/foo", 133, kDummyURL1, kTemporary}, - {false, "bar", 14, kDummyURL1, kTemporary}, - {true, NULL, 0, kDummyURL1, kPersistent}, - {true, "dirtest", 0, kDummyURL1, kPersistent}, - {false, "dirtest/foo", 193, kDummyURL1, kPersistent}, - {false, "bar", 9, kDummyURL1, kPersistent}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(133 + 14 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - EXPECT_EQ(193 + 9 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kPersistent)); - } -} - -TEST_F(FileSystemUsageTrackerTest, MultiDomainTest) { - scoped_ptr<FileSystemUsageTracker> tracker(NewUsageTracker(false)); - TestFile files[] = { - {true, NULL, 0, kDummyURL1, kTemporary}, - {true, "dir1", 0, kDummyURL1, kTemporary}, - {false, "dir1/foo", 1331, kDummyURL1, kTemporary}, - {false, "bar", 134, kDummyURL1, kTemporary}, - {true, NULL, 0, kDummyURL1, kPersistent}, - {true, "dir2", 0, kDummyURL1, kPersistent}, - {false, "dir2/foo", 1903, kDummyURL1, kPersistent}, - {false, "bar", 19, kDummyURL1, kPersistent}, - {true, NULL, 0, kDummyURL2, kTemporary}, - {true, "dom", 0, kDummyURL2, kTemporary}, - {false, "dom/fan", 1319, kDummyURL2, kTemporary}, - {false, "bar", 113, kDummyURL2, kTemporary}, - {true, NULL, 0, kDummyURL2, kPersistent}, - {true, "dom", 0, kDummyURL2, kPersistent}, - {false, "dom/fan", 2013, kDummyURL2, kPersistent}, - {false, "baz", 18, kDummyURL2, kPersistent}, - }; - CreateFiles(files, ARRAYSIZE_UNSAFE(files)); - - for (int i = 0; i < 2; i++) { - EXPECT_EQ(1331 + 134 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kTemporary)); - EXPECT_EQ(1903 + 19 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL1, kPersistent)); - EXPECT_EQ(1319 + 113 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL2, kTemporary)); - EXPECT_EQ(2013 + 18 + kUsageFileSize, - GetOriginUsage(tracker.get(), kDummyURL2, kPersistent)); - } -} diff --git a/webkit/fileapi/file_system_util.cc b/webkit/fileapi/file_system_util.cc index f3e6278..5aa3cf2 100644 --- a/webkit/fileapi/file_system_util.cc +++ b/webkit/fileapi/file_system_util.cc @@ -128,4 +128,27 @@ GURL GetFileSystemRootURI( return GURL(path); } +FileSystemType QuotaStorageTypeToFileSystemType( + quota::StorageType storage_type) { + switch (storage_type) { + case quota::kStorageTypeTemporary: + return kFileSystemTypeTemporary; + case quota::kStorageTypePersistent: + return kFileSystemTypePersistent; + default: + return kFileSystemTypeUnknown; + } +} + +quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type) { + switch (type) { + case kFileSystemTypeTemporary: + return quota::kStorageTypeTemporary; + case kFileSystemTypePersistent: + return quota::kStorageTypePersistent; + default: + return quota::kStorageTypeUnknown; + } +} + } // namespace fileapi diff --git a/webkit/fileapi/file_system_util.h b/webkit/fileapi/file_system_util.h index 272e432..6f3bdca 100644 --- a/webkit/fileapi/file_system_util.h +++ b/webkit/fileapi/file_system_util.h @@ -7,6 +7,7 @@ #pragma once #include "webkit/fileapi/file_system_types.h" +#include "webkit/quota/quota_types.h" class FilePath; class GURL; @@ -27,6 +28,11 @@ bool CrackFileSystemURL(const GURL& url, GURL* origin_url, FileSystemType* type, GURL GetFileSystemRootURI(const GURL& origin_url, fileapi::FileSystemType type); +FileSystemType QuotaStorageTypeToFileSystemType( + quota::StorageType storage_type); + +quota::StorageType FileSystemTypeToQuotaStorageType(FileSystemType type); + } // namespace fileapi #endif // WEBKIT_FILEAPI_FILE_SYSTEM_UTIL_H_ diff --git a/webkit/fileapi/file_writer_delegate_unittest.cc b/webkit/fileapi/file_writer_delegate_unittest.cc index c25a69f..d56c7ae 100644 --- a/webkit/fileapi/file_writer_delegate_unittest.cc +++ b/webkit/fileapi/file_writer_delegate_unittest.cc @@ -208,7 +208,7 @@ void FileWriterDelegateTest::SetUp() { context_.reset(new FileSystemOperationContext( new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - NULL, FilePath(), false /* is_incognito */, + NULL, NULL, FilePath(), false /* is_incognito */, true, true, new MockFileSystemPathManager(filesystem_dir_)), NULL)); @@ -345,7 +345,7 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { context2.reset(new FileSystemOperationContext( new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - NULL, FilePath(), false /* is_incognito */, + NULL, NULL, FilePath(), false /* is_incognito */, true, true, new MockFileSystemPathManager(filesystem_dir_)), NULL)); diff --git a/webkit/fileapi/local_file_system_file_util_unittest.cc b/webkit/fileapi/local_file_system_file_util_unittest.cc index c60d6c8..26ba04d 100644 --- a/webkit/fileapi/local_file_system_file_util_unittest.cc +++ b/webkit/fileapi/local_file_system_file_util_unittest.cc @@ -74,7 +74,7 @@ class LocalFileSystemFileUtilTest : public testing::Test { return new FileSystemOperationContext( new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - NULL, FilePath(), false /* is_incognito */, + NULL, NULL, FilePath(), false /* is_incognito */, true, true, new MockFileSystemPathManager(filesystem_dir_)), FileUtil()); diff --git a/webkit/fileapi/quota_file_util_unittest.cc b/webkit/fileapi/quota_file_util_unittest.cc index b01a178..bb76f65 100644 --- a/webkit/fileapi/quota_file_util_unittest.cc +++ b/webkit/fileapi/quota_file_util_unittest.cc @@ -62,7 +62,7 @@ class QuotaFileUtilTest : public testing::Test { FileSystemOperationContext *context = new FileSystemOperationContext( new FileSystemContext(base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), - NULL, FilePath(), false, + NULL, NULL, FilePath(), false, true, true, new MockFileSystemPathManager(filesystem_dir_)), QuotaFileUtil::GetInstance()); diff --git a/webkit/fileapi/sandbox_mount_point_provider.cc b/webkit/fileapi/sandbox_mount_point_provider.cc index 51954ce..ad46f3b 100644 --- a/webkit/fileapi/sandbox_mount_point_provider.cc +++ b/webkit/fileapi/sandbox_mount_point_provider.cc @@ -19,6 +19,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" #include "webkit/fileapi/file_system_path_manager.h" #include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/sandbox_mount_point_provider.h" #include "webkit/glue/webkit_glue.h" namespace { @@ -51,6 +52,28 @@ inline std::string FilePathStringToASCII( #endif } +// TODO(kinuko): Merge these two methods (conversion methods between +// origin url <==> identifier) with the ones in the database module. +std::string GetOriginIdentifierFromURL(const GURL& url) { + WebKit::WebSecurityOrigin web_security_origin = + WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); + return web_security_origin.databaseIdentifier().utf8(); +} + +GURL GetOriginURLFromIdentifier(const std::string& origin_identifier) { + WebKit::WebSecurityOrigin web_security_origin = + WebKit::WebSecurityOrigin::createFromDatabaseIdentifier( + UTF8ToUTF16(origin_identifier)); + GURL origin_url(web_security_origin.toString()); + + // We need this work-around for file:/// URIs as + // createFromDatabaseIdentifier returns empty origin_url for them. + if (origin_url.spec().empty() && + origin_identifier.find("file__") == 0) + return GURL("file:///"); + return origin_url; +} + FilePath::StringType CreateUniqueDirectoryName(const GURL& origin_url) { // This can be anything but need to be unpredictable. static const FilePath::CharType letters[] = FILE_PATH_LITERAL( @@ -104,6 +127,36 @@ FilePath GetFileSystemRootPathOnFileThreadHelper( return root; } +class SandboxOriginEnumerator + : public fileapi::SandboxMountPointProvider::OriginEnumerator { + public: + explicit SandboxOriginEnumerator(const FilePath& base_path) + : enumerator_(base_path, false /* recursive */, + file_util::FileEnumerator::DIRECTORIES) {} + virtual ~SandboxOriginEnumerator() {} + + virtual GURL Next() OVERRIDE { + current_ = enumerator_.Next(); + if (current_.empty()) + return GURL(); + return GetOriginURLFromIdentifier( + FilePathStringToASCII(current_.BaseName().value())); + } + + virtual bool HasFileSystemType(fileapi::FileSystemType type) const OVERRIDE { + if (current_.empty()) + return false; + std::string directory = + fileapi::FileSystemPathManager::GetFileSystemTypeString(type); + DCHECK(!directory.empty()); + return file_util::DirectoryExists(current_.AppendASCII(directory)); + } + + private: + file_util::FileEnumerator enumerator_; + FilePath current_; +}; + } // anonymous namespace namespace fileapi { @@ -220,6 +273,11 @@ std::vector<FilePath> SandboxMountPointProvider::GetRootDirectories() const { return std::vector<FilePath>(); } +SandboxMountPointProvider::OriginEnumerator* +SandboxMountPointProvider::CreateOriginEnumerator() const { + return new SandboxOriginEnumerator(base_path_); +} + void SandboxMountPointProvider::ValidateFileSystemRootAndGetURL( const GURL& origin_url, fileapi::FileSystemType type, bool create, FileSystemPathManager::GetRootPathCallback* callback_ptr) { @@ -251,49 +309,20 @@ SandboxMountPointProvider::ValidateFileSystemRootAndGetPathOnFileThread( origin_url, origin_base_path, create); } -// static -std::string SandboxMountPointProvider::GetOriginIdentifierFromURL( - const GURL& url) { - WebKit::WebSecurityOrigin web_security_origin = - WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); - return web_security_origin.databaseIdentifier().utf8(); +FilePath SandboxMountPointProvider::GetBaseDirectoryForOrigin( + const GURL& origin_url) const { + return base_path_.AppendASCII(GetOriginIdentifierFromURL(origin_url)); } -// static -FilePath SandboxMountPointProvider::GetFileSystemBaseDirectoryForOriginAndType( - const FilePath& base_path, const std::string& origin_identifier, - fileapi::FileSystemType type) { - if (origin_identifier.empty()) - return FilePath(); +FilePath SandboxMountPointProvider::GetBaseDirectoryForOriginAndType( + const GURL& origin_url, fileapi::FileSystemType type) const { std::string type_string = FileSystemPathManager::GetFileSystemTypeString(type); if (type_string.empty()) { LOG(WARNING) << "Unknown filesystem type is requested:" << type; return FilePath(); } - return base_path.AppendASCII(origin_identifier) - .AppendASCII(type_string); -} - -SandboxMountPointProvider::OriginEnumerator::OriginEnumerator( - const FilePath& base_path) - : enumerator_(base_path, false /* recursive */, - file_util::FileEnumerator::DIRECTORIES) { -} - -std::string SandboxMountPointProvider::OriginEnumerator::Next() { - current_ = enumerator_.Next(); - return FilePathStringToASCII(current_.BaseName().value()); -} - -bool SandboxMountPointProvider::OriginEnumerator::HasTemporary() { - return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( - fileapi::kTemporaryName)); -} - -bool SandboxMountPointProvider::OriginEnumerator::HasPersistent() { - return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( - fileapi::kPersistentName)); + return GetBaseDirectoryForOrigin(origin_url).AppendASCII(type_string); } bool SandboxMountPointProvider::GetOriginBasePathAndName( @@ -309,12 +338,11 @@ bool SandboxMountPointProvider::GetOriginBasePathAndName( if (!path_manager_->IsAllowedScheme(origin_url)) return false; - std::string origin_identifier = GetOriginIdentifierFromURL(origin_url); - *origin_base_path = GetFileSystemBaseDirectoryForOriginAndType( - base_path(), origin_identifier, type); + *origin_base_path = GetBaseDirectoryForOriginAndType(origin_url, type); if (origin_base_path->empty()) return false; + std::string origin_identifier = GetOriginIdentifierFromURL(origin_url); std::string type_string = FileSystemPathManager::GetFileSystemTypeString(type); DCHECK(!type_string.empty()); diff --git a/webkit/fileapi/sandbox_mount_point_provider.h b/webkit/fileapi/sandbox_mount_point_provider.h index 6f8cff8..748ced8 100644 --- a/webkit/fileapi/sandbox_mount_point_provider.h +++ b/webkit/fileapi/sandbox_mount_point_provider.h @@ -12,8 +12,6 @@ #include "googleurl/src/gurl.h" #include "webkit/fileapi/file_system_mount_point_provider.h" -class GURL; - namespace base { class MessageLoopProxy; } @@ -22,6 +20,18 @@ namespace fileapi { class SandboxMountPointProvider : public FileSystemMountPointProvider { public: + // Origin enumerator interface. + // An instance of this interface is assumed to be called on the file thread. + class OriginEnumerator { + public: + virtual ~OriginEnumerator() {} + + // Returns the next origin. Returns empty if there are no more origins. + virtual GURL Next() = 0; + + // Returns the current origin's information. + virtual bool HasFileSystemType(FileSystemType type) const = 0; + }; SandboxMountPointProvider( FileSystemPathManager* path_manager, @@ -63,40 +73,26 @@ class SandboxMountPointProvider : public FileSystemMountPointProvider { virtual std::vector<FilePath> GetRootDirectories() const; - // Returns the origin identifier string, which is used as a part of the - // sandboxed path component, for the given |url|. - static std::string GetOriginIdentifierFromURL(const GURL& url); + // Returns an origin enumerator of this provider. + // This method is supposed to be called on the file thread. + OriginEnumerator* CreateOriginEnumerator() const; // Gets a base directory path of the sandboxed filesystem that is - // specified by |origin_identifier| and |type|. - // |base_path| must be pointing the FileSystem's data directory - // under the profile directory, i.e. <profile_dir>/kFileSystemDirectory. - // Returns an empty path if any of the given parameters are invalid. - // Returned directory path does not contain 'unique' part, therefore - // it is not an actual root path for the filesystem. - static FilePath GetFileSystemBaseDirectoryForOriginAndType( - const FilePath& base_path, - const std::string& origin_identifier, - fileapi::FileSystemType type); - - // Enumerates origins under the given |base_path|. - // This must be used on the FILE thread. - class OriginEnumerator { - public: - explicit OriginEnumerator(const FilePath& base_path); - - // Returns the next origin identifier. Returns empty if there are no - // more origins. - std::string Next(); + // specified by |origin_url|. + // (The path is similar to the origin's root path but doesn't contain + // the 'unique' and 'type' part.) + // This method is portable and can be called on any threads. + FilePath GetBaseDirectoryForOrigin(const GURL& origin_url) const; - bool HasTemporary(); - bool HasPersistent(); - const FilePath& path() { return current_; } - - private: - file_util::FileEnumerator enumerator_; - FilePath current_; - }; + // Gets a base directory path of the sandboxed filesystem that is + // specified by |origin_url| and |type|. + // (The path is similar to the origin's root path but doesn't contain + // the 'unique' part.) + // Returns an empty path if the given type is invalid. + // This method is portable and can be called on any threads. + FilePath GetBaseDirectoryForOriginAndType( + const GURL& origin_url, + fileapi::FileSystemType type) const; private: bool GetOriginBasePathAndName( @@ -121,4 +117,3 @@ class SandboxMountPointProvider : public FileSystemMountPointProvider { } // namespace fileapi #endif // WEBKIT_FILEAPI_SANDBOX_MOUNT_POINT_PROVIDER_H_ - diff --git a/webkit/fileapi/sandbox_mount_point_provider_unittest.cc b/webkit/fileapi/sandbox_mount_point_provider_unittest.cc index 767f0ef..54f6865 100644 --- a/webkit/fileapi/sandbox_mount_point_provider_unittest.cc +++ b/webkit/fileapi/sandbox_mount_point_provider_unittest.cc @@ -22,12 +22,20 @@ using namespace fileapi; +class MockFileSystemPathManager : public FileSystemPathManager { + public: + MockFileSystemPathManager(const FilePath& filesystem_path) + : FileSystemPathManager(base::MessageLoopProxy::CreateForCurrentThread(), + filesystem_path, NULL, false, true) {} +}; + class SandboxMountPointProviderOriginEnumeratorTest : public testing::Test { public: void SetUp() { ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); - enumerator_.reset(new SandboxMountPointProvider::OriginEnumerator( - data_dir_.path())); + path_manager_.reset(new MockFileSystemPathManager(data_dir_.path())); + enumerator_.reset( + path_manager_->sandbox_provider()->CreateOriginEnumerator()); } SandboxMountPointProvider::OriginEnumerator* enumerator() const { @@ -35,61 +43,60 @@ class SandboxMountPointProviderOriginEnumeratorTest : public testing::Test { } protected: - void CreateOriginTypeDirectory(const std::string& origin_identifier, + void CreateOriginTypeDirectory(const GURL& origin, fileapi::FileSystemType type) { - std::string type_string = - FileSystemPathManager::GetFileSystemTypeString(type); - ASSERT_TRUE(!type_string.empty()); - FilePath target = data_dir_.path().AppendASCII(origin_identifier) - .AppendASCII(type_string); + FilePath target = path_manager_->sandbox_provider()-> + GetBaseDirectoryForOriginAndType(origin, type); file_util::CreateDirectory(target); ASSERT_TRUE(file_util::DirectoryExists(target)); } ScopedTempDir data_dir_; + scoped_ptr<FileSystemPathManager> path_manager_; scoped_ptr<SandboxMountPointProvider::OriginEnumerator> enumerator_; }; TEST_F(SandboxMountPointProviderOriginEnumeratorTest, Empty) { - ASSERT_TRUE(enumerator()->Next().empty()); + ASSERT_TRUE(enumerator()->Next().is_empty()); } TEST_F(SandboxMountPointProviderOriginEnumeratorTest, EnumerateOrigins) { const char* temporary_origins[] = { - "http_www.bar.com_0", - "http_www.foo.com_0", - "http_www.foo.com_80", - "http_www.example.com_8080", - "http_www.google.com_80", + "http://www.bar.com/", + "http://www.foo.com/", + "http://www.foo.com:1/", + "http://www.example.com:8080/", + "http://www.google.com:80/", }; const char* persistent_origins[] = { - "http_www.bar.com_0", - "http_www.foo.com_8080", - "http_www.foo.com_80", + "http://www.bar.com/", + "http://www.foo.com:8080/", + "http://www.foo.com:80/", }; size_t temporary_size = ARRAYSIZE_UNSAFE(temporary_origins); size_t persistent_size = ARRAYSIZE_UNSAFE(persistent_origins); - std::set<std::string> temporary_set, persistent_set; + std::set<GURL> temporary_set, persistent_set; for (size_t i = 0; i < temporary_size; ++i) { - CreateOriginTypeDirectory(temporary_origins[i], + CreateOriginTypeDirectory(GURL(temporary_origins[i]), fileapi::kFileSystemTypeTemporary); - temporary_set.insert(temporary_origins[i]); + temporary_set.insert(GURL(temporary_origins[i])); } for (size_t i = 0; i < persistent_size; ++i) { - CreateOriginTypeDirectory(persistent_origins[i], kFileSystemTypePersistent); - persistent_set.insert(persistent_origins[i]); + CreateOriginTypeDirectory(GURL(persistent_origins[i]), + kFileSystemTypePersistent); + persistent_set.insert(GURL(persistent_origins[i])); } size_t temporary_actual_size = 0; size_t persistent_actual_size = 0; - std::string current; - while (!(current = enumerator()->Next()).empty()) { - SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current); - if (enumerator()->HasTemporary()) { + GURL current; + while (!(current = enumerator()->Next()).is_empty()) { + SCOPED_TRACE(testing::Message() << "EnumerateOrigin " << current.spec()); + if (enumerator()->HasFileSystemType(kFileSystemTypeTemporary)) { ASSERT_TRUE(temporary_set.find(current) != temporary_set.end()); ++temporary_actual_size; } - if (enumerator()->HasPersistent()) { + if (enumerator()->HasFileSystemType(kFileSystemTypePersistent)) { ASSERT_TRUE(persistent_set.find(current) != persistent_set.end()); ++persistent_actual_size; } diff --git a/webkit/fileapi/sandbox_quota_client.cc b/webkit/fileapi/sandbox_quota_client.cc new file mode 100644 index 0000000..b35e7ba --- /dev/null +++ b/webkit/fileapi/sandbox_quota_client.cc @@ -0,0 +1,292 @@ +// 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/fileapi/sandbox_quota_client.h" + +#include <algorithm> +#include <set> + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/task.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_util.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_path_manager.h" +#include "webkit/fileapi/file_system_usage_cache.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/sandbox_mount_point_provider.h" + +using namespace std; + +using base::MessageLoopProxy; +using quota::QuotaThreadTask; +using quota::StorageType; + +namespace fileapi { + +class SandboxQuotaClient::GetOriginUsageTask : public QuotaThreadTask { + public: + GetOriginUsageTask( + SandboxQuotaClient* quota_client, + scoped_refptr<MessageLoopProxy> file_message_loop, + const GURL& origin_url, + FileSystemType type) + : QuotaThreadTask(quota_client, file_message_loop), + quota_client_(quota_client), + origin_url_(origin_url), + type_(type), + fs_usage_(0) { + DCHECK(quota_client_); + file_system_context_ = quota_client_->file_system_context_; + visited_ = (quota_client_->visited_origins_.find(origin_url) != + quota_client_->visited_origins_.end()); + } + + virtual ~GetOriginUsageTask() {} + + protected: + virtual void RunOnTargetThread() OVERRIDE { + FilePath base_path = + file_system_context_->path_manager()->sandbox_provider()-> + GetBaseDirectoryForOriginAndType(origin_url_, type_); + if (!file_util::DirectoryExists(base_path)) { + fs_usage_ = 0; + } else { + FilePath usage_file_path = base_path.AppendASCII( + FileSystemUsageCache::kUsageFileName); + int32 dirty_status = FileSystemUsageCache::GetDirty(usage_file_path); + if (dirty_status == 0 || (dirty_status > 0 && visited_)) { + // The usage cache is clean (dirty == 0) or the origin has already + // initialized and running. Read the cache file to get the usage. + fs_usage_ = FileSystemUsageCache::GetUsage(usage_file_path); + } else { + // The usage cache has not been initialized or the cache is dirty. + // Get the directory size now and update the cache. + if (FileSystemUsageCache::Exists(usage_file_path)) + FileSystemUsageCache::Delete(usage_file_path); + int64 usage = file_util::ComputeDirectorySize(base_path); + // The result of ComputeDirectorySize does not include .usage file size. + usage += FileSystemUsageCache::kUsageFileSize; + // This clears the dirty flag too. + FileSystemUsageCache::UpdateUsage(usage_file_path, usage); + fs_usage_ = usage; + } + } + } + + virtual void Completed() OVERRIDE { + quota_client_->DidGetOriginUsage(type_, origin_url_, fs_usage_); + } + + SandboxQuotaClient* quota_client_; + scoped_refptr<FileSystemContext> file_system_context_; + GURL origin_url_; + FileSystemType type_; + int64 fs_usage_; + bool visited_; +}; + +class SandboxQuotaClient::GetOriginsTaskBase : public QuotaThreadTask { + public: + GetOriginsTaskBase( + SandboxQuotaClient* quota_client, + scoped_refptr<MessageLoopProxy> file_message_loop, + FileSystemType type) + : QuotaThreadTask(quota_client, file_message_loop), + quota_client_(quota_client), + type_(type) { + DCHECK(quota_client_); + file_system_context_ = quota_client_->file_system_context_; + } + virtual ~GetOriginsTaskBase() {} + + protected: + virtual bool ShouldAddThisOrigin(const GURL& origin) const = 0; + + virtual void RunOnTargetThread() OVERRIDE { + scoped_ptr<SandboxMountPointProvider::OriginEnumerator> enumerator( + sandbox_provider()->CreateOriginEnumerator()); + GURL origin; + while (!(origin = enumerator->Next()).is_empty()) { + if (ShouldAddThisOrigin(origin) && enumerator->HasFileSystemType(type_)) + origins_.insert(origin); + } + } + + SandboxQuotaClient* quota_client() const { return quota_client_; } + const std::set<GURL>& origins() const { return origins_; } + FileSystemType type() const { return type_; } + SandboxMountPointProvider* sandbox_provider() const { + return file_system_context_->path_manager()->sandbox_provider(); + } + + private: + SandboxQuotaClient * quota_client_; + scoped_refptr<FileSystemContext> file_system_context_; + std::set<GURL> origins_; + FileSystemType type_; +}; + +class SandboxQuotaClient::GetOriginsForTypeTask + : public SandboxQuotaClient::GetOriginsTaskBase { + public: + GetOriginsForTypeTask( + SandboxQuotaClient* quota_client, + scoped_refptr<MessageLoopProxy> file_message_loop, + FileSystemType type) + : GetOriginsTaskBase(quota_client, file_message_loop, type) {} + virtual ~GetOriginsForTypeTask() {} + + protected: + virtual bool ShouldAddThisOrigin(const GURL& origin) const OVERRIDE { + return true; + } + + virtual void Completed() OVERRIDE { + quota_client()->DidGetOriginsForType(type(), origins()); + } +}; + +quota::QuotaClient::ID SandboxQuotaClient::id() const { + return quota::QuotaClient::kFileSystem; +} + +void SandboxQuotaClient::OnQuotaManagerDestroyed() { + delete this; +} + +class SandboxQuotaClient::GetOriginsForHostTask + : public SandboxQuotaClient::GetOriginsTaskBase { + public: + GetOriginsForHostTask( + SandboxQuotaClient* quota_client, + scoped_refptr<MessageLoopProxy> file_message_loop, + FileSystemType type, + const std::string& host) + : GetOriginsTaskBase(quota_client, file_message_loop, type), + host_(host) {} + virtual ~GetOriginsForHostTask() {} + + protected: + virtual bool ShouldAddThisOrigin(const GURL& origin) const OVERRIDE { + return (host_ == net::GetHostOrSpecFromURL(origin)); + } + + virtual void Completed() OVERRIDE { + quota_client()->DidGetOriginsForHost(std::make_pair(type(), host_), + origins()); + } + + private: + std::string host_; +}; + +SandboxQuotaClient::SandboxQuotaClient( + scoped_refptr<base::MessageLoopProxy> file_message_loop, + FileSystemContext* file_system_context, + bool is_incognito) + : file_message_loop_(file_message_loop), + file_system_context_(file_system_context), + is_incognito_(is_incognito) { + DCHECK(file_message_loop); +} + +SandboxQuotaClient::~SandboxQuotaClient() { +} + +void SandboxQuotaClient::GetOriginUsage( + const GURL& origin_url, + StorageType storage_type, + GetUsageCallback* callback_ptr) { + DCHECK(callback_ptr); + scoped_ptr<GetUsageCallback> callback(callback_ptr); + + if (is_incognito_) { + // We don't support FileSystem in incognito mode yet. + callback->Run(0); + return; + } + + FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); + DCHECK(type != kFileSystemTypeUnknown); + + if (pending_usage_callbacks_.Add( + std::make_pair(type, origin_url.spec()), callback.release())) { + scoped_refptr<GetOriginUsageTask> task( + new GetOriginUsageTask(this, file_message_loop_, origin_url, type)); + task->Start(); + } +} + +void SandboxQuotaClient::GetOriginsForType( + StorageType storage_type, + GetOriginsCallback* callback_ptr) { + std::set<GURL> origins; + scoped_ptr<GetOriginsCallback> callback(callback_ptr); + if (is_incognito_) { + // We don't support FileSystem in incognito mode yet. + callback->Run(origins); + return; + } + + FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); + DCHECK(type != kFileSystemTypeUnknown); + + if (pending_origins_for_type_callbacks_.Add(type, callback.release())) { + scoped_refptr<GetOriginsForTypeTask> task( + new GetOriginsForTypeTask(this, file_message_loop_, type)); + task->Start(); + } +} + +void SandboxQuotaClient::GetOriginsForHost( + StorageType storage_type, + const std::string& host, + GetOriginsCallback* callback_ptr) { + std::set<GURL> origins; + scoped_ptr<GetOriginsCallback> callback(callback_ptr); + if (is_incognito_) { + // We don't support FileSystem in incognito mode yet. + callback->Run(origins); + return; + } + + FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type); + DCHECK(type != kFileSystemTypeUnknown); + + if (pending_origins_for_host_callbacks_.Add( + std::make_pair(type, host), callback.release())) { + scoped_refptr<GetOriginsForHostTask> task( + new GetOriginsForHostTask(this, file_message_loop_, + type, host)); + task->Start(); + } +} + +void SandboxQuotaClient::DidGetOriginUsage( + FileSystemType type, const GURL& origin_url, int64 usage) { + visited_origins_.insert(origin_url); + TypeAndHostOrOrigin type_and_origin(std::make_pair( + type, origin_url.spec())); + DCHECK(pending_usage_callbacks_.HasCallbacks(type_and_origin)); + pending_usage_callbacks_.Run(type_and_origin, usage); +} + +void SandboxQuotaClient::DidGetOriginsForType( + FileSystemType type, const std::set<GURL>& origins) { + DCHECK(pending_origins_for_type_callbacks_.HasCallbacks(type)); + pending_origins_for_type_callbacks_.Run(type, origins); +} + +void SandboxQuotaClient::DidGetOriginsForHost( + const TypeAndHostOrOrigin& type_and_host, const std::set<GURL>& origins) { + DCHECK(pending_origins_for_host_callbacks_.HasCallbacks(type_and_host)); + pending_origins_for_host_callbacks_.Run(type_and_host, origins); +} + +} // namespace fileapi diff --git a/webkit/fileapi/sandbox_quota_client.h b/webkit/fileapi/sandbox_quota_client.h new file mode 100644 index 0000000..7aae5fb --- /dev/null +++ b/webkit/fileapi/sandbox_quota_client.h @@ -0,0 +1,93 @@ +// 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_FILEAPI_SANDBOX_QUOTA_CLIENT_H_ +#define WEBKIT_FILEAPI_SANDBOX_QUOTA_CLIENT_H_ + +#include <deque> +#include <list> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "webkit/fileapi/file_system_path_manager.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/quota/quota_client.h" +#include "webkit/quota/quota_task.h" + +namespace fileapi { + +class FileSystemContext; + +// An instance of this class is created per-profile. This class +// is self-destructed and will delete itself when OnQuotaManagerDestroyed +// is called. +class SandboxQuotaClient : public quota::QuotaClient, + public quota::QuotaTaskObserver { + public: + SandboxQuotaClient( + scoped_refptr<base::MessageLoopProxy> file_message_loop, + FileSystemContext* file_system_context, + bool is_incognito); + virtual ~SandboxQuotaClient(); + + // QuotaClient methods. + virtual quota::QuotaClient::ID id() const OVERRIDE; + virtual void OnQuotaManagerDestroyed() OVERRIDE; + virtual void GetOriginUsage(const GURL& origin_url, + quota::StorageType type, + GetUsageCallback* callback) OVERRIDE; + virtual void GetOriginsForType(quota::StorageType type, + GetOriginsCallback* callback) OVERRIDE; + virtual void GetOriginsForHost(quota::StorageType type, + const std::string& host, + GetOriginsCallback* callback) OVERRIDE; + + private: + class GetOriginUsageTask; + class GetOriginsTaskBase; + class GetOriginsForTypeTask; + class GetOriginsForHostTask; + + typedef std::pair<fileapi::FileSystemType, std::string> TypeAndHostOrOrigin; + typedef quota::CallbackQueueMap1<GetUsageCallback*, + TypeAndHostOrOrigin, + int64 + > UsageCallbackMap; + typedef quota::CallbackQueueMap1<GetOriginsCallback*, + fileapi::FileSystemType, + const std::set<GURL>& + > OriginsForTypeCallbackMap; + typedef quota::CallbackQueueMap1<GetOriginsCallback*, + TypeAndHostOrOrigin, + const std::set<GURL>& + > OriginsForHostCallbackMap; + + void DidGetOriginUsage(fileapi::FileSystemType type, + const GURL& origin, int64 usage); + void DidGetOriginsForType(fileapi::FileSystemType type, + const std::set<GURL>& origins); + void DidGetOriginsForHost(const TypeAndHostOrOrigin& type_and_host, + const std::set<GURL>& origins); + + scoped_refptr<base::MessageLoopProxy> file_message_loop_; + scoped_refptr<FileSystemContext> file_system_context_; + + bool is_incognito_; + + std::set<GURL> visited_origins_; + + // Pending callbacks. + UsageCallbackMap pending_usage_callbacks_; + OriginsForTypeCallbackMap pending_origins_for_type_callbacks_; + OriginsForHostCallbackMap pending_origins_for_host_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(SandboxQuotaClient); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_SANDBOX_QUOTA_CLIENT_H_ diff --git a/webkit/fileapi/sandbox_quota_client_unittest.cc b/webkit/fileapi/sandbox_quota_client_unittest.cc new file mode 100644 index 0000000..c77e423 --- /dev/null +++ b/webkit/fileapi/sandbox_quota_client_unittest.cc @@ -0,0 +1,431 @@ +// 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 "base/basictypes.h" +#include "base/file_util.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/memory/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/platform_file.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_usage_cache.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/sandbox_mount_point_provider.h" +#include "webkit/fileapi/sandbox_quota_client.h" +#include "webkit/quota/quota_types.h" + +using namespace fileapi; + +namespace { +const char kDummyURL1[] = "http://www.dummy.org"; +const char kDummyURL2[] = "http://www.example.com"; +const char kDummyURL3[] = "http://www.bleh"; + +// Declared to shorten the variable names. +const quota::StorageType kTemporary = quota::kStorageTypeTemporary; +const quota::StorageType kPersistent = quota::kStorageTypePersistent; +const int kUsageFileSize = FileSystemUsageCache::kUsageFileSize; + +class MockFileSystemPathManager : public FileSystemPathManager { + public: + MockFileSystemPathManager(const FilePath& filesystem_path) + : FileSystemPathManager(base::MessageLoopProxy::CreateForCurrentThread(), + filesystem_path, NULL, false, true) {} +}; +} + +class SandboxQuotaClientTest : public testing::Test { + public: + SandboxQuotaClientTest() + : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + additional_callback_count_(0) { + } + + void SetUp() { + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + file_system_context_ = + new FileSystemContext( + base::MessageLoopProxy::CreateForCurrentThread(), + base::MessageLoopProxy::CreateForCurrentThread(), + NULL, NULL, + FilePath(), false /* is_incognito */, + false, true, + new MockFileSystemPathManager(data_dir_.path())); + } + + struct TestFile { + bool isDirectory; + const char* name; + int64 size; + const char* origin_url; + quota::StorageType type; + }; + + protected: + SandboxQuotaClient* NewQuotaClient(bool is_incognito) { + return new SandboxQuotaClient( + base::MessageLoopProxy::CreateForCurrentThread(), + file_system_context_, is_incognito); + } + + void GetOriginUsageAsync(SandboxQuotaClient* quota_client, + const char* origin_url, + quota::StorageType type) { + quota_client->GetOriginUsage(GURL(origin_url), type, + callback_factory_.NewCallback( + &SandboxQuotaClientTest::OnGetUsage)); + } + + int64 GetOriginUsage(SandboxQuotaClient* quota_client, + const char* origin_url, + quota::StorageType type) { + GetOriginUsageAsync(quota_client, origin_url, type); + MessageLoop::current()->RunAllPending(); + return usage_; + } + + const std::set<GURL>& GetOriginsForType(SandboxQuotaClient* quota_client, + quota::StorageType type) { + origins_.clear(); + quota_client->GetOriginsForType(type, + callback_factory_.NewCallback( + &SandboxQuotaClientTest::OnGetOrigins)); + MessageLoop::current()->RunAllPending(); + return origins_; + } + + const std::set<GURL>& GetOriginsForHost(SandboxQuotaClient* quota_client, + quota::StorageType type, + const char* host) { + origins_.clear(); + quota_client->GetOriginsForHost(type, host, + callback_factory_.NewCallback( + &SandboxQuotaClientTest::OnGetOrigins)); + MessageLoop::current()->RunAllPending(); + return origins_; + } + + void RunAdditionalOriginUsageTask(SandboxQuotaClient* quota_client, + const char* origin_url, + quota::StorageType type) { + quota_client->GetOriginUsage(GURL(origin_url), type, + callback_factory_.NewCallback( + &SandboxQuotaClientTest::OnGetAdditionalUsage)); + } + + FilePath GetOriginBasePath(const char* origin_url, + quota::StorageType type) { + return file_system_context_->path_manager()->sandbox_provider()-> + GetBaseDirectoryForOriginAndType( + GURL(origin_url), QuotaStorageTypeToFileSystemType(type)); + } + + bool CreateFileSystemDirectory(const char* dir_name, + const char* origin_url, + quota::StorageType type) { + FilePath origin_base_path = GetOriginBasePath(origin_url, type); + FilePath dir_path; + if (dir_name != NULL) + dir_path = origin_base_path.AppendASCII(dir_name); + else + dir_path = origin_base_path; + if (dir_path.empty()) + return false; + + return file_util::CreateDirectory(dir_path); + } + + bool CreateFileSystemFile(const char* file_name, + int64 file_size, + const char* origin_url, + quota::StorageType type) { + FilePath origin_base_path = GetOriginBasePath(origin_url, type); + FilePath file_path = origin_base_path.AppendASCII(file_name); + + if (file_path.empty()) + return false; + + int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE; + base::PlatformFileError error_code; + base::PlatformFile file = + base::CreatePlatformFile(file_path, file_flags, NULL, &error_code); + if (error_code != base::PLATFORM_FILE_OK) + return false; + + bool succeeded; + succeeded = base::TruncatePlatformFile(file, file_size); + succeeded = succeeded && base::ClosePlatformFile(file); + return succeeded; + } + + void CreateFiles(const TestFile* files, int num_files) { + for (int i = 0; i < num_files; i++) { + if (files[i].isDirectory) { + ASSERT_TRUE(CreateFileSystemDirectory( + files[i].name, files[i].origin_url, files[i].type)); + } else { + ASSERT_TRUE(CreateFileSystemFile( + files[i].name, files[i].size, files[i].origin_url, files[i].type)); + } + } + } + + int64 usage() const { return usage_; } + int additional_callback_count() const { return additional_callback_count_; } + void set_additional_callback_count(int count) { + additional_callback_count_ = count; + } + + private: + void OnGetUsage(int64 usage) { + usage_ = usage; + } + + void OnGetOrigins(const std::set<GURL>& origins) { + origins_ = origins; + } + + void OnGetAdditionalUsage(int64) { + ++additional_callback_count_; + } + + ScopedTempDir data_dir_; + scoped_refptr<FileSystemContext> file_system_context_; + base::ScopedCallbackFactory<SandboxQuotaClientTest> callback_factory_; + int64 usage_; + int additional_callback_count_; + std::set<GURL> origins_; + + DISALLOW_COPY_AND_ASSIGN(SandboxQuotaClientTest); +}; + +TEST_F(SandboxQuotaClientTest, NoFileSystemTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + + EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); +} + +TEST_F(SandboxQuotaClientTest, NoFileTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + } +} + +TEST_F(SandboxQuotaClientTest, OneFileTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {false, "foo", 4921, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(4921 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + } +} + +TEST_F(SandboxQuotaClientTest, TwoFilesTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {false, "foo", 10310, kDummyURL1, kTemporary}, + {false, "bar", 41, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(10310 + 41 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + } +} + +TEST_F(SandboxQuotaClientTest, EmptyFilesTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {false, "foo", 0, kDummyURL1, kTemporary}, + {false, "bar", 0, kDummyURL1, kTemporary}, + {false, "baz", 0, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + } +} + +TEST_F(SandboxQuotaClientTest, SubDirectoryTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {true, "dirtest", 0, kDummyURL1, kTemporary}, + {false, "dirtest/foo", 11921, kDummyURL1, kTemporary}, + {false, "bar", 4814, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(11921 + 4814 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + } +} + +TEST_F(SandboxQuotaClientTest, MultiTypeTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {true, "dirtest", 0, kDummyURL1, kTemporary}, + {false, "dirtest/foo", 133, kDummyURL1, kTemporary}, + {false, "bar", 14, kDummyURL1, kTemporary}, + {true, NULL, 0, kDummyURL1, kPersistent}, + {true, "dirtest", 0, kDummyURL1, kPersistent}, + {false, "dirtest/foo", 193, kDummyURL1, kPersistent}, + {false, "bar", 9, kDummyURL1, kPersistent}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(133 + 14 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + EXPECT_EQ(193 + 9 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); + } +} + +TEST_F(SandboxQuotaClientTest, MultiDomainTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {true, "dir1", 0, kDummyURL1, kTemporary}, + {false, "dir1/foo", 1331, kDummyURL1, kTemporary}, + {false, "bar", 134, kDummyURL1, kTemporary}, + {true, NULL, 0, kDummyURL1, kPersistent}, + {true, "dir2", 0, kDummyURL1, kPersistent}, + {false, "dir2/foo", 1903, kDummyURL1, kPersistent}, + {false, "bar", 19, kDummyURL1, kPersistent}, + {true, NULL, 0, kDummyURL2, kTemporary}, + {true, "dom", 0, kDummyURL2, kTemporary}, + {false, "dom/fan", 1319, kDummyURL2, kTemporary}, + {false, "bar", 113, kDummyURL2, kTemporary}, + {true, NULL, 0, kDummyURL2, kPersistent}, + {true, "dom", 0, kDummyURL2, kPersistent}, + {false, "dom/fan", 2013, kDummyURL2, kPersistent}, + {false, "baz", 18, kDummyURL2, kPersistent}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + for (int i = 0; i < 2; i++) { + EXPECT_EQ(1331 + 134 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + EXPECT_EQ(1903 + 19 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); + EXPECT_EQ(1319 + 113 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL2, kTemporary)); + EXPECT_EQ(2013 + 18 + kUsageFileSize, + GetOriginUsage(quota_client.get(), kDummyURL2, kPersistent)); + } +} + +TEST_F(SandboxQuotaClientTest, GetUsage_MultipleTasks) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {false, "foo", 11, kDummyURL1, kTemporary}, + {false, "bar", 22, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + // Dispatching three GetUsage tasks. + set_additional_callback_count(0); + GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary); + RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); + RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(11 + 22 + kUsageFileSize, usage()); + EXPECT_EQ(2, additional_callback_count()); + + // Once more, in a different order. + set_additional_callback_count(0); + RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); + GetOriginUsageAsync(quota_client.get(), kDummyURL1, kTemporary); + RunAdditionalOriginUsageTask(quota_client.get(), kDummyURL1, kTemporary); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(11 + 22 + kUsageFileSize, usage()); + EXPECT_EQ(2, additional_callback_count()); +} + +TEST_F(SandboxQuotaClientTest, GetOriginsForType) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {true, NULL, 0, kDummyURL2, kTemporary}, + {true, NULL, 0, kDummyURL3, kPersistent}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary); + EXPECT_EQ(2U, origins.size()); + EXPECT_TRUE(origins.find(GURL(kDummyURL1)) != origins.end()); + EXPECT_TRUE(origins.find(GURL(kDummyURL2)) != origins.end()); + EXPECT_TRUE(origins.find(GURL(kDummyURL3)) == origins.end()); +} + +TEST_F(SandboxQuotaClientTest, GetOriginsForHost) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(false)); + const char* kURL1 = "http://foo.com/"; + const char* kURL2 = "https://foo.com/"; + const char* kURL3 = "http://foo.com:1/"; + const char* kURL4 = "http://foo2.com/"; + const char* kURL5 = "http://foo.com:2/"; + const TestFile kFiles[] = { + {true, NULL, 0, kURL1, kTemporary}, + {true, NULL, 0, kURL2, kTemporary}, + {true, NULL, 0, kURL3, kTemporary}, + {true, NULL, 0, kURL4, kTemporary}, + {true, NULL, 0, kURL5, kPersistent}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + std::set<GURL> origins = GetOriginsForHost( + quota_client.get(), kTemporary, "foo.com"); + EXPECT_EQ(3U, origins.size()); + EXPECT_TRUE(origins.find(GURL(kURL1)) != origins.end()); + EXPECT_TRUE(origins.find(GURL(kURL2)) != origins.end()); + EXPECT_TRUE(origins.find(GURL(kURL3)) != origins.end()); + EXPECT_TRUE(origins.find(GURL(kURL4)) == origins.end()); // Different host. + EXPECT_TRUE(origins.find(GURL(kURL5)) == origins.end()); // Different type. +} + +TEST_F(SandboxQuotaClientTest, IncognitoTest) { + scoped_ptr<SandboxQuotaClient> quota_client(NewQuotaClient(true)); + const TestFile kFiles[] = { + {true, NULL, 0, kDummyURL1, kTemporary}, + {false, "foo", 10, kDummyURL1, kTemporary}, + }; + CreateFiles(kFiles, ARRAYSIZE_UNSAFE(kFiles)); + + // Having files in the usual directory wouldn't affect the result + // queried in incognito mode. + EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kTemporary)); + EXPECT_EQ(0, GetOriginUsage(quota_client.get(), kDummyURL1, kPersistent)); + + std::set<GURL> origins = GetOriginsForType(quota_client.get(), kTemporary); + EXPECT_EQ(0U, origins.size()); + origins = GetOriginsForHost(quota_client.get(), kTemporary, "www.dummy.org"); + EXPECT_EQ(0U, origins.size()); +} diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index 4d0ef77..aa70b0b 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -13,6 +13,7 @@ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/net/net.gyp:net', '<(DEPTH)/third_party/leveldb/leveldb.gyp:leveldb', + '<(DEPTH)/webkit/support/webkit_support.gyp:quota', ], 'sources': [ 'file_system_callback_dispatcher.cc', @@ -41,20 +42,20 @@ 'file_system_url_request_job.h', 'file_system_url_request_job_base.cc', 'file_system_url_request_job_base.h', - 'file_system_usage_tracker.cc', - 'file_system_usage_tracker.h', + 'file_system_usage_cache.cc', + 'file_system_usage_cache.h', 'file_system_util.cc', 'file_system_util.h', - 'file_system_usage_cache.h', - 'file_system_usage_cache.cc', 'file_writer_delegate.cc', 'file_writer_delegate.h', 'local_file_system_file_util.cc', 'local_file_system_file_util.h', - 'sandbox_mount_point_provider.cc', - 'sandbox_mount_point_provider.h', 'quota_file_util.cc', 'quota_file_util.h', + 'sandbox_mount_point_provider.cc', + 'sandbox_mount_point_provider.h', + 'sandbox_quota_client.cc', + 'sandbox_quota_client.h', 'webfilewriter_base.cc', 'webfilewriter_base.h', ], diff --git a/webkit/quota/mock_storage_client.cc b/webkit/quota/mock_storage_client.cc index 93fe909..ee1a0b1 100644 --- a/webkit/quota/mock_storage_client.cc +++ b/webkit/quota/mock_storage_client.cc @@ -40,8 +40,8 @@ class MockStorageClientIDSequencer { } // anonymous namespace -MockStorageClient::MockStorageClient(QuotaManager* qm) - : quota_manager_(qm), +MockStorageClient::MockStorageClient(QuotaManagerProxy* quota_manager_proxy) + : quota_manager_proxy_(quota_manager_proxy), id_(MockStorageClientIDSequencer::GetInstance()->NextMockID()), runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } @@ -52,10 +52,6 @@ MockStorageClient::~MockStorageClient() { origins_callbacks_.begin(), origins_callbacks_.end()); } -QuotaClient::ID MockStorageClient::id() const { - return id_; -} - void MockStorageClient::AddMockOriginData( const GURL& origin_url, StorageType type, int64 size) { origin_data_.insert(std::make_pair(origin_url, MockOriginData(type, size))); @@ -69,7 +65,15 @@ void MockStorageClient::ModifyMockOriginDataSize( AddMockOriginData(origin_url, type, delta); return; } - quota_manager_->NotifyStorageModified(id(), origin_url, type, delta); + quota_manager_proxy_->NotifyStorageModified(id(), origin_url, type, delta); +} + +QuotaClient::ID MockStorageClient::id() const { + return id_; +} + +void MockStorageClient::OnQuotaManagerDestroyed() { + delete this; } void MockStorageClient::GetOriginUsage(const GURL& origin_url, diff --git a/webkit/quota/mock_storage_client.h b/webkit/quota/mock_storage_client.h index 3a803ff..ac2545b 100644 --- a/webkit/quota/mock_storage_client.h +++ b/webkit/quota/mock_storage_client.h @@ -14,25 +14,22 @@ namespace quota { -class QuotaManager; +class QuotaManagerProxy; // Mock storage class for testing. class MockStorageClient : public QuotaClient { public: - MockStorageClient(QuotaManager* qm); + MockStorageClient(QuotaManagerProxy* quota_manager_proxy); virtual ~MockStorageClient(); - virtual QuotaClient::ID id() const OVERRIDE; - // 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 QuotaClient::ID id() const OVERRIDE; + virtual void OnQuotaManagerDestroyed() OVERRIDE; virtual void GetOriginUsage(const GURL& origin_url, StorageType type, GetUsageCallback* callback) OVERRIDE; @@ -51,7 +48,7 @@ class MockStorageClient : public QuotaClient { const std::string& host, GetOriginsCallback* callback); - QuotaManager* quota_manager_; + scoped_refptr<QuotaManagerProxy> quota_manager_proxy_; const ID id_; struct MockOriginData { diff --git a/webkit/quota/quota_client.h b/webkit/quota/quota_client.h index 1852e27..41db15b 100644 --- a/webkit/quota/quota_client.h +++ b/webkit/quota/quota_client.h @@ -37,6 +37,9 @@ class QuotaClient { virtual ID id() const = 0; + // Called when the quota manager is destroyed. + virtual void OnQuotaManagerDestroyed() = 0; + // Called by the QuotaManager. // Gets the amount of data stored in the storage specified by // |origin_url| and |type|. diff --git a/webkit/quota/quota_manager.cc b/webkit/quota/quota_manager.cc index 647131c..0f18532 100644 --- a/webkit/quota/quota_manager.cc +++ b/webkit/quota/quota_manager.cc @@ -345,21 +345,22 @@ QuotaManager::QuotaManager(bool is_incognito, scoped_refptr<base::MessageLoopProxy> db_thread) : is_incognito_(is_incognito), profile_path_(profile_path), + proxy_(new QuotaManagerProxy( + ALLOW_THIS_IN_INITIALIZER_LIST(this), io_thread)), 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)) { + temporary_global_quota_(-1) { } QuotaManager::~QuotaManager() { - STLDeleteContainerPointers(clients_.begin(), clients_.end()); -} - -void QuotaManager::RegisterClient(QuotaClient* client) { - DCHECK(!database_.get()); - clients_.push_back(client); + DCHECK(io_thread_->BelongsToCurrentThread()); + proxy_->manager_ = NULL; + std::for_each(clients_.begin(), clients_.end(), + std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed)); + if (database_.get()) + db_thread_->DeleteSoon(FROM_HERE, database_.release()); } void QuotaManager::GetUsageAndQuota( @@ -399,15 +400,6 @@ void QuotaManager::RequestQuota( 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) { @@ -478,6 +470,21 @@ void QuotaManager::LazyInitialize() { task->Start(); } +void QuotaManager::RegisterClient(QuotaClient* client) { + DCHECK(io_thread_->BelongsToCurrentThread()); + DCHECK(!database_.get()); + clients_.push_back(client); +} + +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); +} + UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const { switch (type) { case kStorageTypeTemporary: @@ -499,13 +506,10 @@ 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); + persistent_host_quota_callbacks_.Run(host, 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; @@ -513,4 +517,41 @@ void QuotaManager::DeleteOnCorrectThread() const { delete this; } +// QuotaManagerProxy ---------------------------------------------------------- + +void QuotaManagerProxy::RegisterClient(QuotaClient* client) { + if (!io_thread_->BelongsToCurrentThread()) { + io_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &QuotaManagerProxy::RegisterClient, client)); + return; + } + if (manager_) + manager_->RegisterClient(client); + else + client->OnQuotaManagerDestroyed(); +} + +void QuotaManagerProxy::NotifyStorageModified( + QuotaClient::ID client_id, + const GURL& origin, + StorageType type, + int64 delta) { + if (!io_thread_->BelongsToCurrentThread()) { + io_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &QuotaManagerProxy::NotifyStorageModified, + client_id, origin, type, delta)); + return; + } + if (manager_) + manager_->NotifyStorageModified(client_id, origin, type, delta); +} + +QuotaManagerProxy::QuotaManagerProxy( + QuotaManager* manager, base::MessageLoopProxy* io_thread) + : manager_(manager), io_thread_(io_thread) { +} + +QuotaManagerProxy::~QuotaManagerProxy() { +} + } // namespace quota diff --git a/webkit/quota/quota_manager.h b/webkit/quota/quota_manager.h index a9a458c..eb8d70f 100644 --- a/webkit/quota/quota_manager.h +++ b/webkit/quota/quota_manager.h @@ -28,9 +28,11 @@ class QuotaDatabase; class UsageTracker; struct QuotaManagerDeleter; +class QuotaManagerProxy; // The quota manager class. This class is instantiated per profile and -// held by the profile. +// held by the profile. With the exception of the constructor and the +// proxy() method, all methods should only be called on the IO thread. class QuotaManager : public QuotaTaskObserver, public base::RefCountedThreadSafe< QuotaManager, QuotaManagerDeleter> { @@ -48,26 +50,20 @@ class QuotaManager : public QuotaTaskObserver, virtual ~QuotaManager(); - // The client's ownership is transferred to the manager. - void RegisterClient(QuotaClient* client); + // Returns a proxy object that can be used on any thread. + QuotaManagerProxy* proxy() { return proxy_.get(); } - // Called by clients or webapps on the IO thread. - void GetUsageAndQuota(const GURL& origin, StorageType type, + // Called by clients or webapps. + void GetUsageAndQuota(const GURL& origin, + StorageType type, GetUsageAndQuotaCallback* callback); - // Called by webapps on the IO thread. - void RequestQuota(const GURL& origin, StorageType type, + // Called by webapps. + 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); @@ -97,23 +93,39 @@ class QuotaManager : public QuotaTaskObserver, typedef std::map<HostAndType, UsageAndQuotaDispatcherTask*> UsageAndQuotaDispatcherTaskMap; + friend struct QuotaManagerDeleter; + friend class QuotaManagerProxy; + // 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(); + // Called by clients via proxy. + // Registers a quota client to the manager. + // The client must remain valid until OnQuotaManagerDestored is called. + void RegisterClient(QuotaClient* client); + + // Called by clients via proxy. + // 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); + 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_; + scoped_refptr<QuotaManagerProxy> proxy_; bool db_initialized_; bool db_disabled_; scoped_refptr<base::MessageLoopProxy> io_thread_; @@ -133,8 +145,6 @@ class QuotaManager : public QuotaTaskObserver, std::map<std::string, int64> persistent_host_quota_; HostQuotaCallbackMap persistent_host_quota_callbacks_; - base::ScopedCallbackFactory<QuotaManager> callback_factory_; - DISALLOW_COPY_AND_ASSIGN(QuotaManager); }; @@ -144,6 +154,28 @@ struct QuotaManagerDeleter { } }; +// The proxy may be called and finally released on any thread. +class QuotaManagerProxy + : public base::RefCountedThreadSafe<QuotaManagerProxy> { + public: + void RegisterClient(QuotaClient* client); + void NotifyStorageModified(QuotaClient::ID client_id, + const GURL& origin, + StorageType type, + int64 delta); + private: + friend class QuotaManager; + friend class base::RefCountedThreadSafe<QuotaManagerProxy>; + QuotaManagerProxy(QuotaManager* manager, base::MessageLoopProxy* io_thread); + ~QuotaManagerProxy(); + + QuotaManager* manager_; // only accessed on the io thread + scoped_refptr<base::MessageLoopProxy> io_thread_; + + DISALLOW_COPY_AND_ASSIGN(QuotaManagerProxy); +}; + + } // namespace quota #endif // WEBKIT_QUOTA_QUOTA_MANAGER_H_ diff --git a/webkit/quota/quota_manager_unittest.cc b/webkit/quota/quota_manager_unittest.cc index 47a4c27..eb0af8f 100644 --- a/webkit/quota/quota_manager_unittest.cc +++ b/webkit/quota/quota_manager_unittest.cc @@ -12,6 +12,7 @@ #include "base/memory/scoped_temp_dir.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" +#include "base/stl_util-inl.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" @@ -57,8 +58,7 @@ class QuotaManagerTest : public testing::Test { protected: MockStorageClient* CreateClient( const MockOriginData* mock_data, size_t mock_data_size) { - MockStorageClient* client( - new MockStorageClient(quota_manager_.get())); + MockStorageClient* client = new MockStorageClient(quota_manager_->proxy()); for (size_t i = 0; i < mock_data_size; ++i) { client->AddMockOriginData(GURL(mock_data[i].origin), mock_data[i].type, @@ -68,7 +68,7 @@ class QuotaManagerTest : public testing::Test { } void RegisterClient(MockStorageClient* client) { - quota_manager_->RegisterClient(client); + quota_manager_->proxy()->RegisterClient(client); } void GetUsageAndQuota(const GURL& origin, StorageType type) { diff --git a/webkit/quota/quota_types.h b/webkit/quota/quota_types.h index 9c67b34..c01b97a 100644 --- a/webkit/quota/quota_types.h +++ b/webkit/quota/quota_types.h @@ -68,6 +68,7 @@ class CallbackQueue1 : public CallbackQueueBase<CallbackType1> { typedef typename CallbackQueueBase<CallbackType1>::Queue Queue; // Runs the callbacks added to the queue and clears the queue. void Run(A1 arg) { + // Note: template-derived class needs 'this->' to access its base class. for (typename Queue::iterator iter = this->callbacks_.begin(); iter != this->callbacks_.end(); ++iter) { (*iter)->Run(arg); @@ -95,15 +96,13 @@ class CallbackQueue2 : public CallbackQueueBase<CallbackType2> { typedef CallbackQueue1<UsageCallback*, int64> UsageCallbackQueue; typedef CallbackQueue1<QuotaCallback*, int64> QuotaCallbackQueue; -template <typename CallbackType2, typename KEY = std::string, - typename ARG = int64> -class CallbackQueueMap { +template <typename CallbackType, typename CallbackQueueType, typename KEY> +class CallbackQueueMapBase { public: - typedef CallbackQueue2<CallbackType2, const KEY&, ARG> Queue; - typedef std::map<KEY, Queue> CallbackMap; + typedef std::map<KEY, CallbackQueueType> CallbackMap; typedef typename CallbackMap::iterator iterator; - bool Add(const KEY& key, CallbackType2 callback) { + bool Add(const KEY& key, CallbackType callback) { return callback_map_[key].Add(callback); } @@ -111,30 +110,65 @@ class CallbackQueueMap { return (callback_map_.find(key) != callback_map_.end()); } + iterator Begin() { return callback_map_.begin(); } + iterator End() { return callback_map_.end(); } + + void Clear() { callback_map_.clear(); } + + protected: + CallbackMap callback_map_; +}; + +template <typename CallbackType1, typename KEY, typename ARG> +class CallbackQueueMap1 + : public CallbackQueueMapBase<CallbackType1, + CallbackQueue1<CallbackType1, ARG>, + KEY> { + public: + typedef typename CallbackQueueMapBase< + CallbackType1, + CallbackQueue1<CallbackType1, ARG>, + KEY>::iterator iterator; + typedef CallbackQueue1<CallbackType1, ARG> Queue; + // 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)) + if (!this->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); + Queue& queue = this->callback_map_[key]; + queue.Run(arg); + this->callback_map_.erase(key); } +}; - void Clear() { callback_map_.clear(); } +template <typename CallbackType2, typename KEY, typename ARG1, typename ARG2> +class CallbackQueueMap2 + : public CallbackQueueMapBase<CallbackType2, + CallbackQueue2<CallbackType2, ARG1, ARG2>, + KEY> { + public: + typedef typename CallbackQueueMapBase< + CallbackType2, + CallbackQueue2<CallbackType2, ARG1, ARG2>, + KEY>::iterator iterator; + typedef CallbackQueue2<CallbackType2, ARG1, ARG2> Queue; - private: - CallbackMap callback_map_; + // Runs the callbacks added for the given |key| and clears the key + // from the map. + void Run(const KEY& key, ARG1 arg1, ARG2 arg2) { + if (!this->HasCallbacks(key)) + return; + Queue& queue = this->callback_map_[key]; + queue.Run(arg1, arg2); + this->callback_map_.erase(key); + } }; -typedef CallbackQueueMap<HostUsageCallback*> HostUsageCallbackMap; -typedef CallbackQueueMap<HostQuotaCallback*> HostQuotaCallbackMap; +typedef CallbackQueueMap2<HostUsageCallback*, std::string, + const std::string&, int64> HostUsageCallbackMap; +typedef CallbackQueueMap2<HostUsageCallback*, std::string, + const std::string&, int64> HostQuotaCallbackMap; } // namespace quota diff --git a/webkit/quota/usage_tracker.cc b/webkit/quota/usage_tracker.cc index 043373e..52d9782 100644 --- a/webkit/quota/usage_tracker.cc +++ b/webkit/quota/usage_tracker.cc @@ -252,7 +252,7 @@ void UsageTracker::DidGetClientHostUsage(const std::string& host, int64 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); + host_usage_callbacks_.Run(host, host, info.usage); outstanding_host_usage_.erase(host); } } @@ -372,9 +372,9 @@ void ClientUsageTracker::DidGetGlobalUsage( std::map<std::string, int64>::iterator found = host_usage_map_.find(iter->first); if (found == host_usage_map_.end()) - HostUsageCallbackMap::RunAt(iter, 0); + iter->second.Run(iter->first, 0); else - HostUsageCallbackMap::RunAt(iter, found->second); + iter->second.Run(iter->first, found->second); } host_usage_callbacks_.Clear(); } @@ -394,7 +394,7 @@ void ClientUsageTracker::DidGetHostUsage( } // Dispatches the host usage callback. - host_usage_callbacks_.Run(host, host_usage_map_[host]); + host_usage_callbacks_.Run(host, host, host_usage_map_[host]); } } // namespace quota diff --git a/webkit/tools/test_shell/simple_file_system.cc b/webkit/tools/test_shell/simple_file_system.cc index e5959ce..ebda9bc 100644 --- a/webkit/tools/test_shell/simple_file_system.cc +++ b/webkit/tools/test_shell/simple_file_system.cc @@ -132,6 +132,7 @@ SimpleFileSystem::SimpleFileSystem() { base::MessageLoopProxy::CreateForCurrentThread(), base::MessageLoopProxy::CreateForCurrentThread(), NULL /* special storage policy */, + NULL /* quota manager */, file_system_dir_.path(), false /* incognito */, true /* allow_file_access */, diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 490b8aa..699901e 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -382,11 +382,12 @@ '../../fileapi/file_system_origin_database_unittest.cc', '../../fileapi/file_system_path_manager_unittest.cc', '../../fileapi/file_system_usage_cache_unittest.cc', - '../../fileapi/file_system_usage_tracker_unittest.cc', '../../fileapi/file_system_util_unittest.cc', '../../fileapi/local_file_system_file_util_unittest.cc', - '../../fileapi/sandbox_mount_point_provider_unittest.cc', '../../fileapi/quota_file_util_unittest.cc', + '../../fileapi/sandbox_mount_point_provider_unittest.cc', + '../../fileapi/sandbox_mount_point_provider_unittest.cc', + '../../fileapi/sandbox_quota_client_unittest.cc', '../../fileapi/webfilewriter_base_unittest.cc', '../../glue/bookmarklet_unittest.cc', '../../glue/context_menu_unittest.cc', |