summaryrefslogtreecommitdiffstats
path: root/webkit/browser
diff options
context:
space:
mode:
authortzik@chromium.org <tzik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-30 05:12:39 +0000
committertzik@chromium.org <tzik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-30 05:12:39 +0000
commit7660ec943dbb6c73943b71e7f0675add48604bd2 (patch)
tree174a91440979e73b7f6f8675f8b131417729ab44 /webkit/browser
parent6961c1c9fbc14d3d7b646d3f5b92331b60336d8b (diff)
downloadchromium_src-7660ec943dbb6c73943b71e7f0675add48604bd2.zip
chromium_src-7660ec943dbb6c73943b71e7f0675add48604bd2.tar.gz
chromium_src-7660ec943dbb6c73943b71e7f0675add48604bd2.tar.bz2
Move webkit/quota files to webkit/browser/quota or webkit/common/quota
This CL includes: - Move webkit/quota/quota_{types.{h,cc},callbacks.h} webkit/common/quota/, - Move other webkit/quota/ files to webkit/browser/quota/, - #include and include guard fix for the move, - Split webkit/quota/webkit_quota.gypi to webkit/{browser,common}/quota, - Change DEPS to allow #include webkit/common from chrome/browser. BUG=244363 TEST=should build successfully and should pass deps check. NOTRY=True Review URL: https://chromiumcodereview.appspot.com/16010007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203082 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/browser')
-rw-r--r--webkit/browser/chromeos/fileapi/cros_mount_point_provider.h2
-rw-r--r--webkit/browser/chromeos/fileapi/cros_mount_point_provider_unittest.cc2
-rw-r--r--webkit/browser/database/database_quota_client.h4
-rw-r--r--webkit/browser/database/database_tracker.cc4
-rw-r--r--webkit/browser/database/database_tracker_unittest.cc4
-rw-r--r--webkit/browser/fileapi/async_file_test_helper.cc2
-rw-r--r--webkit/browser/fileapi/async_file_test_helper.h2
-rw-r--r--webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc2
-rw-r--r--webkit/browser/fileapi/file_system_context.cc4
-rw-r--r--webkit/browser/fileapi/file_system_context_unittest.cc4
-rw-r--r--webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc2
-rw-r--r--webkit/browser/fileapi/file_system_operation_context.h2
-rw-r--r--webkit/browser/fileapi/file_system_quota_client.h2
-rw-r--r--webkit/browser/fileapi/file_system_quota_client_unittest.cc2
-rw-r--r--webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc4
-rw-r--r--webkit/browser/fileapi/local_file_system_operation.cc4
-rw-r--r--webkit/browser/fileapi/local_file_system_operation.h2
-rw-r--r--webkit/browser/fileapi/local_file_system_operation_unittest.cc4
-rw-r--r--webkit/browser/fileapi/local_file_system_operation_write_unittest.cc2
-rw-r--r--webkit/browser/fileapi/mock_file_system_context.cc2
-rw-r--r--webkit/browser/fileapi/obfuscated_file_util.cc2
-rw-r--r--webkit/browser/fileapi/obfuscated_file_util_unittest.cc6
-rw-r--r--webkit/browser/fileapi/sandbox_file_stream_writer.cc2
-rw-r--r--webkit/browser/fileapi/sandbox_file_stream_writer.h2
-rw-r--r--webkit/browser/fileapi/sandbox_file_system_test_helper.cc2
-rw-r--r--webkit/browser/fileapi/sandbox_file_system_test_helper.h2
-rw-r--r--webkit/browser/fileapi/sandbox_mount_point_provider.cc2
-rw-r--r--webkit/browser/fileapi/sandbox_mount_point_provider.h2
-rw-r--r--webkit/browser/fileapi/sandbox_quota_observer.cc4
-rw-r--r--webkit/browser/fileapi/syncable/canned_syncable_file_system.cc4
-rw-r--r--webkit/browser/fileapi/syncable/canned_syncable_file_system.h4
-rw-r--r--webkit/browser/fileapi/syncable/local_file_change_tracker_unittest.cc2
-rw-r--r--webkit/browser/fileapi/syncable/syncable_file_system_unittest.cc4
-rw-r--r--webkit/browser/fileapi/test_mount_point_provider.cc2
-rw-r--r--webkit/browser/quota/mock_quota_manager.cc199
-rw-r--r--webkit/browser/quota/mock_quota_manager.h197
-rw-r--r--webkit/browser/quota/mock_quota_manager_unittest.cc223
-rw-r--r--webkit/browser/quota/mock_special_storage_policy.cc41
-rw-r--r--webkit/browser/quota/mock_special_storage_policy.h90
-rw-r--r--webkit/browser/quota/mock_storage_client.cc182
-rw-r--r--webkit/browser/quota/mock_storage_client.h95
-rw-r--r--webkit/browser/quota/quota_callbacks.h127
-rw-r--r--webkit/browser/quota/quota_client.h79
-rw-r--r--webkit/browser/quota/quota_database.cc661
-rw-r--r--webkit/browser/quota/quota_database.h186
-rw-r--r--webkit/browser/quota/quota_database_unittest.cc579
-rw-r--r--webkit/browser/quota/quota_manager.cc1668
-rw-r--r--webkit/browser/quota/quota_manager.h471
-rw-r--r--webkit/browser/quota/quota_manager_unittest.cc2165
-rw-r--r--webkit/browser/quota/quota_task.cc79
-rw-r--r--webkit/browser/quota/quota_task.h79
-rw-r--r--webkit/browser/quota/quota_temporary_storage_evictor.cc261
-rw-r--r--webkit/browser/quota/quota_temporary_storage_evictor.h130
-rw-r--r--webkit/browser/quota/quota_temporary_storage_evictor_unittest.cc410
-rw-r--r--webkit/browser/quota/special_storage_policy.cc38
-rw-r--r--webkit/browser/quota/special_storage_policy.h83
-rw-r--r--webkit/browser/quota/usage_tracker.cc597
-rw-r--r--webkit/browser/quota/usage_tracker.h185
-rw-r--r--webkit/browser/quota/usage_tracker_unittest.cc277
-rw-r--r--webkit/browser/quota/webkit_browser_quota.gypi24
-rw-r--r--webkit/browser/webkit_browser.gypi2
61 files changed, 9176 insertions, 48 deletions
diff --git a/webkit/browser/chromeos/fileapi/cros_mount_point_provider.h b/webkit/browser/chromeos/fileapi/cros_mount_point_provider.h
index 4058312..0e88161 100644
--- a/webkit/browser/chromeos/fileapi/cros_mount_point_provider.h
+++ b/webkit/browser/chromeos/fileapi/cros_mount_point_provider.h
@@ -14,8 +14,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "webkit/browser/fileapi/file_system_mount_point_provider.h"
+#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/quota/special_storage_policy.h"
#include "webkit/storage/webkit_storage_export.h"
namespace fileapi {
diff --git a/webkit/browser/chromeos/fileapi/cros_mount_point_provider_unittest.cc b/webkit/browser/chromeos/fileapi/cros_mount_point_provider_unittest.cc
index 9eadd7c..a0c85a2 100644
--- a/webkit/browser/chromeos/fileapi/cros_mount_point_provider_unittest.cc
+++ b/webkit/browser/chromeos/fileapi/cros_mount_point_provider_unittest.cc
@@ -14,7 +14,7 @@
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/isolated_context.h"
-#include "webkit/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
#define FPL(x) FILE_PATH_LITERAL(x)
diff --git a/webkit/browser/database/database_quota_client.h b/webkit/browser/database/database_quota_client.h
index c90c0d5b..69ba10d 100644
--- a/webkit/browser/database/database_quota_client.h
+++ b/webkit/browser/database/database_quota_client.h
@@ -10,8 +10,8 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h"
-#include "webkit/quota/quota_client.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/common/quota/quota_types.h"
#include "webkit/storage/webkit_storage_export.h"
namespace webkit_database {
diff --git a/webkit/browser/database/database_tracker.cc b/webkit/browser/database/database_tracker.cc
index effe322..0866feb 100644
--- a/webkit/browser/database/database_tracker.cc
+++ b/webkit/browser/database/database_tracker.cc
@@ -23,8 +23,8 @@
#include "webkit/browser/database/database_quota_client.h"
#include "webkit/browser/database/database_util.h"
#include "webkit/browser/database/databases_table.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/quota/special_storage_policy.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/special_storage_policy.h"
namespace webkit_database {
diff --git a/webkit/browser/database/database_tracker_unittest.cc b/webkit/browser/database/database_tracker_unittest.cc
index 32bf570..f2b3a90 100644
--- a/webkit/browser/database/database_tracker_unittest.cc
+++ b/webkit/browser/database/database_tracker_unittest.cc
@@ -17,8 +17,8 @@
#include "third_party/sqlite/sqlite3.h"
#include "webkit/base/origin_url_conversions.h"
#include "webkit/browser/database/database_tracker.h"
-#include "webkit/quota/mock_special_storage_policy.h"
-#include "webkit/quota/quota_manager.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/quota_manager.h"
namespace {
diff --git a/webkit/browser/fileapi/async_file_test_helper.cc b/webkit/browser/fileapi/async_file_test_helper.cc
index 796d411..7437293 100644
--- a/webkit/browser/fileapi/async_file_test_helper.cc
+++ b/webkit/browser/fileapi/async_file_test_helper.cc
@@ -9,8 +9,8 @@
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_mount_point_provider.h"
#include "webkit/browser/fileapi/file_system_url.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/async_file_test_helper.h b/webkit/browser/fileapi/async_file_test_helper.h
index b77a856..062634d 100644
--- a/webkit/browser/fileapi/async_file_test_helper.h
+++ b/webkit/browser/fileapi/async_file_test_helper.h
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "webkit/browser/fileapi/file_system_operation.h"
#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/quota/quota_status_code.h"
+#include "webkit/common/quota/quota_status_code.h"
namespace quota {
class QuotaManager;
diff --git a/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc b/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc
index 2be01c8..92b9d04 100644
--- a/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc
+++ b/webkit/browser/fileapi/copy_or_move_file_validator_unittest.cc
@@ -17,8 +17,8 @@
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/fileapi/mock_file_system_context.h"
#include "webkit/browser/fileapi/test_mount_point_provider.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/mock_special_storage_policy.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/file_system_context.cc b/webkit/browser/fileapi/file_system_context.cc
index 676d251..c8bb838 100644
--- a/webkit/browser/fileapi/file_system_context.cc
+++ b/webkit/browser/fileapi/file_system_context.cc
@@ -26,9 +26,9 @@
#include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
#include "webkit/browser/fileapi/test_mount_point_provider.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/quota/special_storage_policy.h"
#if defined(OS_CHROMEOS)
#include "webkit/browser/chromeos/fileapi/cros_mount_point_provider.h"
diff --git a/webkit/browser/fileapi/file_system_context_unittest.cc b/webkit/browser/fileapi/file_system_context_unittest.cc
index 18ead11..c699fd2 100644
--- a/webkit/browser/fileapi/file_system_context_unittest.cc
+++ b/webkit/browser/fileapi/file_system_context_unittest.cc
@@ -13,8 +13,8 @@
#include "webkit/browser/fileapi/file_system_task_runners.h"
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/fileapi/mock_file_system_options.h"
-#include "webkit/quota/mock_quota_manager.h"
-#include "webkit/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
#define FPL(x) FILE_PATH_LITERAL(x)
diff --git a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
index e031b9a..f8cc7de 100644
--- a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
+++ b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
@@ -28,7 +28,7 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/mock_file_system_context.h"
#include "webkit/browser/fileapi/sandbox_mount_point_provider.h"
-#include "webkit/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
namespace fileapi {
namespace {
diff --git a/webkit/browser/fileapi/file_system_operation_context.h b/webkit/browser/fileapi/file_system_operation_context.h
index 752d8d7..32bcb23 100644
--- a/webkit/browser/fileapi/file_system_operation_context.h
+++ b/webkit/browser/fileapi/file_system_operation_context.h
@@ -9,7 +9,7 @@
#include "base/supports_user_data.h"
#include "base/threading/thread_checker.h"
#include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
#include "webkit/storage/webkit_storage_export.h"
namespace base {
diff --git a/webkit/browser/fileapi/file_system_quota_client.h b/webkit/browser/fileapi/file_system_quota_client.h
index 25c7752..7154b41 100644
--- a/webkit/browser/fileapi/file_system_quota_client.h
+++ b/webkit/browser/fileapi/file_system_quota_client.h
@@ -14,8 +14,8 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "webkit/browser/fileapi/file_system_quota_util.h"
+#include "webkit/browser/quota/quota_client.h"
#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/quota/quota_client.h"
#include "webkit/storage/webkit_storage_export.h"
namespace base {
diff --git a/webkit/browser/fileapi/file_system_quota_client_unittest.cc b/webkit/browser/fileapi/file_system_quota_client_unittest.cc
index 2f7bb39..ade671a 100644
--- a/webkit/browser/fileapi/file_system_quota_client_unittest.cc
+++ b/webkit/browser/fileapi/file_system_quota_client_unittest.cc
@@ -20,7 +20,7 @@
#include "webkit/browser/fileapi/sandbox_mount_point_provider.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
namespace fileapi {
namespace {
diff --git a/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc b/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc
index 7a31908..1f1ad04 100644
--- a/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc
+++ b/webkit/browser/fileapi/local_file_system_cross_operation_unittest.cc
@@ -19,9 +19,9 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/mock_file_system_context.h"
#include "webkit/browser/fileapi/test_file_set.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/mock_quota_manager.h"
-#include "webkit/quota/quota_manager.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/local_file_system_operation.cc b/webkit/browser/fileapi/local_file_system_operation.cc
index a85316b..765be5c 100644
--- a/webkit/browser/fileapi/local_file_system_operation.cc
+++ b/webkit/browser/fileapi/local_file_system_operation.cc
@@ -22,11 +22,11 @@
#include "webkit/browser/fileapi/file_writer_delegate.h"
#include "webkit/browser/fileapi/remove_operation_delegate.h"
#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
using webkit_blob::ScopedFile;
using webkit_blob::ShareableFileReference;
diff --git a/webkit/browser/fileapi/local_file_system_operation.h b/webkit/browser/fileapi/local_file_system_operation.h
index ee5fe8d..9321c44 100644
--- a/webkit/browser/fileapi/local_file_system_operation.h
+++ b/webkit/browser/fileapi/local_file_system_operation.h
@@ -13,7 +13,7 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/file_writer_delegate.h"
#include "webkit/common/blob/scoped_file.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
#include "webkit/storage/webkit_storage_export.h"
namespace chromeos {
diff --git a/webkit/browser/fileapi/local_file_system_operation_unittest.cc b/webkit/browser/fileapi/local_file_system_operation_unittest.cc
index fec56e3..19a2467 100644
--- a/webkit/browser/fileapi/local_file_system_operation_unittest.cc
+++ b/webkit/browser/fileapi/local_file_system_operation_unittest.cc
@@ -20,10 +20,10 @@
#include "webkit/browser/fileapi/file_system_operation_context.h"
#include "webkit/browser/fileapi/mock_file_change_observer.h"
#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/blob/shareable_file_reference.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/mock_quota_manager.h"
-#include "webkit/quota/quota_manager.h"
using quota::QuotaManager;
using quota::QuotaManagerProxy;
diff --git a/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc b/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc
index bc42130..28849ac 100644
--- a/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc
+++ b/webkit/browser/fileapi/local_file_system_operation_write_unittest.cc
@@ -26,9 +26,9 @@
#include "webkit/browser/fileapi/local_file_util.h"
#include "webkit/browser/fileapi/mock_file_change_observer.h"
#include "webkit/browser/fileapi/mock_file_system_context.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/mock_quota_manager.h"
using webkit_blob::MockBlobURLRequestContext;
using webkit_blob::ScopedTextBlob;
diff --git a/webkit/browser/fileapi/mock_file_system_context.cc b/webkit/browser/fileapi/mock_file_system_context.cc
index 5e222b8..4c1956e 100644
--- a/webkit/browser/fileapi/mock_file_system_context.cc
+++ b/webkit/browser/fileapi/mock_file_system_context.cc
@@ -11,7 +11,7 @@
#include "webkit/browser/fileapi/file_system_task_runners.h"
#include "webkit/browser/fileapi/mock_file_system_options.h"
#include "webkit/browser/fileapi/test_mount_point_provider.h"
-#include "webkit/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/obfuscated_file_util.cc b/webkit/browser/fileapi/obfuscated_file_util.cc
index 2971fe2..59dd7a8 100644
--- a/webkit/browser/fileapi/obfuscated_file_util.cc
+++ b/webkit/browser/fileapi/obfuscated_file_util.cc
@@ -26,8 +26,8 @@
#include "webkit/browser/fileapi/native_file_util.h"
#include "webkit/browser/fileapi/sandbox_mount_point_provider.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
// Example of various paths:
// void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
diff --git a/webkit/browser/fileapi/obfuscated_file_util_unittest.cc b/webkit/browser/fileapi/obfuscated_file_util_unittest.cc
index a0e788d..0450772 100644
--- a/webkit/browser/fileapi/obfuscated_file_util_unittest.cc
+++ b/webkit/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -26,9 +26,9 @@
#include "webkit/browser/fileapi/obfuscated_file_util.h"
#include "webkit/browser/fileapi/sandbox_file_system_test_helper.h"
#include "webkit/browser/fileapi/test_file_set.h"
-#include "webkit/quota/mock_special_storage_policy.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/common/quota/quota_types.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/sandbox_file_stream_writer.cc b/webkit/browser/fileapi/sandbox_file_stream_writer.cc
index 161111f..cfdf25f 100644
--- a/webkit/browser/fileapi/sandbox_file_stream_writer.cc
+++ b/webkit/browser/fileapi/sandbox_file_stream_writer.cc
@@ -14,8 +14,8 @@
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation.h"
#include "webkit/browser/fileapi/local_file_stream_writer.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/sandbox_file_stream_writer.h b/webkit/browser/fileapi/sandbox_file_stream_writer.h
index b6cce10..829f4c8 100644
--- a/webkit/browser/fileapi/sandbox_file_stream_writer.h
+++ b/webkit/browser/fileapi/sandbox_file_stream_writer.h
@@ -13,7 +13,7 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
#include "webkit/storage/webkit_storage_export.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/sandbox_file_system_test_helper.cc b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
index 0e9ba0b..5abcd45 100644
--- a/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
+++ b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
@@ -17,8 +17,8 @@
#include "webkit/browser/fileapi/local_file_system_operation.h"
#include "webkit/browser/fileapi/mock_file_system_context.h"
#include "webkit/browser/fileapi/sandbox_mount_point_provider.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/mock_special_storage_policy.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/sandbox_file_system_test_helper.h b/webkit/browser/fileapi/sandbox_file_system_test_helper.h
index c711713..5a8042b 100644
--- a/webkit/browser/fileapi/sandbox_file_system_test_helper.h
+++ b/webkit/browser/fileapi/sandbox_file_system_test_helper.h
@@ -14,7 +14,7 @@
#include "webkit/browser/fileapi/file_system_usage_cache.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
namespace base {
class FilePath;
diff --git a/webkit/browser/fileapi/sandbox_mount_point_provider.cc b/webkit/browser/fileapi/sandbox_mount_point_provider.cc
index 0527b14..9d24b3f1 100644
--- a/webkit/browser/fileapi/sandbox_mount_point_provider.cc
+++ b/webkit/browser/fileapi/sandbox_mount_point_provider.cc
@@ -27,9 +27,9 @@
#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
#include "webkit/browser/fileapi/sandbox_quota_observer.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_operation.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
using quota::QuotaManagerProxy;
diff --git a/webkit/browser/fileapi/sandbox_mount_point_provider.h b/webkit/browser/fileapi/sandbox_mount_point_provider.h
index 3ae0ed1..d67afbb 100644
--- a/webkit/browser/fileapi/sandbox_mount_point_provider.h
+++ b/webkit/browser/fileapi/sandbox_mount_point_provider.h
@@ -19,7 +19,7 @@
#include "webkit/browser/fileapi/file_system_options.h"
#include "webkit/browser/fileapi/file_system_quota_util.h"
#include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
-#include "webkit/quota/special_storage_policy.h"
+#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/storage/webkit_storage_export.h"
namespace base {
diff --git a/webkit/browser/fileapi/sandbox_quota_observer.cc b/webkit/browser/fileapi/sandbox_quota_observer.cc
index 0661a8e6..05fa511 100644
--- a/webkit/browser/fileapi/sandbox_quota_observer.cc
+++ b/webkit/browser/fileapi/sandbox_quota_observer.cc
@@ -8,9 +8,9 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/file_system_usage_cache.h"
#include "webkit/browser/fileapi/sandbox_mount_point_provider.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_client.h"
-#include "webkit/quota/quota_manager.h"
namespace fileapi {
diff --git a/webkit/browser/fileapi/syncable/canned_syncable_file_system.cc b/webkit/browser/fileapi/syncable/canned_syncable_file_system.cc
index 53f3430..33b4e3e 100644
--- a/webkit/browser/fileapi/syncable/canned_syncable_file_system.cc
+++ b/webkit/browser/fileapi/syncable/canned_syncable_file_system.cc
@@ -24,8 +24,8 @@
#include "webkit/browser/fileapi/syncable/local_file_change_tracker.h"
#include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
-#include "webkit/quota/mock_special_storage_policy.h"
-#include "webkit/quota/quota_manager.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/quota_manager.h"
using base::PlatformFileError;
using fileapi::FileSystemContext;
diff --git a/webkit/browser/fileapi/syncable/canned_syncable_file_system.h b/webkit/browser/fileapi/syncable/canned_syncable_file_system.h
index 9fb4831..7d0b52e 100644
--- a/webkit/browser/fileapi/syncable/canned_syncable_file_system.h
+++ b/webkit/browser/fileapi/syncable/canned_syncable_file_system.h
@@ -16,10 +16,10 @@
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/syncable/local_file_sync_status.h"
#include "webkit/browser/fileapi/syncable/sync_status_code.h"
+#include "webkit/browser/quota/quota_callbacks.h"
#include "webkit/common/fileapi/file_system_types.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_callbacks.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
namespace base {
class MessageLoopProxy;
diff --git a/webkit/browser/fileapi/syncable/local_file_change_tracker_unittest.cc b/webkit/browser/fileapi/syncable/local_file_change_tracker_unittest.cc
index 78fb5fe..834389d 100644
--- a/webkit/browser/fileapi/syncable/local_file_change_tracker_unittest.cc
+++ b/webkit/browser/fileapi/syncable/local_file_change_tracker_unittest.cc
@@ -21,7 +21,7 @@
#include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
#include "webkit/browser/fileapi/syncable/sync_status_code.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
-#include "webkit/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager.h"
using fileapi::FileSystemContext;
using fileapi::FileSystemURL;
diff --git a/webkit/browser/fileapi/syncable/syncable_file_system_unittest.cc b/webkit/browser/fileapi/syncable/syncable_file_system_unittest.cc
index a1d25e1..36e5b6a 100644
--- a/webkit/browser/fileapi/syncable/syncable_file_system_unittest.cc
+++ b/webkit/browser/fileapi/syncable/syncable_file_system_unittest.cc
@@ -15,9 +15,9 @@
#include "webkit/browser/fileapi/syncable/local_file_change_tracker.h"
#include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_types.h"
-#include "webkit/quota/quota_manager.h"
-#include "webkit/quota/quota_types.h"
+#include "webkit/common/quota/quota_types.h"
using base::PlatformFileError;
using fileapi::FileSystemContext;
diff --git a/webkit/browser/fileapi/test_mount_point_provider.cc b/webkit/browser/fileapi/test_mount_point_provider.cc
index c005d0b..0c87035 100644
--- a/webkit/browser/fileapi/test_mount_point_provider.cc
+++ b/webkit/browser/fileapi/test_mount_point_provider.cc
@@ -19,8 +19,8 @@
#include "webkit/browser/fileapi/local_file_util.h"
#include "webkit/browser/fileapi/native_file_util.h"
#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
+#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/fileapi/file_system_util.h"
-#include "webkit/quota/quota_manager.h"
namespace fileapi {
diff --git a/webkit/browser/quota/mock_quota_manager.cc b/webkit/browser/quota/mock_quota_manager.cc
new file mode 100644
index 0000000..30a0ebf
--- /dev/null
+++ b/webkit/browser/quota/mock_quota_manager.cc
@@ -0,0 +1,199 @@
+// Copyright 2013 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/browser/quota/mock_quota_manager.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "googleurl/src/gurl.h"
+
+namespace quota {
+
+MockQuotaManager::OriginInfo::OriginInfo(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified)
+ : origin(origin),
+ type(type),
+ quota_client_mask(quota_client_mask),
+ modified(modified) {
+}
+
+MockQuotaManager::OriginInfo::~OriginInfo() {}
+
+MockQuotaManager::StorageInfo::StorageInfo() : usage(0), quota(kint64max) {}
+MockQuotaManager::StorageInfo::~StorageInfo() {}
+
+// MockQuotaManager ----------------------------------------------------------
+
+MockQuotaManager::MockQuotaManager(
+ bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy)
+ : QuotaManager(is_incognito, profile_path, io_thread, db_thread,
+ special_storage_policy),
+ weak_factory_(this) {
+}
+
+void MockQuotaManager::GetUsageAndQuota(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) {
+ StorageInfo& info = usage_and_quota_map_[std::make_pair(origin, type)];
+ callback.Run(quota::kQuotaStatusOk, info.usage, info.quota);
+}
+
+void MockQuotaManager::SetQuota(const GURL& origin, StorageType type,
+ int64 quota) {
+ usage_and_quota_map_[std::make_pair(origin, type)].quota = quota;
+}
+
+bool MockQuotaManager::AddOrigin(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified) {
+ origins_.push_back(OriginInfo(origin, type, quota_client_mask, modified));
+ return true;
+}
+
+bool MockQuotaManager::OriginHasData(
+ const GURL& origin,
+ StorageType type,
+ QuotaClient::ID quota_client) const {
+ for (std::vector<OriginInfo>::const_iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->origin == origin &&
+ current->type == type &&
+ current->quota_client_mask & quota_client)
+ return true;
+ }
+ return false;
+}
+
+void MockQuotaManager::GetOriginsModifiedSince(
+ StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback) {
+ std::set<GURL>* origins_to_return = new std::set<GURL>();
+ for (std::vector<OriginInfo>::const_iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->type == type && current->modified >= modified_since)
+ origins_to_return->insert(current->origin);
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockQuotaManager::DidGetModifiedSince,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Owned(origins_to_return),
+ type));
+}
+
+void MockQuotaManager::DeleteOriginData(
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback) {
+ for (std::vector<OriginInfo>::iterator current = origins_.begin();
+ current != origins_.end();
+ ++current) {
+ if (current->origin == origin && current->type == type) {
+ // Modify the mask: if it's 0 after "deletion", remove the origin.
+ current->quota_client_mask &= ~quota_client_mask;
+ if (current->quota_client_mask == 0)
+ origins_.erase(current);
+ break;
+ }
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockQuotaManager::DidDeleteOriginData,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ kQuotaStatusOk));
+}
+
+MockQuotaManager::~MockQuotaManager() {}
+
+void MockQuotaManager::UpdateUsage(
+ const GURL& origin, StorageType type, int64 delta) {
+ usage_and_quota_map_[std::make_pair(origin, type)].usage += delta;
+}
+
+void MockQuotaManager::DidGetModifiedSince(
+ const GetOriginsCallback& callback,
+ std::set<GURL>* origins,
+ StorageType storage_type) {
+ callback.Run(*origins, storage_type);
+}
+
+void MockQuotaManager::DidDeleteOriginData(
+ const StatusCallback& callback,
+ QuotaStatusCode status) {
+ callback.Run(status);
+}
+
+// MockQuotaManagerProxy -----------------------------------------------------
+
+MockQuotaManagerProxy::MockQuotaManagerProxy(
+ MockQuotaManager* quota_manager,
+ base::SingleThreadTaskRunner* task_runner)
+ : QuotaManagerProxy(quota_manager, task_runner),
+ storage_accessed_count_(0),
+ storage_modified_count_(0),
+ last_notified_type_(kStorageTypeUnknown),
+ last_notified_delta_(0),
+ registered_client_(NULL) {}
+
+void MockQuotaManagerProxy::RegisterClient(QuotaClient* client) {
+ DCHECK(!registered_client_);
+ registered_client_ = client;
+}
+
+void MockQuotaManagerProxy::SimulateQuotaManagerDestroyed() {
+ if (registered_client_) {
+ // We cannot call this in the destructor as the client (indirectly)
+ // holds a refptr of the proxy.
+ registered_client_->OnQuotaManagerDestroyed();
+ registered_client_ = NULL;
+ }
+}
+
+void MockQuotaManagerProxy::NotifyStorageAccessed(
+ QuotaClient::ID client_id, const GURL& origin, StorageType type) {
+ ++storage_accessed_count_;
+ last_notified_origin_ = origin;
+ last_notified_type_ = type;
+}
+
+void MockQuotaManagerProxy::NotifyStorageModified(
+ QuotaClient::ID client_id, const GURL& origin,
+ StorageType type, int64 delta) {
+ ++storage_modified_count_;
+ last_notified_origin_ = origin;
+ last_notified_type_ = type;
+ last_notified_delta_ = delta;
+ if (mock_manager())
+ mock_manager()->UpdateUsage(origin, type, delta);
+}
+
+MockQuotaManagerProxy::~MockQuotaManagerProxy() {
+ DCHECK(!registered_client_);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/mock_quota_manager.h b/webkit/browser/quota/mock_quota_manager.h
new file mode 100644
index 0000000..3baa3e6
--- /dev/null
+++ b/webkit/browser/quota/mock_quota_manager.h
@@ -0,0 +1,197 @@
+// Copyright 2013 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_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_H_
+#define WEBKIT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_task.h"
+#include "webkit/common/quota/quota_types.h"
+
+namespace quota {
+
+// Mocks the pieces of QuotaManager's interface.
+//
+// For usage/quota tracking test:
+// Usage and quota information can be updated by following private helper
+// methods: SetQuota() and UpdateUsage().
+//
+// For time-based deletion test:
+// Origins can be added to the mock by calling AddOrigin, and that list of
+// origins is then searched through in GetOriginsModifiedSince.
+// Neither GetOriginsModifiedSince nor DeleteOriginData touches the actual
+// origin data stored in the profile.
+class MockQuotaManager : public QuotaManager {
+ public:
+ MockQuotaManager(bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy);
+
+ // Overrides QuotaManager's implementation. The internal usage data is
+ // updated when MockQuotaManagerProxy::NotifyStorageModified() is
+ // called. The internal quota value can be updated by calling
+ // a helper method MockQuotaManagerProxy::SetQuota().
+ virtual void GetUsageAndQuota(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageAndQuotaCallback& callback) OVERRIDE;
+
+ // Overrides QuotaManager's implementation with a canned implementation that
+ // allows clients to set up the origin database that should be queried. This
+ // method will only search through the origins added explicitly via AddOrigin.
+ virtual void GetOriginsModifiedSince(
+ StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback) OVERRIDE;
+
+ // Removes an origin from the canned list of origins, but doesn't touch
+ // anything on disk. The caller must provide |quota_client_mask| which
+ // specifies the types of QuotaClients which should be removed from this
+ // origin as a bitmask built from QuotaClient::IDs. Setting the mask to
+ // QuotaClient::kAllClientsMask will remove all clients from the origin,
+ // regardless of type.
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback) OVERRIDE;
+
+ // Helper method for updating internal quota info.
+ void SetQuota(const GURL& origin, StorageType type, int64 quota);
+
+ // Helper methods for timed-deletion testing:
+ // Adds an origin to the canned list that will be searched through via
+ // GetOriginsModifiedSince. The caller must provide |quota_client_mask|
+ // which specifies the types of QuotaClients this canned origin contains
+ // as a bitmask built from QuotaClient::IDs.
+ bool AddOrigin(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified);
+
+ // Helper methods for timed-deletion testing:
+ // Checks an origin and type against the origins that have been added via
+ // AddOrigin and removed via DeleteOriginData. If the origin exists in the
+ // canned list with the proper StorageType and client, returns true.
+ bool OriginHasData(const GURL& origin,
+ StorageType type,
+ QuotaClient::ID quota_client) const;
+
+ protected:
+ virtual ~MockQuotaManager();
+
+ private:
+ friend class MockQuotaManagerProxy;
+
+ // Contains the essential bits of information about an origin that the
+ // MockQuotaManager needs to understand for time-based deletion:
+ // the origin itself, the StorageType and its modification time.
+ struct OriginInfo {
+ OriginInfo(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ base::Time modified);
+ ~OriginInfo();
+
+ GURL origin;
+ StorageType type;
+ int quota_client_mask;
+ base::Time modified;
+ };
+
+ // Contains the essential information for each origin for usage/quota testing.
+ // (Ideally this should probably merged into the above struct, but for
+ // regular usage/quota testing we hardly need modified time but only
+ // want to keep usage and quota information, so this struct exists.
+ struct StorageInfo {
+ StorageInfo();
+ ~StorageInfo();
+ int64 usage;
+ int64 quota;
+ };
+
+ typedef std::pair<GURL, StorageType> OriginAndType;
+ typedef std::map<OriginAndType, StorageInfo> UsageAndQuotaMap;
+
+ // This must be called via MockQuotaManagerProxy.
+ void UpdateUsage(const GURL& origin, StorageType type, int64 delta);
+ void DidGetModifiedSince(const GetOriginsCallback& callback,
+ std::set<GURL>* origins,
+ StorageType storage_type);
+ void DidDeleteOriginData(const StatusCallback& callback,
+ QuotaStatusCode status);
+
+ // The list of stored origins that have been added via AddOrigin.
+ std::vector<OriginInfo> origins_;
+ base::WeakPtrFactory<MockQuotaManager> weak_factory_;
+ UsageAndQuotaMap usage_and_quota_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManager);
+};
+
+// MockQuotaManagerProxy.
+class MockQuotaManagerProxy : public QuotaManagerProxy {
+ public:
+ // It is ok to give NULL to |quota_manager|.
+ MockQuotaManagerProxy(MockQuotaManager* quota_manager,
+ base::SingleThreadTaskRunner* task_runner);
+
+ virtual void RegisterClient(QuotaClient* client) OVERRIDE;
+
+ void SimulateQuotaManagerDestroyed();
+
+ // We don't mock them.
+ virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
+
+ // Validates the |client_id| and updates the internal access count
+ // which can be accessed via notify_storage_accessed_count().
+ // The also records the |origin| and |type| in last_notified_origin_ and
+ // last_notified_type_.
+ virtual void NotifyStorageAccessed(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type) OVERRIDE;
+
+ // Records the |origin|, |type| and |delta| as last_notified_origin_,
+ // last_notified_type_ and last_notified_delta_ respecitvely.
+ // If non-null MockQuotaManager is given to the constructor this also
+ // updates the manager's internal usage information.
+ virtual void NotifyStorageModified(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta) OVERRIDE;
+
+ int notify_storage_accessed_count() const { return storage_accessed_count_; }
+ int notify_storage_modified_count() const { return storage_modified_count_; }
+ GURL last_notified_origin() const { return last_notified_origin_; }
+ StorageType last_notified_type() const { return last_notified_type_; }
+ int64 last_notified_delta() const { return last_notified_delta_; }
+
+ protected:
+ virtual ~MockQuotaManagerProxy();
+
+ private:
+ MockQuotaManager* mock_manager() const {
+ return static_cast<MockQuotaManager*>(quota_manager());
+ }
+
+ int storage_accessed_count_;
+ int storage_modified_count_;
+ GURL last_notified_origin_;
+ StorageType last_notified_type_;
+ int64 last_notified_delta_;
+
+ QuotaClient* registered_client_;
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_MOCK_QUOTA_MANAGER_H_
diff --git a/webkit/browser/quota/mock_quota_manager_unittest.cc b/webkit/browser/quota/mock_quota_manager_unittest.cc
new file mode 100644
index 0000000..923a1fe
--- /dev/null
+++ b/webkit/browser/quota/mock_quota_manager_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2013 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 <set>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/mock_quota_manager.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_storage_client.h"
+
+namespace quota {
+
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:1/";
+const char kTestOrigin3[] = "http://host3:1/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+
+const StorageType kTemporary = kStorageTypeTemporary;
+const StorageType kPersistent = kStorageTypePersistent;
+
+const QuotaClient::ID kClientFile = QuotaClient::kFileSystem;
+const QuotaClient::ID kClientDB = QuotaClient::kIndexedDatabase;
+
+class MockQuotaManagerTest : public testing::Test {
+ public:
+ MockQuotaManagerTest()
+ : weak_factory_(this),
+ deletion_callback_count_(0) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ policy_ = new MockSpecialStoragePolicy;
+ manager_ = new MockQuotaManager(
+ false /* is_incognito */,
+ data_dir_.path(),
+ base::MessageLoopProxy::current(),
+ base::MessageLoopProxy::current(),
+ policy_);
+ }
+
+ virtual void TearDown() {
+ // Make sure the quota manager cleans up correctly.
+ manager_ = NULL;
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ void GetModifiedOrigins(StorageType type, base::Time since) {
+ manager_->GetOriginsModifiedSince(
+ type, since,
+ base::Bind(&MockQuotaManagerTest::GotModifiedOrigins,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GotModifiedOrigins(const std::set<GURL>& origins, StorageType type) {
+ origins_ = origins;
+ type_ = type;
+ }
+
+ void DeleteOriginData(const GURL& origin, StorageType type,
+ int quota_client_mask) {
+ manager_->DeleteOriginData(
+ origin, type, quota_client_mask,
+ base::Bind(&MockQuotaManagerTest::DeletedOriginData,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeletedOriginData(QuotaStatusCode status) {
+ ++deletion_callback_count_;
+ EXPECT_EQ(quota::kQuotaStatusOk, status);
+ }
+
+ int deletion_callback_count() const {
+ return deletion_callback_count_;
+ }
+
+ MockQuotaManager* manager() const {
+ return manager_.get();
+ }
+
+ const std::set<GURL>& origins() const {
+ return origins_;
+ }
+
+ const StorageType& type() const {
+ return type_;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ base::WeakPtrFactory<MockQuotaManagerTest> weak_factory_;
+ scoped_refptr<MockQuotaManager> manager_;
+ scoped_refptr<MockSpecialStoragePolicy> policy_;
+
+ int deletion_callback_count_;
+
+ std::set<GURL> origins_;
+ StorageType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerTest);
+};
+
+TEST_F(MockQuotaManagerTest, BasicOriginManipulation) {
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin1, kPersistent, kClientFile, base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
+}
+
+TEST_F(MockQuotaManagerTest, OriginDeletion) {
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+ manager()->AddOrigin(kOrigin3, kTemporary, kClientFile | kClientDB,
+ base::Time::Now());
+
+ DeleteOriginData(kOrigin2, kTemporary, kClientFile);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(1, deletion_callback_count());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));
+
+ DeleteOriginData(kOrigin3, kTemporary, kClientFile | kClientDB);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(2, deletion_callback_count());
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
+ EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));
+}
+
+TEST_F(MockQuotaManagerTest, ModifiedOrigins) {
+ base::Time now = base::Time::Now();
+ base::Time then = base::Time();
+ base::TimeDelta an_hour = base::TimeDelta::FromMilliseconds(3600000);
+ base::TimeDelta a_minute = base::TimeDelta::FromMilliseconds(60000);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(origins().empty());
+
+ manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, now - an_hour);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(1UL, origins().size());
+ EXPECT_EQ(1UL, origins().count(kOrigin1));
+ EXPECT_EQ(0UL, origins().count(kOrigin2));
+
+ manager()->AddOrigin(kOrigin2, kTemporary, kClientFile, now);
+
+ GetModifiedOrigins(kTemporary, then);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(2UL, origins().size());
+ EXPECT_EQ(1UL, origins().count(kOrigin1));
+ EXPECT_EQ(1UL, origins().count(kOrigin2));
+
+ GetModifiedOrigins(kTemporary, now - a_minute);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(kTemporary, type());
+ EXPECT_EQ(1UL, origins().size());
+ EXPECT_EQ(0UL, origins().count(kOrigin1));
+ EXPECT_EQ(1UL, origins().count(kOrigin2));
+}
+} // Namespace quota
diff --git a/webkit/browser/quota/mock_special_storage_policy.cc b/webkit/browser/quota/mock_special_storage_policy.cc
new file mode 100644
index 0000000..3cb0e66
--- /dev/null
+++ b/webkit/browser/quota/mock_special_storage_policy.cc
@@ -0,0 +1,41 @@
+// Copyright 2013 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/browser/quota/mock_special_storage_policy.h"
+
+namespace quota {
+
+MockSpecialStoragePolicy::MockSpecialStoragePolicy()
+ : all_unlimited_(false) {
+}
+
+bool MockSpecialStoragePolicy::IsStorageProtected(const GURL& origin) {
+ return protected_.find(origin) != protected_.end();
+}
+
+bool MockSpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) {
+ if (all_unlimited_)
+ return true;
+ return unlimited_.find(origin) != unlimited_.end();
+}
+
+bool MockSpecialStoragePolicy::IsStorageSessionOnly(const GURL& origin) {
+ return session_only_.find(origin) != session_only_.end();
+}
+
+bool MockSpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) {
+ return can_query_disk_size_.find(origin) != can_query_disk_size_.end();
+}
+
+bool MockSpecialStoragePolicy::IsFileHandler(const std::string& extension_id) {
+ return file_handlers_.find(extension_id) != file_handlers_.end();
+}
+
+bool MockSpecialStoragePolicy::HasSessionOnlyOrigins() {
+ return !session_only_.empty();
+}
+
+MockSpecialStoragePolicy::~MockSpecialStoragePolicy() {}
+
+} // namespace quota
diff --git a/webkit/browser/quota/mock_special_storage_policy.h b/webkit/browser/quota/mock_special_storage_policy.h
new file mode 100644
index 0000000..1cfaa1e
--- /dev/null
+++ b/webkit/browser/quota/mock_special_storage_policy.h
@@ -0,0 +1,90 @@
+// Copyright 2013 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_BROWSER_QUOTA_MOCK_SPECIAL_STORAGE_POLICY_H_
+#define WEBKIT_BROWSER_QUOTA_MOCK_SPECIAL_STORAGE_POLICY_H_
+
+#include <set>
+#include <string>
+
+#include "googleurl/src/gurl.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace quota {
+
+class MockSpecialStoragePolicy : public quota::SpecialStoragePolicy {
+ public:
+ MockSpecialStoragePolicy();
+
+ virtual bool IsStorageProtected(const GURL& origin) OVERRIDE;
+ virtual bool IsStorageUnlimited(const GURL& origin) OVERRIDE;
+ virtual bool IsStorageSessionOnly(const GURL& origin) OVERRIDE;
+ virtual bool CanQueryDiskSize(const GURL& origin) OVERRIDE;
+ virtual bool IsFileHandler(const std::string& extension_id) OVERRIDE;
+ virtual bool HasSessionOnlyOrigins() OVERRIDE;
+
+ void AddProtected(const GURL& origin) {
+ protected_.insert(origin);
+ }
+
+ void AddUnlimited(const GURL& origin) {
+ unlimited_.insert(origin);
+ }
+
+ void RemoveUnlimited(const GURL& origin) {
+ unlimited_.erase(origin);
+ }
+
+ void AddSessionOnly(const GURL& origin) {
+ session_only_.insert(origin);
+ }
+
+ void GrantQueryDiskSize(const GURL& origin) {
+ can_query_disk_size_.insert(origin);
+ }
+
+ void AddFileHandler(const std::string& id) {
+ file_handlers_.insert(id);
+ }
+
+ void SetAllUnlimited(bool all_unlimited) {
+ all_unlimited_ = all_unlimited;
+ }
+
+ void Reset() {
+ protected_.clear();
+ unlimited_.clear();
+ session_only_.clear();
+ can_query_disk_size_.clear();
+ file_handlers_.clear();
+ all_unlimited_ = false;
+ }
+
+ void NotifyGranted(const GURL& origin, int change_flags) {
+ SpecialStoragePolicy::NotifyGranted(origin, change_flags);
+ }
+
+ void NotifyRevoked(const GURL& origin, int change_flags) {
+ SpecialStoragePolicy::NotifyRevoked(origin, change_flags);
+ }
+
+ void NotifyCleared() {
+ SpecialStoragePolicy::NotifyCleared();
+ }
+
+ protected:
+ virtual ~MockSpecialStoragePolicy();
+
+ private:
+ std::set<GURL> protected_;
+ std::set<GURL> unlimited_;
+ std::set<GURL> session_only_;
+ std::set<GURL> can_query_disk_size_;
+ std::set<std::string> file_handlers_;
+
+ bool all_unlimited_;
+};
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_MOCK_SPECIAL_STORAGE_POLICY_H_
diff --git a/webkit/browser/quota/mock_storage_client.cc b/webkit/browser/quota/mock_storage_client.cc
new file mode 100644
index 0000000..c0d03c4
--- /dev/null
+++ b/webkit/browser/quota/mock_storage_client.cc
@@ -0,0 +1,182 @@
+// Copyright 2013 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/browser/quota/mock_storage_client.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "net/base/net_util.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+namespace quota {
+
+using std::make_pair;
+
+MockStorageClient::MockStorageClient(
+ QuotaManagerProxy* quota_manager_proxy,
+ const MockOriginData* mock_data, QuotaClient::ID id, size_t mock_data_size)
+ : quota_manager_proxy_(quota_manager_proxy),
+ id_(id),
+ mock_time_counter_(0),
+ weak_factory_(this) {
+ Populate(mock_data, mock_data_size);
+}
+
+void MockStorageClient::Populate(
+ const MockOriginData* mock_data,
+ size_t mock_data_size) {
+ for (size_t i = 0; i < mock_data_size; ++i) {
+ origin_data_[make_pair(GURL(mock_data[i].origin), mock_data[i].type)] =
+ mock_data[i].usage;
+ }
+}
+
+MockStorageClient::~MockStorageClient() {}
+
+void MockStorageClient::AddOriginAndNotify(
+ const GURL& origin_url, StorageType type, int64 size) {
+ DCHECK(origin_data_.find(make_pair(origin_url, type)) == origin_data_.end());
+ DCHECK_GE(size, 0);
+ origin_data_[make_pair(origin_url, type)] = size;
+ quota_manager_proxy_->quota_manager()->NotifyStorageModifiedInternal(
+ id(), origin_url, type, size, IncrementMockTime());
+}
+
+void MockStorageClient::ModifyOriginAndNotify(
+ const GURL& origin_url, StorageType type, int64 delta) {
+ OriginDataMap::iterator find = origin_data_.find(make_pair(origin_url, type));
+ DCHECK(find != origin_data_.end());
+ find->second += delta;
+ DCHECK_GE(find->second, 0);
+
+ // TODO(tzik): Check quota to prevent usage exceed
+ quota_manager_proxy_->quota_manager()->NotifyStorageModifiedInternal(
+ id(), origin_url, type, delta, IncrementMockTime());
+}
+
+void MockStorageClient::TouchAllOriginsAndNotify() {
+ for (OriginDataMap::const_iterator itr = origin_data_.begin();
+ itr != origin_data_.end();
+ ++itr) {
+ quota_manager_proxy_->quota_manager()->NotifyStorageModifiedInternal(
+ id(), itr->first.first, itr->first.second, 0, IncrementMockTime());
+ }
+}
+
+void MockStorageClient::AddOriginToErrorSet(
+ const GURL& origin_url, StorageType type) {
+ error_origins_.insert(make_pair(origin_url, type));
+}
+
+base::Time MockStorageClient::IncrementMockTime() {
+ ++mock_time_counter_;
+ return base::Time::FromDoubleT(mock_time_counter_ * 10.0);
+}
+
+QuotaClient::ID MockStorageClient::id() const {
+ return id_;
+}
+
+void MockStorageClient::OnQuotaManagerDestroyed() {
+ delete this;
+}
+
+void MockStorageClient::GetOriginUsage(const GURL& origin_url,
+ StorageType type,
+ const GetUsageCallback& callback) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockStorageClient::RunGetOriginUsage,
+ weak_factory_.GetWeakPtr(), origin_url, type, callback));
+}
+
+void MockStorageClient::GetOriginsForType(
+ StorageType type, const GetOriginsCallback& callback) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockStorageClient::RunGetOriginsForType,
+ weak_factory_.GetWeakPtr(), type, callback));
+}
+
+void MockStorageClient::GetOriginsForHost(
+ StorageType type, const std::string& host,
+ const GetOriginsCallback& callback) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockStorageClient::RunGetOriginsForHost,
+ weak_factory_.GetWeakPtr(), type, host, callback));
+}
+
+void MockStorageClient::DeleteOriginData(
+ const GURL& origin, StorageType type,
+ const DeletionCallback& callback) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockStorageClient::RunDeleteOriginData,
+ weak_factory_.GetWeakPtr(), origin, type, callback));
+}
+
+void MockStorageClient::RunGetOriginUsage(
+ const GURL& origin_url, StorageType type,
+ const GetUsageCallback& callback) {
+ OriginDataMap::iterator find = origin_data_.find(make_pair(origin_url, type));
+ if (find == origin_data_.end()) {
+ callback.Run(0);
+ } else {
+ callback.Run(find->second);
+ }
+}
+
+void MockStorageClient::RunGetOriginsForType(
+ StorageType type, const GetOriginsCallback& callback) {
+ std::set<GURL> origins;
+ for (OriginDataMap::iterator iter = origin_data_.begin();
+ iter != origin_data_.end(); ++iter) {
+ if (type == iter->first.second)
+ origins.insert(iter->first.first);
+ }
+ callback.Run(origins);
+}
+
+void MockStorageClient::RunGetOriginsForHost(
+ StorageType type, const std::string& host,
+ const GetOriginsCallback& callback) {
+ std::set<GURL> origins;
+ for (OriginDataMap::iterator iter = origin_data_.begin();
+ iter != origin_data_.end(); ++iter) {
+ std::string host_or_spec = net::GetHostOrSpecFromURL(iter->first.first);
+ if (type == iter->first.second && host == host_or_spec)
+ origins.insert(iter->first.first);
+ }
+ callback.Run(origins);
+}
+
+void MockStorageClient::RunDeleteOriginData(
+ const GURL& origin_url,
+ StorageType type,
+ const DeletionCallback& callback) {
+ ErrorOriginSet::iterator itr_error =
+ error_origins_.find(make_pair(origin_url, type));
+ if (itr_error != error_origins_.end()) {
+ callback.Run(kQuotaErrorInvalidModification);
+ return;
+ }
+
+ OriginDataMap::iterator itr =
+ origin_data_.find(make_pair(origin_url, type));
+ if (itr != origin_data_.end()) {
+ int64 delta = itr->second;
+ quota_manager_proxy_->
+ NotifyStorageModified(id(), origin_url, type, -delta);
+ origin_data_.erase(itr);
+ }
+
+ callback.Run(kQuotaStatusOk);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/mock_storage_client.h b/webkit/browser/quota/mock_storage_client.h
new file mode 100644
index 0000000..56ab53d
--- /dev/null
+++ b/webkit/browser/quota/mock_storage_client.h
@@ -0,0 +1,95 @@
+// Copyright 2013 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_BROWSER_QUOTA_MOCK_STORAGE_CLIENT_H_
+#define WEBKIT_BROWSER_QUOTA_MOCK_STORAGE_CLIENT_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/browser/quota/quota_client.h"
+
+namespace quota {
+
+class QuotaManagerProxy;
+
+struct MockOriginData {
+ const char* origin;
+ StorageType type;
+ int64 usage;
+};
+
+// Mock storage class for testing.
+class MockStorageClient : public QuotaClient {
+ public:
+ MockStorageClient(QuotaManagerProxy* quota_manager_proxy,
+ const MockOriginData* mock_data,
+ QuotaClient::ID id,
+ size_t mock_data_size);
+ virtual ~MockStorageClient();
+
+ // To add or modify mock data in this client.
+ void AddOriginAndNotify(
+ const GURL& origin_url, StorageType type, int64 size);
+ void ModifyOriginAndNotify(
+ const GURL& origin_url, StorageType type, int64 delta);
+ void TouchAllOriginsAndNotify();
+
+ void AddOriginToErrorSet(const GURL& origin_url, StorageType type);
+
+ base::Time IncrementMockTime();
+
+ // QuotaClient methods.
+ virtual QuotaClient::ID id() const OVERRIDE;
+ virtual void OnQuotaManagerDestroyed() OVERRIDE;
+ virtual void GetOriginUsage(const GURL& origin_url,
+ StorageType type,
+ const GetUsageCallback& callback) OVERRIDE;
+ virtual void GetOriginsForType(StorageType type,
+ const GetOriginsCallback& callback) OVERRIDE;
+ virtual void GetOriginsForHost(StorageType type, const std::string& host,
+ const GetOriginsCallback& callback) OVERRIDE;
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ const DeletionCallback& callback) OVERRIDE;
+
+ private:
+ void RunGetOriginUsage(const GURL& origin_url,
+ StorageType type,
+ const GetUsageCallback& callback);
+ void RunGetOriginsForType(StorageType type,
+ const GetOriginsCallback& callback);
+ void RunGetOriginsForHost(StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback);
+ void RunDeleteOriginData(const GURL& origin_url,
+ StorageType type,
+ const DeletionCallback& callback);
+
+ void Populate(const MockOriginData* mock_data, size_t mock_data_size);
+
+ scoped_refptr<QuotaManagerProxy> quota_manager_proxy_;
+ const ID id_;
+
+ typedef std::map<std::pair<GURL, StorageType>, int64> OriginDataMap;
+ OriginDataMap origin_data_;
+ typedef std::set<std::pair<GURL, StorageType> > ErrorOriginSet;
+ ErrorOriginSet error_origins_;
+
+ int mock_time_counter_;
+
+ base::WeakPtrFactory<MockStorageClient> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockStorageClient);
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_MOCK_STORAGE_CLIENT_H_
diff --git a/webkit/browser/quota/quota_callbacks.h b/webkit/browser/quota/quota_callbacks.h
new file mode 100644
index 0000000..cc0e735
--- /dev/null
+++ b/webkit/browser/quota/quota_callbacks.h
@@ -0,0 +1,127 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_CALLBACKS_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_CALLBACKS_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/tuple.h"
+#include "webkit/common/quota/quota_status_code.h"
+#include "webkit/common/quota/quota_types.h"
+
+class GURL;
+
+namespace quota {
+
+struct UsageInfo;
+typedef std::vector<UsageInfo> UsageInfoEntries;
+
+// Common callback types that are used throughout in the quota module.
+typedef base::Callback<void(int64 usage,
+ int64 unlimited_usage)> GlobalUsageCallback;
+typedef base::Callback<void(QuotaStatusCode status, int64 quota)> QuotaCallback;
+typedef base::Callback<void(int64 usage)> UsageCallback;
+typedef base::Callback<void(QuotaStatusCode, int64)> AvailableSpaceCallback;
+typedef base::Callback<void(QuotaStatusCode)> StatusCallback;
+typedef base::Callback<void(const std::set<GURL>& origins,
+ StorageType type)> GetOriginsCallback;
+typedef base::Callback<void(const UsageInfoEntries&)> GetUsageInfoCallback;
+
+template<typename CallbackType, typename Args>
+void DispatchToCallback(const CallbackType& callback,
+ const Args& args) {
+ DispatchToMethod(&callback, &CallbackType::Run, args);
+}
+
+// Simple template wrapper for a callback queue.
+template <typename CallbackType, typename Args>
+class CallbackQueue {
+ public:
+ // Returns true if the given |callback| is the first one added to the queue.
+ bool Add(const CallbackType& callback) {
+ callbacks_.push_back(callback);
+ return (callbacks_.size() == 1);
+ }
+
+ bool HasCallbacks() const {
+ return !callbacks_.empty();
+ }
+
+ // Runs the callbacks added to the queue and clears the queue.
+ void Run(const Args& args) {
+ typedef typename std::vector<CallbackType>::iterator iterator;
+ for (iterator iter = callbacks_.begin();
+ iter != callbacks_.end(); ++iter)
+ DispatchToCallback(*iter, args);
+ callbacks_.clear();
+ }
+
+ private:
+ std::vector<CallbackType> callbacks_;
+};
+
+typedef CallbackQueue<GlobalUsageCallback,
+ Tuple2<int64, int64> >
+ GlobalUsageCallbackQueue;
+typedef CallbackQueue<AvailableSpaceCallback,
+ Tuple2<QuotaStatusCode, int64> >
+ AvailableSpaceCallbackQueue;
+typedef CallbackQueue<QuotaCallback,
+ Tuple2<QuotaStatusCode, int64> >
+ GlobalQuotaCallbackQueue;
+typedef CallbackQueue<base::Closure, Tuple0> ClosureQueue;
+
+template <typename CallbackType, typename Key, typename Args>
+class CallbackQueueMap {
+ public:
+ typedef CallbackQueue<CallbackType, Args> CallbackQueueType;
+ typedef std::map<Key, CallbackQueueType> CallbackMap;
+ typedef typename CallbackMap::iterator iterator;
+
+ bool Add(const Key& key, const CallbackType& callback) {
+ return callback_map_[key].Add(callback);
+ }
+
+ bool HasCallbacks(const Key& key) const {
+ return (callback_map_.find(key) != callback_map_.end());
+ }
+
+ bool HasAnyCallbacks() const {
+ return !callback_map_.empty();
+ }
+
+ iterator Begin() { return callback_map_.begin(); }
+ iterator End() { return callback_map_.end(); }
+
+ void Clear() { callback_map_.clear(); }
+
+ // Runs the callbacks added for the given |key| and clears the key
+ // from the map.
+ void Run(const Key& key, const Args& args) {
+ if (!this->HasCallbacks(key))
+ return;
+ CallbackQueueType& queue = callback_map_[key];
+ queue.Run(args);
+ callback_map_.erase(key);
+ }
+
+ private:
+ CallbackMap callback_map_;
+};
+
+typedef CallbackQueueMap<UsageCallback, std::string, Tuple1<int64> >
+ HostUsageCallbackMap;
+typedef CallbackQueueMap<QuotaCallback, std::string,
+ Tuple2<QuotaStatusCode, int64> >
+ HostQuotaCallbackMap;
+
+} // namespace quota
+
+#endif // WEBKIT_QUOTA_QUOTA_TYPES_H_
diff --git a/webkit/browser/quota/quota_client.h b/webkit/browser/quota/quota_client.h
new file mode 100644
index 0000000..69b95db
--- /dev/null
+++ b/webkit/browser/quota/quota_client.h
@@ -0,0 +1,79 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_CLIENT_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_CLIENT_H_
+
+#include <list>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/common/quota/quota_types.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace quota {
+
+// An abstract interface for quota manager clients.
+// Each storage API must provide an implementation of this interface and
+// register it to the quota manager.
+// All the methods are assumed to be called on the IO thread in the browser.
+class WEBKIT_STORAGE_EXPORT QuotaClient {
+ public:
+ typedef base::Callback<void(int64 usage)> GetUsageCallback;
+ typedef base::Callback<void(const std::set<GURL>& origins)>
+ GetOriginsCallback;
+ typedef base::Callback<void(QuotaStatusCode status)> DeletionCallback;
+
+ virtual ~QuotaClient() {}
+
+ enum ID {
+ kUnknown = 1 << 0,
+ kFileSystem = 1 << 1,
+ kDatabase = 1 << 2,
+ kAppcache = 1 << 3,
+ kIndexedDatabase = 1 << 4,
+ kAllClientsMask = -1,
+ };
+
+ 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|.
+ // Note it is safe to fire the callback after the QuotaClient is destructed.
+ virtual void GetOriginUsage(const GURL& origin_url,
+ StorageType type,
+ const GetUsageCallback& callback) = 0;
+
+ // Called by the QuotaManager.
+ // Returns a list of origins that has data in the |type| storage.
+ // Note it is safe to fire the callback after the QuotaClient is destructed.
+ virtual void GetOriginsForType(StorageType type,
+ const GetOriginsCallback& callback) = 0;
+
+ // Called by the QuotaManager.
+ // Returns a list of origins that match the |host|.
+ // Note it is safe to fire the callback after the QuotaClient is destructed.
+ virtual void GetOriginsForHost(StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) = 0;
+
+ // Called by the QuotaManager.
+ // Note it is safe to fire the callback after the QuotaClient is destructed.
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ const DeletionCallback& callback) = 0;
+};
+
+// TODO(dmikurube): Replace it to std::vector for efficiency.
+typedef std::list<QuotaClient*> QuotaClientList;
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_QUOTA_CLIENT_H_
diff --git a/webkit/browser/quota/quota_database.cc b/webkit/browser/quota/quota_database.cc
new file mode 100644
index 0000000..550f6c3
--- /dev/null
+++ b/webkit/browser/quota/quota_database.cc
@@ -0,0 +1,661 @@
+// Copyright 2013 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/browser/quota/quota_database.h"
+
+#include <string>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace quota {
+namespace {
+
+// Definitions for database schema.
+
+const int kCurrentVersion = 4;
+const int kCompatibleVersion = 2;
+
+const char kHostQuotaTable[] = "HostQuotaTable";
+const char kOriginInfoTable[] = "OriginInfoTable";
+const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped";
+
+bool VerifyValidQuotaConfig(const char* key) {
+ return (key != NULL &&
+ (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) ||
+ !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey)));
+}
+
+const int kCommitIntervalMs = 30000;
+
+} // anonymous namespace
+
+// static
+const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace";
+const char QuotaDatabase::kTemporaryQuotaOverrideKey[] =
+ "TemporaryQuotaOverride";
+
+const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = {
+ { kHostQuotaTable,
+ "(host TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " quota INTEGER DEFAULT 0,"
+ " UNIQUE(host, type))" },
+ { kOriginInfoTable,
+ "(origin TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " used_count INTEGER DEFAULT 0,"
+ " last_access_time INTEGER DEFAULT 0,"
+ " last_modified_time INTEGER DEFAULT 0,"
+ " UNIQUE(origin, type))" },
+};
+
+// static
+const QuotaDatabase::IndexSchema QuotaDatabase::kIndexes[] = {
+ { "HostIndex",
+ kHostQuotaTable,
+ "(host)",
+ false },
+ { "OriginInfoIndex",
+ kOriginInfoTable,
+ "(origin)",
+ false },
+ { "OriginLastAccessTimeIndex",
+ kOriginInfoTable,
+ "(last_access_time)",
+ false },
+ { "OriginLastModifiedTimeIndex",
+ kOriginInfoTable,
+ "(last_modified_time)",
+ false },
+};
+
+struct QuotaDatabase::QuotaTableImporter {
+ bool Append(const QuotaTableEntry& entry) {
+ entries.push_back(entry);
+ return true;
+ }
+ std::vector<QuotaTableEntry> entries;
+};
+
+// Clang requires explicit out-of-line constructors for them.
+QuotaDatabase::QuotaTableEntry::QuotaTableEntry()
+ : type(kStorageTypeUnknown),
+ quota(0) {
+}
+
+QuotaDatabase::QuotaTableEntry::QuotaTableEntry(
+ const std::string& host,
+ StorageType type,
+ int64 quota)
+ : host(host),
+ type(type),
+ quota(quota) {
+}
+
+QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry()
+ : type(kStorageTypeUnknown),
+ used_count(0) {
+}
+
+QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry(
+ const GURL& origin,
+ StorageType type,
+ int used_count,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time)
+ : origin(origin),
+ type(type),
+ used_count(used_count),
+ last_access_time(last_access_time),
+ last_modified_time(last_modified_time) {
+}
+
+// QuotaDatabase ------------------------------------------------------------
+QuotaDatabase::QuotaDatabase(const base::FilePath& path)
+ : db_file_path_(path),
+ is_recreating_(false),
+ is_disabled_(false) {
+}
+
+QuotaDatabase::~QuotaDatabase() {
+ if (db_) {
+ db_->CommitTransaction();
+ }
+}
+
+void QuotaDatabase::CloseConnection() {
+ meta_table_.reset();
+ db_.reset();
+}
+
+bool QuotaDatabase::GetHostQuota(
+ const std::string& host, StorageType type, int64* quota) {
+ DCHECK(quota);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT quota"
+ " FROM HostQuotaTable"
+ " WHERE host = ? AND type = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, host);
+ statement.BindInt(1, static_cast<int>(type));
+
+ if (!statement.Step())
+ return false;
+
+ *quota = statement.ColumnInt64(0);
+ return true;
+}
+
+bool QuotaDatabase::SetHostQuota(
+ const std::string& host, StorageType type, int64 quota) {
+ DCHECK_GE(quota, 0);
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT OR REPLACE INTO HostQuotaTable"
+ " (quota, host, type)"
+ " VALUES (?, ?, ?)";
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, quota);
+ statement.BindString(1, host);
+ statement.BindInt(2, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::SetOriginLastAccessTime(
+ const GURL& origin, StorageType type, base::Time last_access_time) {
+ if (!LazyOpen(true))
+ return false;
+
+ sql::Statement statement;
+
+ int used_count = 1;
+ if (FindOriginUsedCount(origin, type, &used_count)) {
+ ++used_count;
+ const char* kSql =
+ "UPDATE OriginInfoTable"
+ " SET used_count = ?, last_access_time = ?"
+ " WHERE origin = ? AND type = ?";
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ } else {
+ const char* kSql =
+ "INSERT INTO OriginInfoTable"
+ " (used_count, last_access_time, origin, type)"
+ " VALUES (?, ?, ?, ?)";
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ }
+ statement.BindInt(0, used_count);
+ statement.BindInt64(1, last_access_time.ToInternalValue());
+ statement.BindString(2, origin.spec());
+ statement.BindInt(3, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::SetOriginLastModifiedTime(
+ const GURL& origin, StorageType type, base::Time last_modified_time) {
+ if (!LazyOpen(true))
+ return false;
+
+ sql::Statement statement;
+
+ int dummy;
+ if (FindOriginUsedCount(origin, type, &dummy)) {
+ const char* kSql =
+ "UPDATE OriginInfoTable"
+ " SET last_modified_time = ?"
+ " WHERE origin = ? AND type = ?";
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ } else {
+ const char* kSql =
+ "INSERT INTO OriginInfoTable"
+ " (last_modified_time, origin, type) VALUES (?, ?, ?)";
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ }
+ statement.BindInt64(0, last_modified_time.ToInternalValue());
+ statement.BindString(1, origin.spec());
+ statement.BindInt(2, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::RegisterInitialOriginInfo(
+ const std::set<GURL>& origins, StorageType type) {
+ if (!LazyOpen(true))
+ return false;
+
+ typedef std::set<GURL>::const_iterator itr_type;
+ for (itr_type itr = origins.begin(), end = origins.end();
+ itr != end; ++itr) {
+ const char* kSql =
+ "INSERT OR IGNORE INTO OriginInfoTable"
+ " (origin, type) VALUES (?, ?)";
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, itr->spec());
+ statement.BindInt(1, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+ }
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::DeleteHostQuota(
+ const std::string& host, StorageType type) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM HostQuotaTable"
+ " WHERE host = ? AND type = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, host);
+ statement.BindInt(1, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::DeleteOriginInfo(
+ const GURL& origin, StorageType type) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM OriginInfoTable"
+ " WHERE origin = ? AND type = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, origin.spec());
+ statement.BindInt(1, static_cast<int>(type));
+
+ if (!statement.Run())
+ return false;
+
+ ScheduleCommit();
+ return true;
+}
+
+bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) {
+ if (!LazyOpen(false))
+ return false;
+ DCHECK(VerifyValidQuotaConfig(key));
+ return meta_table_->GetValue(key, value);
+}
+
+bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) {
+ if (!LazyOpen(true))
+ return false;
+ DCHECK(VerifyValidQuotaConfig(key));
+ return meta_table_->SetValue(key, value);
+}
+
+bool QuotaDatabase::GetLRUOrigin(
+ StorageType type,
+ const std::set<GURL>& exceptions,
+ SpecialStoragePolicy* special_storage_policy,
+ GURL* origin) {
+ DCHECK(origin);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql = "SELECT origin FROM OriginInfoTable"
+ " WHERE type = ?"
+ " ORDER BY last_access_time ASC";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt(0, static_cast<int>(type));
+
+ while (statement.Step()) {
+ GURL url(statement.ColumnString(0));
+ if (exceptions.find(url) != exceptions.end())
+ continue;
+ if (special_storage_policy &&
+ special_storage_policy->IsStorageUnlimited(url))
+ continue;
+ *origin = url;
+ return true;
+ }
+
+ *origin = GURL();
+ return statement.Succeeded();
+}
+
+bool QuotaDatabase::GetOriginsModifiedSince(
+ StorageType type, std::set<GURL>* origins, base::Time modified_since) {
+ DCHECK(origins);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql = "SELECT origin FROM OriginInfoTable"
+ " WHERE type = ? AND last_modified_time >= ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt(0, static_cast<int>(type));
+ statement.BindInt64(1, modified_since.ToInternalValue());
+
+ origins->clear();
+ while (statement.Step())
+ origins->insert(GURL(statement.ColumnString(0)));
+
+ return statement.Succeeded();
+}
+
+bool QuotaDatabase::IsOriginDatabaseBootstrapped() {
+ if (!LazyOpen(true))
+ return false;
+
+ int flag = 0;
+ return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag;
+}
+
+bool QuotaDatabase::SetOriginDatabaseBootstrapped(bool bootstrap_flag) {
+ if (!LazyOpen(true))
+ return false;
+
+ return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag);
+}
+
+void QuotaDatabase::Commit() {
+ if (!db_)
+ return;
+
+ if (timer_.IsRunning())
+ timer_.Stop();
+
+ db_->CommitTransaction();
+ db_->BeginTransaction();
+}
+
+void QuotaDatabase::ScheduleCommit() {
+ if (timer_.IsRunning())
+ return;
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCommitIntervalMs),
+ this, &QuotaDatabase::Commit);
+}
+
+bool QuotaDatabase::FindOriginUsedCount(
+ const GURL& origin, StorageType type, int* used_count) {
+ DCHECK(used_count);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT used_count FROM OriginInfoTable"
+ " WHERE origin = ? AND type = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, origin.spec());
+ statement.BindInt(1, static_cast<int>(type));
+
+ if (!statement.Step())
+ return false;
+
+ *used_count = statement.ColumnInt(0);
+ return true;
+}
+
+bool QuotaDatabase::LazyOpen(bool create_if_needed) {
+ if (db_)
+ return true;
+
+ // If we tried and failed once, don't try again in the same session
+ // to avoid creating an incoherent mess on disk.
+ if (is_disabled_)
+ return false;
+
+ bool in_memory_only = db_file_path_.empty();
+ if (!create_if_needed &&
+ (in_memory_only || !file_util::PathExists(db_file_path_))) {
+ return false;
+ }
+
+ db_.reset(new sql::Connection);
+ meta_table_.reset(new sql::MetaTable);
+
+ db_->set_histogram_tag("Quota");
+
+ bool opened = false;
+ if (in_memory_only) {
+ opened = db_->OpenInMemory();
+ } else if (!file_util::CreateDirectory(db_file_path_.DirName())) {
+ LOG(ERROR) << "Failed to create quota database directory.";
+ } else {
+ opened = db_->Open(db_file_path_);
+ if (opened)
+ db_->Preload();
+ }
+
+ if (!opened || !EnsureDatabaseVersion()) {
+ LOG(ERROR) << "Failed to open the quota database.";
+ is_disabled_ = true;
+ db_.reset();
+ meta_table_.reset();
+ return false;
+ }
+
+ // Start a long-running transaction.
+ db_->BeginTransaction();
+
+ return true;
+}
+
+bool QuotaDatabase::EnsureDatabaseVersion() {
+ static const size_t kTableCount = ARRAYSIZE_UNSAFE(kTables);
+ static const size_t kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
+ if (!sql::MetaTable::DoesTableExist(db_.get()))
+ return CreateSchema(db_.get(), meta_table_.get(),
+ kCurrentVersion, kCompatibleVersion,
+ kTables, kTableCount,
+ kIndexes, kIndexCount);
+
+ if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
+ return false;
+
+ if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
+ LOG(WARNING) << "Quota database is too new.";
+ return false;
+ }
+
+ if (meta_table_->GetVersionNumber() < kCurrentVersion) {
+ if (!UpgradeSchema(meta_table_->GetVersionNumber()))
+ return ResetSchema();
+ }
+
+#ifndef NDEBUG
+ DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
+ for (size_t i = 0; i < kTableCount; ++i) {
+ DCHECK(db_->DoesTableExist(kTables[i].table_name));
+ }
+#endif
+
+ return true;
+}
+
+// static
+bool QuotaDatabase::CreateSchema(
+ sql::Connection* database,
+ sql::MetaTable* meta_table,
+ int schema_version, int compatible_version,
+ const TableSchema* tables, size_t tables_size,
+ const IndexSchema* indexes, size_t indexes_size) {
+ // TODO(kinuko): Factor out the common code to create databases.
+ sql::Transaction transaction(database);
+ if (!transaction.Begin())
+ return false;
+
+ if (!meta_table->Init(database, schema_version, compatible_version))
+ return false;
+
+ for (size_t i = 0; i < tables_size; ++i) {
+ std::string sql("CREATE TABLE ");
+ sql += tables[i].table_name;
+ sql += tables[i].columns;
+ if (!database->Execute(sql.c_str())) {
+ VLOG(1) << "Failed to execute " << sql;
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < indexes_size; ++i) {
+ std::string sql;
+ if (indexes[i].unique)
+ sql += "CREATE UNIQUE INDEX ";
+ else
+ sql += "CREATE INDEX ";
+ sql += indexes[i].index_name;
+ sql += " ON ";
+ sql += indexes[i].table_name;
+ sql += indexes[i].columns;
+ if (!database->Execute(sql.c_str())) {
+ VLOG(1) << "Failed to execute " << sql;
+ return false;
+ }
+ }
+
+ return transaction.Commit();
+}
+
+bool QuotaDatabase::ResetSchema() {
+ DCHECK(!db_file_path_.empty());
+ DCHECK(file_util::PathExists(db_file_path_));
+ VLOG(1) << "Deleting existing quota data and starting over.";
+
+ db_.reset();
+ meta_table_.reset();
+
+ if (!file_util::Delete(db_file_path_, true))
+ return false;
+
+ // Make sure the steps above actually deleted things.
+ if (file_util::PathExists(db_file_path_))
+ return false;
+
+ // So we can't go recursive.
+ if (is_recreating_)
+ return false;
+
+ base::AutoReset<bool> auto_reset(&is_recreating_, true);
+ return LazyOpen(true);
+}
+
+bool QuotaDatabase::UpgradeSchema(int current_version) {
+ if (current_version == 2) {
+ QuotaTableImporter importer;
+ typedef std::vector<QuotaTableEntry> QuotaTableEntries;
+ if (!DumpQuotaTable(new QuotaTableCallback(base::Bind(
+ &QuotaTableImporter::Append, base::Unretained(&importer)))))
+ return false;
+ ResetSchema();
+ for (QuotaTableEntries::const_iterator iter = importer.entries.begin();
+ iter != importer.entries.end(); ++iter) {
+ if (!SetHostQuota(iter->host, iter->type, iter->quota))
+ return false;
+ }
+ Commit();
+ return true;
+ }
+ return false;
+}
+
+bool QuotaDatabase::DumpQuotaTable(QuotaTableCallback* callback) {
+ scoped_ptr<QuotaTableCallback> callback_deleter(callback);
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql = "SELECT * FROM HostQuotaTable";
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+ while (statement.Step()) {
+ QuotaTableEntry entry = QuotaTableEntry(
+ statement.ColumnString(0),
+ static_cast<StorageType>(statement.ColumnInt(1)),
+ statement.ColumnInt64(2));
+
+ if (!callback->Run(entry))
+ return true;
+ }
+
+ return statement.Succeeded();
+}
+
+bool QuotaDatabase::DumpOriginInfoTable(
+ OriginInfoTableCallback* callback) {
+ scoped_ptr<OriginInfoTableCallback> callback_deleter(callback);
+
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql = "SELECT * FROM OriginInfoTable";
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+ while (statement.Step()) {
+ OriginInfoTableEntry entry(
+ GURL(statement.ColumnString(0)),
+ static_cast<StorageType>(statement.ColumnInt(1)),
+ statement.ColumnInt(2),
+ base::Time::FromInternalValue(statement.ColumnInt64(3)),
+ base::Time::FromInternalValue(statement.ColumnInt64(4)));
+
+ if (!callback->Run(entry))
+ return true;
+ }
+
+ return statement.Succeeded();
+}
+
+bool operator<(const QuotaDatabase::QuotaTableEntry& lhs,
+ const QuotaDatabase::QuotaTableEntry& rhs) {
+ if (lhs.host < rhs.host) return true;
+ if (rhs.host < lhs.host) return false;
+ if (lhs.type < rhs.type) return true;
+ if (rhs.type < lhs.type) return false;
+ return lhs.quota < rhs.quota;
+}
+
+bool operator<(const QuotaDatabase::OriginInfoTableEntry& lhs,
+ const QuotaDatabase::OriginInfoTableEntry& rhs) {
+ if (lhs.origin < rhs.origin) return true;
+ if (rhs.origin < lhs.origin) return false;
+ if (lhs.type < rhs.type) return true;
+ if (rhs.type < lhs.type) return false;
+ if (lhs.used_count < rhs.used_count) return true;
+ if (rhs.used_count < lhs.used_count) return false;
+ return lhs.last_access_time < rhs.last_access_time;
+}
+
+} // quota namespace
diff --git a/webkit/browser/quota/quota_database.h b/webkit/browser/quota/quota_database.h
new file mode 100644
index 0000000..ad9d5ad
--- /dev/null
+++ b/webkit/browser/quota/quota_database.h
@@ -0,0 +1,186 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_DATABASE_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_DATABASE_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/common/quota/quota_types.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace sql {
+class Connection;
+class MetaTable;
+}
+
+class GURL;
+
+namespace quota {
+
+class SpecialStoragePolicy;
+
+// All the methods of this class must run on the DB thread.
+class WEBKIT_STORAGE_EXPORT_PRIVATE QuotaDatabase {
+ public:
+ // Constants for {Get,Set}QuotaConfigValue keys.
+ static const char kDesiredAvailableSpaceKey[];
+ static const char kTemporaryQuotaOverrideKey[];
+
+ // If 'path' is empty, an in memory database will be used.
+ explicit QuotaDatabase(const base::FilePath& path);
+ ~QuotaDatabase();
+
+ void CloseConnection();
+
+ bool GetHostQuota(const std::string& host, StorageType type, int64* quota);
+ bool SetHostQuota(const std::string& host, StorageType type, int64 quota);
+ bool DeleteHostQuota(const std::string& host, StorageType type);
+
+ bool SetOriginLastAccessTime(const GURL& origin,
+ StorageType type,
+ base::Time last_access_time);
+
+ bool SetOriginLastModifiedTime(const GURL& origin,
+ StorageType type,
+ base::Time last_modified_time);
+
+ // Register initial |origins| info |type| to the database.
+ // This method is assumed to be called only after the installation or
+ // the database schema reset.
+ bool RegisterInitialOriginInfo(
+ const std::set<GURL>& origins, StorageType type);
+
+ bool DeleteOriginInfo(const GURL& origin, StorageType type);
+
+ bool GetQuotaConfigValue(const char* key, int64* value);
+ bool SetQuotaConfigValue(const char* key, int64 value);
+
+ // Sets |origin| to the least recently used origin of origins not included
+ // in |exceptions| and not granted the special unlimited storage right.
+ // It returns false when it failed in accessing the database.
+ // |origin| is set to empty when there is no matching origin.
+ bool GetLRUOrigin(StorageType type,
+ const std::set<GURL>& exceptions,
+ SpecialStoragePolicy* special_storage_policy,
+ GURL* origin);
+
+ // Populates |origins| with the ones that have been modified since
+ // the |modified_since|.
+ bool GetOriginsModifiedSince(StorageType type,
+ std::set<GURL>* origins,
+ base::Time modified_since);
+
+ // Returns false if SetOriginDatabaseBootstrapped has never
+ // been called before, which means existing origins may not have been
+ // registered.
+ bool IsOriginDatabaseBootstrapped();
+ bool SetOriginDatabaseBootstrapped(bool bootstrap_flag);
+
+ private:
+ struct WEBKIT_STORAGE_EXPORT_PRIVATE QuotaTableEntry {
+ QuotaTableEntry();
+ QuotaTableEntry(
+ const std::string& host,
+ StorageType type,
+ int64 quota);
+ std::string host;
+ StorageType type;
+ int64 quota;
+ };
+ friend WEBKIT_STORAGE_EXPORT_PRIVATE bool operator <(
+ const QuotaTableEntry& lhs, const QuotaTableEntry& rhs);
+
+ struct WEBKIT_STORAGE_EXPORT_PRIVATE OriginInfoTableEntry {
+ OriginInfoTableEntry();
+ OriginInfoTableEntry(
+ const GURL& origin,
+ StorageType type,
+ int used_count,
+ const base::Time& last_access_time,
+ const base::Time& last_modified_time);
+ GURL origin;
+ StorageType type;
+ int used_count;
+ base::Time last_access_time;
+ base::Time last_modified_time;
+ };
+ friend WEBKIT_STORAGE_EXPORT_PRIVATE bool operator <(
+ const OriginInfoTableEntry& lhs, const OriginInfoTableEntry& rhs);
+
+ // Structures used for CreateSchema.
+ struct TableSchema {
+ const char* table_name;
+ const char* columns;
+ };
+ struct IndexSchema {
+ const char* index_name;
+ const char* table_name;
+ const char* columns;
+ bool unique;
+ };
+
+ typedef base::Callback<bool (const QuotaTableEntry&)> QuotaTableCallback;
+ typedef base::Callback<bool (const OriginInfoTableEntry&)>
+ OriginInfoTableCallback;
+
+ struct QuotaTableImporter;
+
+ // For long-running transactions support. We always keep a transaction open
+ // so that multiple transactions can be batched. They are flushed
+ // with a delay after a modification has been made. We support neither
+ // nested transactions nor rollback (as we don't need them for now).
+ void Commit();
+ void ScheduleCommit();
+
+ bool FindOriginUsedCount(const GURL& origin,
+ StorageType type,
+ int* used_count);
+
+ bool LazyOpen(bool create_if_needed);
+ bool EnsureDatabaseVersion();
+ bool ResetSchema();
+ bool UpgradeSchema(int current_version);
+
+ static bool CreateSchema(
+ sql::Connection* database,
+ sql::MetaTable* meta_table,
+ int schema_version, int compatible_version,
+ const TableSchema* tables, size_t tables_size,
+ const IndexSchema* indexes, size_t indexes_size);
+
+ // |callback| may return false to stop reading data.
+ bool DumpQuotaTable(QuotaTableCallback* callback);
+ bool DumpOriginInfoTable(OriginInfoTableCallback* callback);
+
+ base::FilePath db_file_path_;
+
+ scoped_ptr<sql::Connection> db_;
+ scoped_ptr<sql::MetaTable> meta_table_;
+ bool is_recreating_;
+ bool is_disabled_;
+
+ base::OneShotTimer<QuotaDatabase> timer_;
+
+ friend class QuotaDatabaseTest;
+ friend class QuotaManager;
+
+ static const TableSchema kTables[];
+ static const IndexSchema kIndexes[];
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaDatabase);
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_QUOTA_DATABASE_H_
diff --git a/webkit/browser/quota/quota_database_unittest.cc b/webkit/browser/quota/quota_database_unittest.cc
new file mode 100644
index 0000000..e9f2760
--- /dev/null
+++ b/webkit/browser/quota/quota_database_unittest.cc
@@ -0,0 +1,579 @@
+// Copyright 2013 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 <algorithm>
+#include <iterator>
+#include <set>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop.h"
+#include "googleurl/src/gurl.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/quota_database.h"
+
+namespace quota {
+namespace {
+
+const base::Time kZeroTime;
+
+class TestErrorDelegate : public sql::ErrorDelegate {
+ public:
+ virtual int OnError(int error,
+ sql::Connection* connection,
+ sql::Statement* stmt) OVERRIDE {
+ return error;
+ }
+
+ protected:
+ virtual ~TestErrorDelegate() {}
+};
+
+} // namespace
+
+class QuotaDatabaseTest : public testing::Test {
+ protected:
+ typedef QuotaDatabase::QuotaTableEntry QuotaTableEntry;
+ typedef QuotaDatabase::QuotaTableCallback QuotaTableCallback;
+ typedef QuotaDatabase::OriginInfoTableCallback
+ OriginInfoTableCallback;
+
+ void LazyOpen(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ EXPECT_FALSE(db.LazyOpen(false));
+ ASSERT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.db_.get());
+ EXPECT_TRUE(kDbFile.empty() || file_util::PathExists(kDbFile));
+ }
+
+ void UpgradeSchemaV2toV3(const base::FilePath& kDbFile) {
+ const QuotaTableEntry entries[] = {
+ QuotaTableEntry("a", kStorageTypeTemporary, 1),
+ QuotaTableEntry("b", kStorageTypeTemporary, 2),
+ QuotaTableEntry("c", kStorageTypePersistent, 3),
+ };
+
+ CreateV2Database(kDbFile, entries, ARRAYSIZE_UNSAFE(entries));
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ EXPECT_TRUE(db.db_.get());
+
+ typedef EntryVerifier<QuotaTableEntry> Verifier;
+ Verifier verifier(entries, entries + ARRAYSIZE_UNSAFE(entries));
+ EXPECT_TRUE(db.DumpQuotaTable(
+ new QuotaTableCallback(
+ base::Bind(&Verifier::Run,
+ base::Unretained(&verifier)))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ void HostQuota(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ const char* kHost = "foo.com";
+ const int kQuota1 = 13579;
+ const int kQuota2 = kQuota1 + 1024;
+
+ int64 quota = -1;
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, &quota));
+
+ // Insert quota for temporary.
+ EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota1));
+ EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_EQ(kQuota1, quota);
+
+ // Update quota for temporary.
+ EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota2));
+ EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ EXPECT_EQ(kQuota2, quota);
+
+ // Quota for persistent must not be updated.
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, &quota));
+
+ // Delete temporary storage quota.
+ EXPECT_TRUE(db.DeleteHostQuota(kHost, kStorageTypeTemporary));
+ EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, &quota));
+ }
+
+ void GlobalQuota(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ const char* kTempQuotaKey = QuotaDatabase::kTemporaryQuotaOverrideKey;
+ const char* kAvailSpaceKey = QuotaDatabase::kDesiredAvailableSpaceKey;
+
+ int64 value = 0;
+ const int64 kValue1 = 456;
+ const int64 kValue2 = 123000;
+ EXPECT_FALSE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_FALSE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue1));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_EQ(kValue1, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue2));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
+ EXPECT_EQ(kValue2, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue1));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+ EXPECT_EQ(kValue1, value);
+
+ EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue2));
+ EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
+ EXPECT_EQ(kValue2, value);
+ }
+
+ void OriginLastAccessTimeLRU(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ std::set<GURL> exceptions;
+ GURL origin;
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+
+ const GURL kOrigin1("http://a/");
+ const GURL kOrigin2("http://b/");
+ const GURL kOrigin3("http://c/");
+ const GURL kOrigin4("http://p/");
+
+ // Adding three temporary storages, and
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(30)));
+
+ // one persistent.
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin4, kStorageTypePersistent, base::Time::FromInternalValue(40)));
+
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin1.spec(), origin.spec());
+
+ // Test that unlimited origins are exluded from eviction, but
+ // protected origins are not excluded.
+ scoped_refptr<MockSpecialStoragePolicy> policy(
+ new MockSpecialStoragePolicy);
+ policy->AddUnlimited(kOrigin1);
+ policy->AddProtected(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ policy, &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin1);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin3.spec(), origin.spec());
+
+ exceptions.insert(kOrigin3);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::Now()));
+
+ // Delete origin/type last access time information.
+ EXPECT_TRUE(db.DeleteOriginInfo(kOrigin3, kStorageTypeTemporary));
+
+ // Querying again to see if the deletion has worked.
+ exceptions.clear();
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_EQ(kOrigin2.spec(), origin.spec());
+
+ exceptions.insert(kOrigin1);
+ exceptions.insert(kOrigin2);
+ EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
+ NULL, &origin));
+ EXPECT_TRUE(origin.is_empty());
+ }
+
+ void OriginLastModifiedSince(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+ ASSERT_TRUE(db.LazyOpen(true));
+
+ std::set<GURL> origins;
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time()));
+ EXPECT_TRUE(origins.empty());
+
+ const GURL kOrigin1("http://a/");
+ const GURL kOrigin2("http://b/");
+ const GURL kOrigin3("http://c/");
+
+ // Report last mod time for the test origins.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(0)));
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time()));
+ EXPECT_EQ(3U, origins.size());
+ EXPECT_EQ(1U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(15)));
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(0U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(25)));
+ EXPECT_TRUE(origins.empty());
+
+ // Update origin1's mod time but for persistent storage.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin1, kStorageTypePersistent, base::Time::FromInternalValue(30)));
+
+ // Must have no effects on temporary origins info.
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(1U, origins.count(kOrigin3));
+
+ // One more update for persistent origin2.
+ EXPECT_TRUE(db.SetOriginLastModifiedTime(
+ kOrigin2, kStorageTypePersistent, base::Time::FromInternalValue(40)));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypePersistent, &origins, base::Time::FromInternalValue(25)));
+ EXPECT_EQ(2U, origins.size());
+ EXPECT_EQ(1U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(0U, origins.count(kOrigin3));
+
+ EXPECT_TRUE(db.GetOriginsModifiedSince(
+ kStorageTypePersistent, &origins, base::Time::FromInternalValue(35)));
+ EXPECT_EQ(1U, origins.size());
+ EXPECT_EQ(0U, origins.count(kOrigin1));
+ EXPECT_EQ(1U, origins.count(kOrigin2));
+ EXPECT_EQ(0U, origins.count(kOrigin3));
+ }
+
+ void RegisterInitialOriginInfo(const base::FilePath& kDbFile) {
+ QuotaDatabase db(kDbFile);
+
+ const GURL kOrigins[] = {
+ GURL("http://a/"),
+ GURL("http://b/"),
+ GURL("http://c/") };
+ std::set<GURL> origins(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
+
+ EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
+
+ int used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(0, used_count);
+
+ EXPECT_TRUE(db.SetOriginLastAccessTime(
+ GURL("http://a/"), kStorageTypeTemporary,
+ base::Time::FromDoubleT(1.0)));
+ used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(1, used_count);
+
+ EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
+
+ used_count = -1;
+ EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
+ kStorageTypeTemporary,
+ &used_count));
+ EXPECT_EQ(1, used_count);
+ }
+
+ template <typename EntryType>
+ struct EntryVerifier {
+ std::set<EntryType> table;
+
+ template <typename Iterator>
+ EntryVerifier(Iterator itr, Iterator end)
+ : table(itr, end) {}
+
+ bool Run(const EntryType& entry) {
+ EXPECT_EQ(1u, table.erase(entry));
+ return true;
+ }
+ };
+
+ void DumpQuotaTable(const base::FilePath& kDbFile) {
+ QuotaTableEntry kTableEntries[] = {
+ QuotaTableEntry("http://go/", kStorageTypeTemporary, 1),
+ QuotaTableEntry("http://oo/", kStorageTypeTemporary, 2),
+ QuotaTableEntry("http://gle/", kStorageTypePersistent, 3)
+ };
+ QuotaTableEntry* begin = kTableEntries;
+ QuotaTableEntry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ AssignQuotaTable(db.db_.get(), begin, end);
+ db.Commit();
+
+ typedef EntryVerifier<QuotaTableEntry> Verifier;
+ Verifier verifier(begin, end);
+ EXPECT_TRUE(db.DumpQuotaTable(
+ new QuotaTableCallback(
+ base::Bind(&Verifier::Run,
+ base::Unretained(&verifier)))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ void DumpOriginInfoTable(const base::FilePath& kDbFile) {
+ base::Time now(base::Time::Now());
+ typedef QuotaDatabase::OriginInfoTableEntry Entry;
+ Entry kTableEntries[] = {
+ Entry(GURL("http://go/"), kStorageTypeTemporary, 2147483647, now, now),
+ Entry(GURL("http://oo/"), kStorageTypeTemporary, 0, now, now),
+ Entry(GURL("http://gle/"), kStorageTypeTemporary, 1, now, now),
+ };
+ Entry* begin = kTableEntries;
+ Entry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
+
+ QuotaDatabase db(kDbFile);
+ EXPECT_TRUE(db.LazyOpen(true));
+ AssignOriginInfoTable(db.db_.get(), begin, end);
+ db.Commit();
+
+ typedef EntryVerifier<Entry> Verifier;
+ Verifier verifier(begin, end);
+ EXPECT_TRUE(db.DumpOriginInfoTable(
+ new OriginInfoTableCallback(
+ base::Bind(&Verifier::Run,
+ base::Unretained(&verifier)))));
+ EXPECT_TRUE(verifier.table.empty());
+ }
+
+ private:
+ template <typename Iterator>
+ void AssignQuotaTable(sql::Connection* db, Iterator itr, Iterator end) {
+ ASSERT_NE(db, (sql::Connection*)NULL);
+ for (; itr != end; ++itr) {
+ const char* kSql =
+ "INSERT INTO HostQuotaTable"
+ " (host, type, quota)"
+ " VALUES (?, ?, ?)";
+ sql::Statement statement;
+ statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ ASSERT_TRUE(statement.is_valid());
+
+ statement.BindString(0, itr->host);
+ statement.BindInt(1, static_cast<int>(itr->type));
+ statement.BindInt64(2, itr->quota);
+ EXPECT_TRUE(statement.Run());
+ }
+ }
+
+ template <typename Iterator>
+ void AssignOriginInfoTable(sql::Connection* db, Iterator itr, Iterator end) {
+ ASSERT_NE(db, (sql::Connection*)NULL);
+ for (; itr != end; ++itr) {
+ const char* kSql =
+ "INSERT INTO OriginInfoTable"
+ " (origin, type, used_count, last_access_time, last_modified_time)"
+ " VALUES (?, ?, ?, ?, ?)";
+ sql::Statement statement;
+ statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ ASSERT_TRUE(statement.is_valid());
+
+ statement.BindString(0, itr->origin.spec());
+ statement.BindInt(1, static_cast<int>(itr->type));
+ statement.BindInt(2, itr->used_count);
+ statement.BindInt64(3, itr->last_access_time.ToInternalValue());
+ statement.BindInt64(4, itr->last_modified_time.ToInternalValue());
+ EXPECT_TRUE(statement.Run());
+ }
+ }
+
+ bool OpenDatabase(sql::Connection* db, const base::FilePath& kDbFile) {
+ if (kDbFile.empty()) {
+ return db->OpenInMemory();
+ }
+ if (!file_util::CreateDirectory(kDbFile.DirName()))
+ return false;
+ if (!db->Open(kDbFile))
+ return false;
+ db->Preload();
+ return true;
+ }
+
+ // Create V2 database and populate some data.
+ void CreateV2Database(
+ const base::FilePath& kDbFile,
+ const QuotaTableEntry* entries,
+ size_t entries_size) {
+ scoped_ptr<sql::Connection> db(new sql::Connection);
+ scoped_ptr<sql::MetaTable> meta_table(new sql::MetaTable);
+
+ // V2 schema definitions.
+ static const int kCurrentVersion = 2;
+ static const int kCompatibleVersion = 2;
+ static const char kHostQuotaTable[] = "HostQuotaTable";
+ static const char kOriginLastAccessTable[] = "OriginLastAccessTable";
+ static const QuotaDatabase::TableSchema kTables[] = {
+ { kHostQuotaTable,
+ "(host TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " quota INTEGER,"
+ " UNIQUE(host, type))" },
+ { kOriginLastAccessTable,
+ "(origin TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " used_count INTEGER,"
+ " last_access_time INTEGER,"
+ " UNIQUE(origin, type))" },
+ };
+ static const QuotaDatabase::IndexSchema kIndexes[] = {
+ { "HostIndex",
+ kHostQuotaTable,
+ "(host)",
+ false },
+ { "OriginLastAccessIndex",
+ kOriginLastAccessTable,
+ "(origin, last_access_time)",
+ false },
+ };
+
+ ASSERT_TRUE(OpenDatabase(db.get(), kDbFile));
+ EXPECT_TRUE(QuotaDatabase::CreateSchema(
+ db.get(), meta_table.get(),
+ kCurrentVersion, kCompatibleVersion,
+ kTables, ARRAYSIZE_UNSAFE(kTables),
+ kIndexes, ARRAYSIZE_UNSAFE(kIndexes)));
+
+ // V2 and V3 QuotaTable are compatible, so we can simply use
+ // AssignQuotaTable to poplulate v2 database here.
+ db->BeginTransaction();
+ AssignQuotaTable(db.get(), entries, entries + entries_size);
+ db->CommitTransaction();
+ }
+
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(QuotaDatabaseTest, LazyOpen) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ LazyOpen(kDbFile);
+ LazyOpen(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, UpgradeSchema) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ UpgradeSchemaV2toV3(kDbFile);
+}
+
+TEST_F(QuotaDatabaseTest, HostQuota) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ HostQuota(kDbFile);
+ HostQuota(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, GlobalQuota) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ GlobalQuota(kDbFile);
+ GlobalQuota(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, OriginLastAccessTimeLRU) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ OriginLastAccessTimeLRU(kDbFile);
+ OriginLastAccessTimeLRU(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, OriginLastModifiedSince) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ OriginLastModifiedSince(kDbFile);
+ OriginLastModifiedSince(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, BootstrapFlag) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ QuotaDatabase db(kDbFile);
+
+ EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
+ EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(true));
+ EXPECT_TRUE(db.IsOriginDatabaseBootstrapped());
+ EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(false));
+ EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
+}
+
+TEST_F(QuotaDatabaseTest, RegisterInitialOriginInfo) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ RegisterInitialOriginInfo(kDbFile);
+ RegisterInitialOriginInfo(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, DumpQuotaTable) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ DumpQuotaTable(kDbFile);
+ DumpQuotaTable(base::FilePath());
+}
+
+TEST_F(QuotaDatabaseTest, DumpOriginInfoTable) {
+ base::ScopedTempDir data_dir;
+ ASSERT_TRUE(data_dir.CreateUniqueTempDir());
+ const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
+ DumpOriginInfoTable(kDbFile);
+ DumpOriginInfoTable(base::FilePath());
+}
+} // namespace quota
diff --git a/webkit/browser/quota/quota_manager.cc b/webkit/browser/quota/quota_manager.cc
new file mode 100644
index 0000000..3d3e7d7
--- /dev/null
+++ b/webkit/browser/quota/quota_manager.cc
@@ -0,0 +1,1668 @@
+// Copyright 2013 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/browser/quota/quota_manager.h"
+
+#include <algorithm>
+#include <deque>
+#include <functional>
+#include <set>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/metrics/histogram.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
+#include "base/task_runner_util.h"
+#include "base/time.h"
+#include "net/base/net_util.h"
+#include "webkit/browser/quota/quota_database.h"
+#include "webkit/browser/quota/quota_temporary_storage_evictor.h"
+#include "webkit/browser/quota/usage_tracker.h"
+#include "webkit/common/quota/quota_types.h"
+
+#define UMA_HISTOGRAM_MBYTES(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ (name), static_cast<int>((sample) / kMBytes), \
+ 1, 10 * 1024 * 1024 /* 10TB */, 100)
+
+namespace quota {
+
+namespace {
+
+const int64 kMBytes = 1024 * 1024;
+const int kMinutesInMilliSeconds = 60 * 1000;
+
+const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour
+const double kTemporaryQuotaRatioToAvail = 0.5; // 50%
+
+} // namespace
+
+// Arbitrary for now, but must be reasonably small so that
+// in-memory databases can fit.
+// TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this.
+const int64 QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes;
+
+const int64 QuotaManager::kNoLimit = kint64max;
+
+const int QuotaManager::kPerHostTemporaryPortion = 5; // 20%
+
+const char QuotaManager::kDatabaseName[] = "QuotaManager";
+
+// Preserve kMinimumPreserveForSystem disk space for system book-keeping
+// when returning the quota to unlimited apps/extensions.
+// TODO(kinuko): This should be like 10% of the actual disk space.
+// For now we simply use a constant as getting the disk size needs
+// platform-dependent code. (http://crbug.com/178976)
+const int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes;
+
+const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3;
+
+const int QuotaManager::kEvictionIntervalInMilliSeconds =
+ 30 * kMinutesInMilliSeconds;
+
+// Heuristics: assuming average cloud server allows a few Gigs storage
+// on the server side and the storage needs to be shared for user data
+// and by multiple apps.
+int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;
+
+namespace {
+
+void CountOriginType(const std::set<GURL>& origins,
+ SpecialStoragePolicy* policy,
+ size_t* protected_origins,
+ size_t* unlimited_origins) {
+ DCHECK(protected_origins);
+ DCHECK(unlimited_origins);
+ *protected_origins = 0;
+ *unlimited_origins = 0;
+ if (!policy)
+ return;
+ for (std::set<GURL>::const_iterator itr = origins.begin();
+ itr != origins.end();
+ ++itr) {
+ if (policy->IsStorageProtected(*itr))
+ ++*protected_origins;
+ if (policy->IsStorageUnlimited(*itr))
+ ++*unlimited_origins;
+ }
+}
+
+bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64* new_quota,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ if (!database->SetQuotaConfigValue(
+ QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) {
+ *new_quota = -1;
+ return false;
+ }
+ return true;
+}
+
+bool GetPersistentHostQuotaOnDBThread(const std::string& host,
+ int64* quota,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ database->GetHostQuota(host, kStorageTypePersistent, quota);
+ return true;
+}
+
+bool SetPersistentHostQuotaOnDBThread(const std::string& host,
+ int64* new_quota,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ if (database->SetHostQuota(host, kStorageTypePersistent, *new_quota))
+ return true;
+ *new_quota = 0;
+ return false;
+}
+
+bool InitializeOnDBThread(int64* temporary_quota_override,
+ int64* desired_available_space,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey,
+ temporary_quota_override);
+ database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey,
+ desired_available_space);
+ return true;
+}
+
+bool GetLRUOriginOnDBThread(StorageType type,
+ std::set<GURL>* exceptions,
+ SpecialStoragePolicy* policy,
+ GURL* url,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ database->GetLRUOrigin(type, *exceptions, policy, url);
+ return true;
+}
+
+bool DeleteOriginInfoOnDBThread(const GURL& origin,
+ StorageType type,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ return database->DeleteOriginInfo(origin, type);
+}
+
+bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ if (database->IsOriginDatabaseBootstrapped())
+ return true;
+
+ // Register existing origins with 0 last time access.
+ if (database->RegisterInitialOriginInfo(*origins, kStorageTypeTemporary)) {
+ database->SetOriginDatabaseBootstrapped(true);
+ return true;
+ }
+ return false;
+}
+
+bool UpdateAccessTimeOnDBThread(const GURL& origin,
+ StorageType type,
+ base::Time accessed_time,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ return database->SetOriginLastAccessTime(origin, type, accessed_time);
+}
+
+bool UpdateModifiedTimeOnDBThread(const GURL& origin,
+ StorageType type,
+ base::Time modified_time,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ return database->SetOriginLastModifiedTime(origin, type, modified_time);
+}
+
+int64 CallSystemGetAmountOfFreeDiskSpace(const base::FilePath& profile_path) {
+ // Ensure the profile path exists.
+ if(!file_util::CreateDirectory(profile_path)) {
+ LOG(WARNING) << "Create directory failed for path" << profile_path.value();
+ return 0;
+ }
+ return base::SysInfo::AmountOfFreeDiskSpace(profile_path);
+}
+
+int64 CalculateTemporaryGlobalQuota(int64 global_limited_usage,
+ int64 available_space) {
+ DCHECK_GE(global_limited_usage, 0);
+ int64 avail_space = available_space;
+ if (avail_space < kint64max - global_limited_usage) {
+ // We basically calculate the temporary quota by
+ // [available_space + space_used_for_temp] * kTempQuotaRatio,
+ // but make sure we'll have no overflow.
+ avail_space += global_limited_usage;
+ }
+ return avail_space * kTemporaryQuotaRatioToAvail;
+}
+
+void DispatchTemporaryGlobalQuotaCallback(
+ const QuotaCallback& callback,
+ QuotaStatusCode status,
+ const UsageAndQuota& usage_and_quota) {
+ if (status != kQuotaStatusOk) {
+ callback.Run(status, 0);
+ return;
+ }
+
+ callback.Run(status, CalculateTemporaryGlobalQuota(
+ usage_and_quota.global_limited_usage,
+ usage_and_quota.available_disk_space));
+}
+
+int64 CalculateQuotaWithDiskSpace(
+ int64 available_disk_space, int64 usage, int64 quota) {
+ if (available_disk_space < QuotaManager::kMinimumPreserveForSystem ||
+ quota < usage) {
+ // No more space; cap the quota to the current usage.
+ return usage;
+ }
+
+ available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
+ if (available_disk_space < quota - usage)
+ return available_disk_space + usage;
+
+ return quota;
+}
+
+int64 CalculateTemporaryHostQuota(int64 host_usage,
+ int64 global_quota,
+ int64 global_limited_usage) {
+ DCHECK_GE(global_limited_usage, 0);
+ int64 host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion;
+ if (global_limited_usage > global_quota)
+ host_quota = std::min(host_quota, host_usage);
+ return host_quota;
+}
+
+void DispatchUsageAndQuotaForWebApps(
+ StorageType type,
+ bool is_incognito,
+ bool is_unlimited,
+ bool can_query_disk_size,
+ const QuotaManager::GetUsageAndQuotaCallback& callback,
+ QuotaStatusCode status,
+ const UsageAndQuota& usage_and_quota) {
+ if (status != kQuotaStatusOk) {
+ callback.Run(status, 0, 0);
+ return;
+ }
+
+ int64 usage = usage_and_quota.usage;
+ int64 quota = usage_and_quota.quota;
+
+ if (type == kStorageTypeTemporary && !is_unlimited) {
+ quota = CalculateTemporaryHostQuota(
+ usage, quota, usage_and_quota.global_limited_usage);
+ }
+
+ if (is_incognito) {
+ quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit);
+ callback.Run(status, usage, quota);
+ return;
+ }
+
+ // For apps with unlimited permission or can_query_disk_size is true (and not
+ // in incognito mode).
+ // We assume we can expose the actual disk size for them and cap the quota by
+ // the available disk space.
+ if (is_unlimited || can_query_disk_size) {
+ callback.Run(
+ status, usage,
+ CalculateQuotaWithDiskSpace(
+ usage_and_quota.available_disk_space,
+ usage, quota));
+ return;
+ }
+
+ callback.Run(status, usage, quota);
+}
+
+} // namespace
+
+UsageAndQuota::UsageAndQuota()
+ : usage(0),
+ global_limited_usage(0),
+ quota(0),
+ available_disk_space(0) {
+}
+
+UsageAndQuota::UsageAndQuota(
+ int64 usage,
+ int64 global_limited_usage,
+ int64 quota,
+ int64 available_disk_space)
+ : usage(usage),
+ global_limited_usage(global_limited_usage),
+ quota(quota),
+ available_disk_space(available_disk_space) {
+}
+
+class UsageAndQuotaCallbackDispatcher
+ : public QuotaTask,
+ public base::SupportsWeakPtr<UsageAndQuotaCallbackDispatcher> {
+ public:
+ UsageAndQuotaCallbackDispatcher(QuotaManager* manager)
+ : QuotaTask(manager),
+ has_usage_(false),
+ has_global_limited_usage_(false),
+ has_quota_(false),
+ has_available_disk_space_(false),
+ status_(kQuotaStatusUnknown),
+ usage_and_quota_(-1, -1, -1, -1),
+ waiting_callbacks_(1) {}
+
+ virtual ~UsageAndQuotaCallbackDispatcher() {}
+
+ void WaitForResults(const QuotaManager::UsageAndQuotaCallback& callback) {
+ callback_ = callback;
+ Start();
+ }
+
+ void set_usage(int64 usage) {
+ usage_and_quota_.usage = usage;
+ has_usage_ = true;
+ }
+
+ void set_global_limited_usage(int64 global_limited_usage) {
+ usage_and_quota_.global_limited_usage = global_limited_usage;
+ has_global_limited_usage_ = true;
+ }
+
+ void set_quota(int64 quota) {
+ usage_and_quota_.quota = quota;
+ has_quota_ = true;
+ }
+
+ void set_available_disk_space(int64 available_disk_space) {
+ usage_and_quota_.available_disk_space = available_disk_space;
+ has_available_disk_space_ = true;
+ }
+
+ UsageCallback GetHostUsageCallback() {
+ ++waiting_callbacks_;
+ has_usage_ = true;
+ return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetHostUsage,
+ AsWeakPtr());
+ }
+
+ UsageCallback GetGlobalLimitedUsageCallback() {
+ ++waiting_callbacks_;
+ has_global_limited_usage_ = true;
+ return base::Bind(
+ &UsageAndQuotaCallbackDispatcher::DidGetGlobalLimitedUsage,
+ AsWeakPtr());
+ }
+
+ QuotaCallback GetQuotaCallback() {
+ ++waiting_callbacks_;
+ has_quota_ = true;
+ return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetQuota,
+ AsWeakPtr());
+ }
+
+ QuotaCallback GetAvailableSpaceCallback() {
+ ++waiting_callbacks_;
+ has_available_disk_space_ = true;
+ return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace,
+ AsWeakPtr());
+ }
+
+ private:
+ void DidGetHostUsage(int64 usage) {
+ if (status_ == kQuotaStatusUnknown)
+ status_ = kQuotaStatusOk;
+ usage_and_quota_.usage = usage;
+ CheckCompleted();
+ }
+
+ void DidGetGlobalLimitedUsage(int64 limited_usage) {
+ if (status_ == kQuotaStatusUnknown)
+ status_ = kQuotaStatusOk;
+ usage_and_quota_.global_limited_usage = limited_usage;
+ CheckCompleted();
+ }
+
+ void DidGetQuota(QuotaStatusCode status, int64 quota) {
+ if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
+ status_ = status;
+ usage_and_quota_.quota = quota;
+ CheckCompleted();
+ }
+
+ void DidGetAvailableSpace(QuotaStatusCode status, int64 space) {
+ DCHECK_GE(space, 0);
+ if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
+ status_ = status;
+ usage_and_quota_.available_disk_space = space;
+ CheckCompleted();
+ }
+
+ virtual void Run() OVERRIDE {
+ // We initialize waiting_callbacks to 1 so that we won't run
+ // the completion callback until here even some of the callbacks
+ // are dispatched synchronously.
+ CheckCompleted();
+ }
+
+ virtual void Aborted() OVERRIDE {
+ callback_.Run(kQuotaErrorAbort, UsageAndQuota());
+ DeleteSoon();
+ }
+
+ virtual void Completed() OVERRIDE {
+ DCHECK(!has_usage_ || usage_and_quota_.usage >= 0);
+ DCHECK(!has_global_limited_usage_ ||
+ usage_and_quota_.global_limited_usage >= 0);
+ DCHECK(!has_quota_ || usage_and_quota_.quota >= 0);
+ DCHECK(!has_available_disk_space_ ||
+ usage_and_quota_.available_disk_space >= 0);
+
+ callback_.Run(status_, usage_and_quota_);
+ DeleteSoon();
+ }
+
+ void CheckCompleted() {
+ if (--waiting_callbacks_ <= 0)
+ CallCompleted();
+ }
+
+ // For sanity checks, they're checked only when DCHECK is on.
+ bool has_usage_;
+ bool has_global_limited_usage_;
+ bool has_quota_;
+ bool has_available_disk_space_;
+
+ QuotaStatusCode status_;
+ UsageAndQuota usage_and_quota_;
+ QuotaManager::UsageAndQuotaCallback callback_;
+ int waiting_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaCallbackDispatcher);
+};
+
+class QuotaManager::GetUsageInfoTask : public QuotaTask {
+ private:
+ typedef QuotaManager::GetUsageInfoTask self_type;
+
+ public:
+ GetUsageInfoTask(
+ QuotaManager* manager,
+ const GetUsageInfoCallback& callback)
+ : QuotaTask(manager),
+ callback_(callback),
+ weak_factory_(this) {
+ }
+
+ protected:
+ virtual void Run() OVERRIDE {
+ remaining_trackers_ = 3;
+ // This will populate cached hosts and usage info.
+ manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage(
+ base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ kStorageTypeTemporary));
+ manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage(
+ base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ kStorageTypePersistent));
+ manager()->GetUsageTracker(kStorageTypeSyncable)->GetGlobalUsage(
+ base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ kStorageTypeSyncable));
+ }
+
+ virtual void Completed() OVERRIDE {
+ callback_.Run(entries_);
+ DeleteSoon();
+ }
+
+ virtual void Aborted() OVERRIDE {
+ callback_.Run(UsageInfoEntries());
+ DeleteSoon();
+ }
+
+ private:
+ void AddEntries(StorageType type, UsageTracker* tracker) {
+ std::map<std::string, int64> host_usage;
+ tracker->GetCachedHostsUsage(&host_usage);
+ for (std::map<std::string, int64>::const_iterator iter = host_usage.begin();
+ iter != host_usage.end();
+ ++iter) {
+ entries_.push_back(UsageInfo(iter->first, type, iter->second));
+ }
+ if (--remaining_trackers_ == 0)
+ CallCompleted();
+ }
+
+ void DidGetGlobalUsage(StorageType type, int64, int64) {
+ AddEntries(type, manager()->GetUsageTracker(type));
+ }
+
+ QuotaManager* manager() const {
+ return static_cast<QuotaManager*>(observer());
+ }
+
+ GetUsageInfoCallback callback_;
+ UsageInfoEntries entries_;
+ base::WeakPtrFactory<GetUsageInfoTask> weak_factory_;
+ int remaining_trackers_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask);
+};
+
+class QuotaManager::OriginDataDeleter : public QuotaTask {
+ public:
+ OriginDataDeleter(QuotaManager* manager,
+ const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback)
+ : QuotaTask(manager),
+ origin_(origin),
+ type_(type),
+ quota_client_mask_(quota_client_mask),
+ error_count_(0),
+ remaining_clients_(-1),
+ skipped_clients_(0),
+ callback_(callback),
+ weak_factory_(this) {}
+
+ protected:
+ virtual void Run() OVERRIDE {
+ error_count_ = 0;
+ remaining_clients_ = manager()->clients_.size();
+ for (QuotaClientList::iterator iter = manager()->clients_.begin();
+ iter != manager()->clients_.end(); ++iter) {
+ if (quota_client_mask_ & (*iter)->id()) {
+ (*iter)->DeleteOriginData(
+ origin_, type_,
+ base::Bind(&OriginDataDeleter::DidDeleteOriginData,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ ++skipped_clients_;
+ if (--remaining_clients_ == 0)
+ CallCompleted();
+ }
+ }
+ }
+
+ virtual void Completed() OVERRIDE {
+ if (error_count_ == 0) {
+ // Only remove the entire origin if we didn't skip any client types.
+ if (skipped_clients_ == 0)
+ manager()->DeleteOriginFromDatabase(origin_, type_);
+ callback_.Run(kQuotaStatusOk);
+ } else {
+ callback_.Run(kQuotaErrorInvalidModification);
+ }
+ DeleteSoon();
+ }
+
+ virtual void Aborted() OVERRIDE {
+ callback_.Run(kQuotaErrorAbort);
+ DeleteSoon();
+ }
+
+ void DidDeleteOriginData(QuotaStatusCode status) {
+ DCHECK_GT(remaining_clients_, 0);
+
+ if (status != kQuotaStatusOk)
+ ++error_count_;
+
+ if (--remaining_clients_ == 0)
+ CallCompleted();
+ }
+
+ QuotaManager* manager() const {
+ return static_cast<QuotaManager*>(observer());
+ }
+
+ GURL origin_;
+ StorageType type_;
+ int quota_client_mask_;
+ int error_count_;
+ int remaining_clients_;
+ int skipped_clients_;
+ StatusCallback callback_;
+
+ base::WeakPtrFactory<OriginDataDeleter> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter);
+};
+
+class QuotaManager::HostDataDeleter : public QuotaTask {
+ public:
+ HostDataDeleter(QuotaManager* manager,
+ const std::string& host,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback)
+ : QuotaTask(manager),
+ host_(host),
+ type_(type),
+ quota_client_mask_(quota_client_mask),
+ error_count_(0),
+ remaining_clients_(-1),
+ remaining_deleters_(-1),
+ callback_(callback),
+ weak_factory_(this) {}
+
+ protected:
+ virtual void Run() OVERRIDE {
+ error_count_ = 0;
+ remaining_clients_ = manager()->clients_.size();
+ for (QuotaClientList::iterator iter = manager()->clients_.begin();
+ iter != manager()->clients_.end(); ++iter) {
+ (*iter)->GetOriginsForHost(
+ type_, host_,
+ base::Bind(&HostDataDeleter::DidGetOriginsForHost,
+ weak_factory_.GetWeakPtr()));
+ }
+ }
+
+ virtual void Completed() OVERRIDE {
+ if (error_count_ == 0) {
+ callback_.Run(kQuotaStatusOk);
+ } else {
+ callback_.Run(kQuotaErrorInvalidModification);
+ }
+ DeleteSoon();
+ }
+
+ virtual void Aborted() OVERRIDE {
+ callback_.Run(kQuotaErrorAbort);
+ DeleteSoon();
+ }
+
+ void DidGetOriginsForHost(const std::set<GURL>& origins) {
+ DCHECK_GT(remaining_clients_, 0);
+
+ origins_.insert(origins.begin(), origins.end());
+
+ if (--remaining_clients_ == 0) {
+ if (!origins_.empty())
+ ScheduleOriginsDeletion();
+ else
+ CallCompleted();
+ }
+ }
+
+ void ScheduleOriginsDeletion() {
+ remaining_deleters_ = origins_.size();
+ for (std::set<GURL>::const_iterator p = origins_.begin();
+ p != origins_.end();
+ ++p) {
+ OriginDataDeleter* deleter =
+ new OriginDataDeleter(
+ manager(), *p, type_, quota_client_mask_,
+ base::Bind(&HostDataDeleter::DidDeleteOriginData,
+ weak_factory_.GetWeakPtr()));
+ deleter->Start();
+ }
+ }
+
+ void DidDeleteOriginData(QuotaStatusCode status) {
+ DCHECK_GT(remaining_deleters_, 0);
+
+ if (status != kQuotaStatusOk)
+ ++error_count_;
+
+ if (--remaining_deleters_ == 0)
+ CallCompleted();
+ }
+
+ QuotaManager* manager() const {
+ return static_cast<QuotaManager*>(observer());
+ }
+
+ std::string host_;
+ StorageType type_;
+ int quota_client_mask_;
+ std::set<GURL> origins_;
+ int error_count_;
+ int remaining_clients_;
+ int remaining_deleters_;
+ StatusCallback callback_;
+
+ base::WeakPtrFactory<HostDataDeleter> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(HostDataDeleter);
+};
+
+class QuotaManager::GetModifiedSinceHelper {
+ public:
+ bool GetModifiedSinceOnDBThread(StorageType type,
+ base::Time modified_since,
+ QuotaDatabase* database) {
+ DCHECK(database);
+ return database->GetOriginsModifiedSince(type, &origins_, modified_since);
+ }
+
+ void DidGetModifiedSince(QuotaManager* manager,
+ const GetOriginsCallback& callback,
+ StorageType type,
+ bool success) {
+ if (!manager) {
+ // The operation was aborted.
+ callback.Run(std::set<GURL>(), type);
+ return;
+ }
+ manager->DidDatabaseWork(success);
+ callback.Run(origins_, type);
+ }
+
+ private:
+ std::set<GURL> origins_;
+};
+
+class QuotaManager::DumpQuotaTableHelper {
+ public:
+ bool DumpQuotaTableOnDBThread(QuotaDatabase* database) {
+ DCHECK(database);
+ return database->DumpQuotaTable(
+ new TableCallback(base::Bind(&DumpQuotaTableHelper::AppendEntry,
+ base::Unretained(this))));
+ }
+
+ void DidDumpQuotaTable(QuotaManager* manager,
+ const DumpQuotaTableCallback& callback,
+ bool success) {
+ if (!manager) {
+ // The operation was aborted.
+ callback.Run(QuotaTableEntries());
+ return;
+ }
+ manager->DidDatabaseWork(success);
+ callback.Run(entries_);
+ }
+
+ private:
+ typedef QuotaDatabase::QuotaTableCallback TableCallback;
+
+ bool AppendEntry(const QuotaTableEntry& entry) {
+ entries_.push_back(entry);
+ return true;
+ }
+
+ QuotaTableEntries entries_;
+};
+
+class QuotaManager::DumpOriginInfoTableHelper {
+ public:
+ bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) {
+ DCHECK(database);
+ return database->DumpOriginInfoTable(
+ new TableCallback(base::Bind(&DumpOriginInfoTableHelper::AppendEntry,
+ base::Unretained(this))));
+ }
+
+ void DidDumpOriginInfoTable(QuotaManager* manager,
+ const DumpOriginInfoTableCallback& callback,
+ bool success) {
+ if (!manager) {
+ // The operation was aborted.
+ callback.Run(OriginInfoTableEntries());
+ return;
+ }
+ manager->DidDatabaseWork(success);
+ callback.Run(entries_);
+ }
+
+ private:
+ typedef QuotaDatabase::OriginInfoTableCallback TableCallback;
+
+ bool AppendEntry(const OriginInfoTableEntry& entry) {
+ entries_.push_back(entry);
+ return true;
+ }
+
+ OriginInfoTableEntries entries_;
+};
+
+// QuotaManager ---------------------------------------------------------------
+
+QuotaManager::QuotaManager(bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy)
+ : is_incognito_(is_incognito),
+ profile_path_(profile_path),
+ proxy_(new QuotaManagerProxy(
+ this, io_thread)),
+ db_disabled_(false),
+ eviction_disabled_(false),
+ io_thread_(io_thread),
+ db_thread_(db_thread),
+ temporary_quota_initialized_(false),
+ temporary_quota_override_(-1),
+ desired_available_space_(-1),
+ special_storage_policy_(special_storage_policy),
+ weak_factory_(this),
+ get_disk_space_fn_(&CallSystemGetAmountOfFreeDiskSpace) {
+}
+
+void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) {
+ LazyInitialize();
+ GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback);
+ get_usage_info->Start();
+}
+
+void QuotaManager::GetUsageAndQuotaForWebApps(
+ const GURL& origin,
+ StorageType type,
+ const GetUsageAndQuotaCallback& callback) {
+ if (type != kStorageTypeTemporary &&
+ type != kStorageTypePersistent &&
+ type != kStorageTypeSyncable) {
+ callback.Run(kQuotaErrorNotSupported, 0, 0);
+ return;
+ }
+
+ DCHECK(origin == origin.GetOrigin());
+ LazyInitialize();
+
+ bool unlimited = IsStorageUnlimited(origin, type);
+ bool can_query_disk_size = CanQueryDiskSize(origin);
+
+ UsageAndQuotaCallbackDispatcher* dispatcher =
+ new UsageAndQuotaCallbackDispatcher(this);
+
+ UsageAndQuota usage_and_quota;
+ if (unlimited) {
+ dispatcher->set_quota(kNoLimit);
+ } else {
+ if (type == kStorageTypeTemporary) {
+ GetUsageTracker(type)->GetGlobalLimitedUsage(
+ dispatcher->GetGlobalLimitedUsageCallback());
+ GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
+ } else if (type == kStorageTypePersistent) {
+ GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin),
+ dispatcher->GetQuotaCallback());
+ } else {
+ dispatcher->set_quota(kSyncableStorageDefaultHostQuota);
+ }
+ }
+
+ GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin),
+ dispatcher->GetHostUsageCallback());
+
+ if (!is_incognito_ && (unlimited || can_query_disk_size))
+ GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
+
+ dispatcher->WaitForResults(base::Bind(
+ &DispatchUsageAndQuotaForWebApps,
+ type, is_incognito_, unlimited, can_query_disk_size,
+ callback));
+}
+
+void QuotaManager::GetUsageAndQuota(
+ const GURL& origin, StorageType type,
+ const GetUsageAndQuotaCallback& callback) {
+ DCHECK(origin == origin.GetOrigin());
+
+ if (IsStorageUnlimited(origin, type)) {
+ callback.Run(kQuotaStatusOk, 0, kNoLimit);
+ return;
+ }
+
+ GetUsageAndQuotaForWebApps(origin, type, callback);
+}
+
+void QuotaManager::NotifyStorageAccessed(
+ QuotaClient::ID client_id,
+ const GURL& origin, StorageType type) {
+ DCHECK(origin == origin.GetOrigin());
+ NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now());
+}
+
+void QuotaManager::NotifyStorageModified(
+ QuotaClient::ID client_id,
+ const GURL& origin, StorageType type, int64 delta) {
+ DCHECK(origin == origin.GetOrigin());
+ NotifyStorageModifiedInternal(client_id, origin, type, delta,
+ base::Time::Now());
+}
+
+void QuotaManager::NotifyOriginInUse(const GURL& origin) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ origins_in_use_[origin]++;
+}
+
+void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ DCHECK(IsOriginInUse(origin));
+ int& count = origins_in_use_[origin];
+ if (--count == 0)
+ origins_in_use_.erase(origin);
+}
+
+void QuotaManager::SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ bool enabled) {
+ LazyInitialize();
+ GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled);
+}
+
+void QuotaManager::DeleteOriginData(
+ const GURL& origin, StorageType type, int quota_client_mask,
+ const StatusCallback& callback) {
+ LazyInitialize();
+
+ if (origin.is_empty() || clients_.empty()) {
+ callback.Run(kQuotaStatusOk);
+ return;
+ }
+
+ DCHECK(origin == origin.GetOrigin());
+ OriginDataDeleter* deleter =
+ new OriginDataDeleter(this, origin, type, quota_client_mask, callback);
+ deleter->Start();
+}
+
+void QuotaManager::DeleteHostData(const std::string& host,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback) {
+ LazyInitialize();
+
+ if (host.empty() || clients_.empty()) {
+ callback.Run(kQuotaStatusOk);
+ return;
+ }
+
+ HostDataDeleter* deleter =
+ new HostDataDeleter(this, host, type, quota_client_mask, callback);
+ deleter->Start();
+}
+
+void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) {
+ if (!available_space_callbacks_.Add(callback))
+ return;
+
+ PostTaskAndReplyWithResult(
+ db_thread_,
+ FROM_HERE,
+ base::Bind(get_disk_space_fn_, profile_path_),
+ base::Bind(&QuotaManager::DidGetAvailableSpace,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) {
+ LazyInitialize();
+ if (!temporary_quota_initialized_) {
+ db_initialization_callbacks_.Add(base::Bind(
+ &QuotaManager::GetTemporaryGlobalQuota,
+ weak_factory_.GetWeakPtr(), callback));
+ return;
+ }
+
+ if (temporary_quota_override_ > 0) {
+ callback.Run(kQuotaStatusOk, temporary_quota_override_);
+ return;
+ }
+
+ UsageAndQuotaCallbackDispatcher* dispatcher =
+ new UsageAndQuotaCallbackDispatcher(this);
+ GetUsageTracker(kStorageTypeTemporary)->
+ GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
+ GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
+ dispatcher->WaitForResults(
+ base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback));
+}
+
+void QuotaManager::SetTemporaryGlobalOverrideQuota(
+ int64 new_quota, const QuotaCallback& callback) {
+ LazyInitialize();
+
+ if (new_quota < 0) {
+ if (!callback.is_null())
+ callback.Run(kQuotaErrorInvalidModification, -1);
+ return;
+ }
+
+ if (db_disabled_) {
+ if (callback.is_null())
+ callback.Run(kQuotaErrorInvalidAccess, -1);
+ return;
+ }
+
+ int64* new_quota_ptr = new int64(new_quota);
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread,
+ base::Unretained(new_quota_ptr)),
+ base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota,
+ weak_factory_.GetWeakPtr(),
+ callback,
+ base::Owned(new_quota_ptr)));
+}
+
+void QuotaManager::GetPersistentHostQuota(const std::string& host,
+ const QuotaCallback& callback) {
+ LazyInitialize();
+ if (host.empty()) {
+ // This could happen if we are called on file:///.
+ // TODO(kinuko) We may want to respect --allow-file-access-from-files
+ // command line switch.
+ callback.Run(kQuotaStatusOk, 0);
+ return;
+ }
+
+ if (!persistent_host_quota_callbacks_.Add(host, callback))
+ return;
+
+ int64* quota_ptr = new int64(0);
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&GetPersistentHostQuotaOnDBThread,
+ host,
+ base::Unretained(quota_ptr)),
+ base::Bind(&QuotaManager::DidGetPersistentHostQuota,
+ weak_factory_.GetWeakPtr(),
+ host,
+ base::Owned(quota_ptr)));
+}
+
+void QuotaManager::SetPersistentHostQuota(const std::string& host,
+ int64 new_quota,
+ const QuotaCallback& callback) {
+ LazyInitialize();
+ if (host.empty()) {
+ // This could happen if we are called on file:///.
+ callback.Run(kQuotaErrorNotSupported, 0);
+ return;
+ }
+ if (new_quota < 0) {
+ callback.Run(kQuotaErrorInvalidModification, -1);
+ return;
+ }
+
+ if (db_disabled_) {
+ callback.Run(kQuotaErrorInvalidAccess, -1);
+ return;
+ }
+
+ int64* new_quota_ptr = new int64(new_quota);
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&SetPersistentHostQuotaOnDBThread,
+ host,
+ base::Unretained(new_quota_ptr)),
+ base::Bind(&QuotaManager::DidSetPersistentHostQuota,
+ weak_factory_.GetWeakPtr(),
+ host,
+ callback,
+ base::Owned(new_quota_ptr)));
+}
+
+void QuotaManager::GetGlobalUsage(StorageType type,
+ const GlobalUsageCallback& callback) {
+ LazyInitialize();
+ GetUsageTracker(type)->GetGlobalUsage(callback);
+}
+
+void QuotaManager::GetHostUsage(const std::string& host,
+ StorageType type,
+ const UsageCallback& callback) {
+ LazyInitialize();
+ GetUsageTracker(type)->GetHostUsage(host, callback);
+}
+
+void QuotaManager::GetStatistics(
+ std::map<std::string, std::string>* statistics) {
+ DCHECK(statistics);
+ if (temporary_storage_evictor_) {
+ std::map<std::string, int64> stats;
+ temporary_storage_evictor_->GetStatistics(&stats);
+ for (std::map<std::string, int64>::iterator p = stats.begin();
+ p != stats.end();
+ ++p)
+ (*statistics)[p->first] = base::Int64ToString(p->second);
+ }
+}
+
+bool QuotaManager::IsStorageUnlimited(const GURL& origin,
+ StorageType type) const {
+ // For syncable storage we should always enforce quota (since the
+ // quota must be capped by the server limit).
+ if (type == kStorageTypeSyncable)
+ return false;
+ return special_storage_policy_.get() &&
+ special_storage_policy_->IsStorageUnlimited(origin);
+}
+
+void QuotaManager::GetOriginsModifiedSince(StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback) {
+ LazyInitialize();
+ GetModifiedSinceHelper* helper = new GetModifiedSinceHelper;
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&GetModifiedSinceHelper::GetModifiedSinceOnDBThread,
+ base::Unretained(helper),
+ type,
+ modified_since),
+ base::Bind(&GetModifiedSinceHelper::DidGetModifiedSince,
+ base::Owned(helper),
+ weak_factory_.GetWeakPtr(),
+ callback,
+ type));
+}
+
+bool QuotaManager::ResetUsageTracker(StorageType type) {
+ DCHECK(GetUsageTracker(type));
+ if (GetUsageTracker(type)->IsWorking())
+ return false;
+ switch (type) {
+ case kStorageTypeTemporary:
+ temporary_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypeTemporary,
+ special_storage_policy_));
+ return true;
+ case kStorageTypePersistent:
+ persistent_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypePersistent,
+ special_storage_policy_));
+ return true;
+ case kStorageTypeSyncable:
+ syncable_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypeSyncable,
+ special_storage_policy_));
+ return true;
+ default:
+ NOTREACHED();
+ }
+ return true;
+}
+
+QuotaManager::~QuotaManager() {
+ proxy_->manager_ = NULL;
+ std::for_each(clients_.begin(), clients_.end(),
+ std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed));
+ if (database_)
+ db_thread_->DeleteSoon(FROM_HERE, database_.release());
+}
+
+QuotaManager::EvictionContext::EvictionContext()
+ : evicted_type(kStorageTypeUnknown) {
+}
+
+QuotaManager::EvictionContext::~EvictionContext() {
+}
+
+void QuotaManager::LazyInitialize() {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ if (database_) {
+ // Initialization seems to be done already.
+ return;
+ }
+
+ // Use an empty path to open an in-memory only databse for incognito.
+ database_.reset(new QuotaDatabase(is_incognito_ ? base::FilePath() :
+ profile_path_.AppendASCII(kDatabaseName)));
+
+ temporary_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypeTemporary,
+ special_storage_policy_));
+ persistent_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypePersistent,
+ special_storage_policy_));
+ syncable_usage_tracker_.reset(
+ new UsageTracker(clients_, kStorageTypeSyncable,
+ special_storage_policy_));
+
+ int64* temporary_quota_override = new int64(-1);
+ int64* desired_available_space = new int64(-1);
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&InitializeOnDBThread,
+ base::Unretained(temporary_quota_override),
+ base::Unretained(desired_available_space)),
+ base::Bind(&QuotaManager::DidInitialize,
+ weak_factory_.GetWeakPtr(),
+ base::Owned(temporary_quota_override),
+ base::Owned(desired_available_space)));
+}
+
+void QuotaManager::RegisterClient(QuotaClient* client) {
+ DCHECK(!database_.get());
+ clients_.push_back(client);
+}
+
+UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const {
+ switch (type) {
+ case kStorageTypeTemporary:
+ return temporary_usage_tracker_.get();
+ case kStorageTypePersistent:
+ return persistent_usage_tracker_.get();
+ case kStorageTypeSyncable:
+ return syncable_usage_tracker_.get();
+ default:
+ NOTREACHED();
+ }
+ return NULL;
+}
+
+void QuotaManager::GetCachedOrigins(
+ StorageType type, std::set<GURL>* origins) {
+ DCHECK(origins);
+ LazyInitialize();
+ DCHECK(GetUsageTracker(type));
+ GetUsageTracker(type)->GetCachedOrigins(origins);
+}
+
+void QuotaManager::NotifyStorageAccessedInternal(
+ QuotaClient::ID client_id,
+ const GURL& origin, StorageType type,
+ base::Time accessed_time) {
+ LazyInitialize();
+ if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) {
+ // Record the accessed origins while GetLRUOrigin task is runing
+ // to filter out them from eviction.
+ access_notified_origins_.insert(origin);
+ }
+
+ if (db_disabled_)
+ return;
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&UpdateAccessTimeOnDBThread, origin, type, accessed_time),
+ base::Bind(&QuotaManager::DidDatabaseWork,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::NotifyStorageModifiedInternal(
+ QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta,
+ base::Time modified_time) {
+ LazyInitialize();
+ GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta);
+
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&UpdateModifiedTimeOnDBThread, origin, type, modified_time),
+ base::Bind(&QuotaManager::DidDatabaseWork,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) {
+ DumpQuotaTableHelper* helper = new DumpQuotaTableHelper;
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread,
+ base::Unretained(helper)),
+ base::Bind(&DumpQuotaTableHelper::DidDumpQuotaTable,
+ base::Owned(helper),
+ weak_factory_.GetWeakPtr(),
+ callback));
+}
+
+void QuotaManager::DumpOriginInfoTable(
+ const DumpOriginInfoTableCallback& callback) {
+ DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper;
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread,
+ base::Unretained(helper)),
+ base::Bind(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable,
+ base::Owned(helper),
+ weak_factory_.GetWeakPtr(),
+ callback));
+}
+
+void QuotaManager::StartEviction() {
+ DCHECK(!temporary_storage_evictor_.get());
+ temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
+ this, kEvictionIntervalInMilliSeconds));
+ if (desired_available_space_ >= 0)
+ temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
+ desired_available_space_);
+ temporary_storage_evictor_->Start();
+}
+
+void QuotaManager::DeleteOriginFromDatabase(
+ const GURL& origin, StorageType type) {
+ LazyInitialize();
+ if (db_disabled_)
+ return;
+
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&DeleteOriginInfoOnDBThread, origin, type),
+ base::Bind(&QuotaManager::DidDatabaseWork,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+
+ // We only try evict origins that are not in use, so basically
+ // deletion attempt for eviction should not fail. Let's record
+ // the origin if we get error and exclude it from future eviction
+ // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted).
+ if (status != kQuotaStatusOk)
+ origins_in_error_[eviction_context_.evicted_origin]++;
+
+ eviction_context_.evict_origin_data_callback.Run(status);
+ eviction_context_.evict_origin_data_callback.Reset();
+}
+
+void QuotaManager::ReportHistogram() {
+ GetGlobalUsage(kStorageTypeTemporary,
+ base::Bind(
+ &QuotaManager::DidGetTemporaryGlobalUsageForHistogram,
+ weak_factory_.GetWeakPtr()));
+ GetGlobalUsage(kStorageTypePersistent,
+ base::Bind(
+ &QuotaManager::DidGetPersistentGlobalUsageForHistogram,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::DidGetTemporaryGlobalUsageForHistogram(
+ int64 usage,
+ int64 unlimited_usage) {
+ UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage);
+
+ std::set<GURL> origins;
+ GetCachedOrigins(kStorageTypeTemporary, &origins);
+
+ size_t num_origins = origins.size();
+ size_t protected_origins = 0;
+ size_t unlimited_origins = 0;
+ CountOriginType(origins, special_storage_policy_,
+ &protected_origins, &unlimited_origins);
+
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins",
+ num_origins);
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins",
+ protected_origins);
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins",
+ unlimited_origins);
+}
+
+void QuotaManager::DidGetPersistentGlobalUsageForHistogram(
+ int64 usage,
+ int64 unlimited_usage) {
+ UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage);
+
+ std::set<GURL> origins;
+ GetCachedOrigins(kStorageTypePersistent, &origins);
+
+ size_t num_origins = origins.size();
+ size_t protected_origins = 0;
+ size_t unlimited_origins = 0;
+ CountOriginType(origins, special_storage_policy_,
+ &protected_origins, &unlimited_origins);
+
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins",
+ num_origins);
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins",
+ protected_origins);
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins",
+ unlimited_origins);
+}
+
+void QuotaManager::GetLRUOrigin(
+ StorageType type,
+ const GetLRUOriginCallback& callback) {
+ LazyInitialize();
+ // This must not be called while there's an in-flight task.
+ DCHECK(lru_origin_callback_.is_null());
+ lru_origin_callback_ = callback;
+ if (db_disabled_) {
+ lru_origin_callback_.Run(GURL());
+ lru_origin_callback_.Reset();
+ return;
+ }
+
+ std::set<GURL>* exceptions = new std::set<GURL>;
+ for (std::map<GURL, int>::const_iterator p = origins_in_use_.begin();
+ p != origins_in_use_.end();
+ ++p) {
+ if (p->second > 0)
+ exceptions->insert(p->first);
+ }
+ for (std::map<GURL, int>::const_iterator p = origins_in_error_.begin();
+ p != origins_in_error_.end();
+ ++p) {
+ if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted)
+ exceptions->insert(p->first);
+ }
+
+ GURL* url = new GURL;
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&GetLRUOriginOnDBThread,
+ type,
+ base::Owned(exceptions),
+ special_storage_policy_,
+ base::Unretained(url)),
+ base::Bind(&QuotaManager::DidGetLRUOrigin,
+ weak_factory_.GetWeakPtr(),
+ base::Owned(url)));
+}
+
+void QuotaManager::EvictOriginData(
+ const GURL& origin,
+ StorageType type,
+ const EvictOriginDataCallback& callback) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ DCHECK_EQ(type, kStorageTypeTemporary);
+
+ eviction_context_.evicted_origin = origin;
+ eviction_context_.evicted_type = type;
+ eviction_context_.evict_origin_data_callback = callback;
+
+ DeleteOriginData(origin, type, QuotaClient::kAllClientsMask,
+ base::Bind(&QuotaManager::DidOriginDataEvicted,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::GetUsageAndQuotaForEviction(
+ const UsageAndQuotaCallback& callback) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ LazyInitialize();
+
+ UsageAndQuotaCallbackDispatcher* dispatcher =
+ new UsageAndQuotaCallbackDispatcher(this);
+ GetUsageTracker(kStorageTypeTemporary)->
+ GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
+ GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
+ GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
+ dispatcher->WaitForResults(callback);
+}
+
+void QuotaManager::DidSetTemporaryGlobalOverrideQuota(
+ const QuotaCallback& callback,
+ const int64* new_quota,
+ bool success) {
+ QuotaStatusCode status = kQuotaErrorInvalidAccess;
+ DidDatabaseWork(success);
+ if (success) {
+ temporary_quota_override_ = *new_quota;
+ status = kQuotaStatusOk;
+ }
+
+ if (callback.is_null())
+ return;
+
+ callback.Run(status, *new_quota);
+}
+
+void QuotaManager::DidGetPersistentHostQuota(const std::string& host,
+ const int64* quota,
+ bool success) {
+ DidDatabaseWork(success);
+ persistent_host_quota_callbacks_.Run(
+ host, MakeTuple(kQuotaStatusOk, *quota));
+}
+
+void QuotaManager::DidSetPersistentHostQuota(const std::string& host,
+ const QuotaCallback& callback,
+ const int64* new_quota,
+ bool success) {
+ DidDatabaseWork(success);
+ callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota);
+}
+
+void QuotaManager::DidInitialize(int64* temporary_quota_override,
+ int64* desired_available_space,
+ bool success) {
+ temporary_quota_override_ = *temporary_quota_override;
+ desired_available_space_ = *desired_available_space;
+ temporary_quota_initialized_ = true;
+ DidDatabaseWork(success);
+
+ histogram_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(
+ kReportHistogramInterval),
+ this, &QuotaManager::ReportHistogram);
+
+ db_initialization_callbacks_.Run(MakeTuple());
+ GetTemporaryGlobalQuota(
+ base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::DidGetLRUOrigin(const GURL* origin,
+ bool success) {
+ DidDatabaseWork(success);
+ // Make sure the returned origin is (still) not in the origin_in_use_ set
+ // and has not been accessed since we posted the task.
+ if (origins_in_use_.find(*origin) != origins_in_use_.end() ||
+ access_notified_origins_.find(*origin) != access_notified_origins_.end())
+ lru_origin_callback_.Run(GURL());
+ else
+ lru_origin_callback_.Run(*origin);
+ access_notified_origins_.clear();
+ lru_origin_callback_.Reset();
+}
+
+void QuotaManager::DidGetInitialTemporaryGlobalQuota(
+ QuotaStatusCode status, int64 quota_unused) {
+ if (eviction_disabled_)
+ return;
+
+ std::set<GURL>* origins = new std::set<GURL>;
+ temporary_usage_tracker_->GetCachedOrigins(origins);
+ // This will call the StartEviction() when initial origin registration
+ // is completed.
+ PostTaskAndReplyWithResultForDBThread(
+ FROM_HERE,
+ base::Bind(&InitializeTemporaryOriginsInfoOnDBThread,
+ base::Owned(origins)),
+ base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) {
+ DidDatabaseWork(success);
+ if (success)
+ StartEviction();
+}
+
+void QuotaManager::DidGetAvailableSpace(int64 space) {
+ available_space_callbacks_.Run(MakeTuple(kQuotaStatusOk, space));
+}
+
+void QuotaManager::DidDatabaseWork(bool success) {
+ db_disabled_ = !success;
+}
+
+void QuotaManager::DeleteOnCorrectThread() const {
+ if (!io_thread_->BelongsToCurrentThread() &&
+ io_thread_->DeleteSoon(FROM_HERE, this)) {
+ return;
+ }
+ delete this;
+}
+
+void QuotaManager::PostTaskAndReplyWithResultForDBThread(
+ const tracked_objects::Location& from_here,
+ const base::Callback<bool(QuotaDatabase*)>& task,
+ const base::Callback<void(bool)>& reply) {
+ // Deleting manager will post another task to DB thread to delete
+ // |database_|, therefore we can be sure that database_ is alive when this
+ // task runs.
+ base::PostTaskAndReplyWithResult(
+ db_thread_,
+ from_here,
+ base::Bind(task, base::Unretained(database_.get())),
+ reply);
+}
+
+// QuotaManagerProxy ----------------------------------------------------------
+
+void QuotaManagerProxy::RegisterClient(QuotaClient* client) {
+ if (!io_thread_->BelongsToCurrentThread() &&
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::RegisterClient, this, client))) {
+ return;
+ }
+
+ if (manager_)
+ manager_->RegisterClient(client);
+ else
+ client->OnQuotaManagerDestroyed();
+}
+
+void QuotaManagerProxy::NotifyStorageAccessed(
+ QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::NotifyStorageAccessed, this, client_id,
+ origin, type));
+ return;
+ }
+
+ if (manager_)
+ manager_->NotifyStorageAccessed(client_id, origin, type);
+}
+
+void QuotaManagerProxy::NotifyStorageModified(
+ QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::NotifyStorageModified, this, client_id,
+ origin, type, delta));
+ return;
+ }
+
+ if (manager_)
+ manager_->NotifyStorageModified(client_id, origin, type, delta);
+}
+
+void QuotaManagerProxy::NotifyOriginInUse(
+ const GURL& origin) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::NotifyOriginInUse, this, origin));
+ return;
+ }
+
+ if (manager_)
+ manager_->NotifyOriginInUse(origin);
+}
+
+void QuotaManagerProxy::NotifyOriginNoLongerInUse(
+ const GURL& origin) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::NotifyOriginNoLongerInUse, this,
+ origin));
+ return;
+ }
+ if (manager_)
+ manager_->NotifyOriginNoLongerInUse(origin);
+}
+
+void QuotaManagerProxy::SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ bool enabled) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&QuotaManagerProxy::SetUsageCacheEnabled, this,
+ client_id, origin, type, enabled));
+ return;
+ }
+ if (manager_)
+ manager_->SetUsageCacheEnabled(client_id, origin, type, enabled);
+}
+
+QuotaManager* QuotaManagerProxy::quota_manager() const {
+ DCHECK(!io_thread_ || io_thread_->BelongsToCurrentThread());
+ return manager_;
+}
+
+QuotaManagerProxy::QuotaManagerProxy(
+ QuotaManager* manager, base::SingleThreadTaskRunner* io_thread)
+ : manager_(manager), io_thread_(io_thread) {
+}
+
+QuotaManagerProxy::~QuotaManagerProxy() {
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/quota_manager.h b/webkit/browser/quota/quota_manager.h
new file mode 100644
index 0000000..9496b97
--- /dev/null
+++ b/webkit/browser/quota/quota_manager.h
@@ -0,0 +1,471 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_MANAGER_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_MANAGER_H_
+
+#include <deque>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "webkit/browser/quota/quota_callbacks.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_database.h"
+#include "webkit/browser/quota/quota_task.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+class SingleThreadTaskRunner;
+}
+
+namespace quota_internals {
+class QuotaInternalsProxy;
+}
+
+namespace quota {
+
+class MockQuotaManager;
+class QuotaDatabase;
+class QuotaManagerProxy;
+class QuotaTemporaryStorageEvictor;
+class UsageTracker;
+
+struct QuotaManagerDeleter;
+
+struct WEBKIT_STORAGE_EXPORT UsageAndQuota {
+ int64 usage;
+ int64 global_limited_usage;
+ int64 quota;
+ int64 available_disk_space;
+
+ UsageAndQuota();
+ UsageAndQuota(int64 usage,
+ int64 global_limited_usage,
+ int64 quota,
+ int64 available_disk_space);
+};
+
+// An interface called by QuotaTemporaryStorageEvictor.
+class WEBKIT_STORAGE_EXPORT QuotaEvictionHandler {
+ public:
+ typedef base::Callback<void(const GURL&)> GetLRUOriginCallback;
+ typedef StatusCallback EvictOriginDataCallback;
+ typedef base::Callback<void(QuotaStatusCode status,
+ const UsageAndQuota& usage_and_quota)>
+ UsageAndQuotaCallback;
+
+ // Returns the least recently used origin. It might return empty
+ // GURL when there are no evictable origins.
+ virtual void GetLRUOrigin(
+ StorageType type,
+ const GetLRUOriginCallback& callback) = 0;
+
+ virtual void EvictOriginData(
+ const GURL& origin,
+ StorageType type,
+ const EvictOriginDataCallback& callback) = 0;
+
+ virtual void GetUsageAndQuotaForEviction(
+ const UsageAndQuotaCallback& callback) = 0;
+
+ protected:
+ virtual ~QuotaEvictionHandler() {}
+};
+
+struct UsageInfo {
+ UsageInfo(const std::string& host, StorageType type, int64 usage)
+ : host(host),
+ type(type),
+ usage(usage) {}
+ std::string host;
+ StorageType type;
+ int64 usage;
+};
+
+// The quota manager class. This class is instantiated per profile and
+// 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 WEBKIT_STORAGE_EXPORT QuotaManager
+ : public QuotaTaskObserver,
+ public QuotaEvictionHandler,
+ public base::RefCountedThreadSafe<QuotaManager, QuotaManagerDeleter> {
+ public:
+ typedef base::Callback<void(QuotaStatusCode,
+ int64 /* usage */,
+ int64 /* quota */)>
+ GetUsageAndQuotaCallback;
+
+ static const int64 kIncognitoDefaultQuotaLimit;
+ static const int64 kNoLimit;
+
+ QuotaManager(bool is_incognito,
+ const base::FilePath& profile_path,
+ base::SingleThreadTaskRunner* io_thread,
+ base::SequencedTaskRunner* db_thread,
+ SpecialStoragePolicy* special_storage_policy);
+
+ // Returns a proxy object that can be used on any thread.
+ QuotaManagerProxy* proxy() { return proxy_.get(); }
+
+ // Called by clients or webapps. Returns usage per host.
+ void GetUsageInfo(const GetUsageInfoCallback& callback);
+
+ // Called by Web Apps.
+ // This method is declared as virtual to allow test code to override it.
+ virtual void GetUsageAndQuotaForWebApps(
+ const GURL& origin,
+ StorageType type,
+ const GetUsageAndQuotaCallback& callback);
+
+ // Called by StorageClients.
+ // This method is declared as virtual to allow test code to override it.
+ //
+ // For UnlimitedStorage origins, this version skips usage and quota handling
+ // to avoid extra query cost.
+ // Do not call this method for apps/user-facing code.
+ virtual void GetUsageAndQuota(
+ const GURL& origin,
+ StorageType type,
+ const GetUsageAndQuotaCallback& callback);
+
+ // Called by clients via proxy.
+ // Client storage should call this method when storage is accessed.
+ // Used to maintain LRU ordering.
+ void NotifyStorageAccessed(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type);
+
+ // Called by clients via proxy.
+ // Client storage 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);
+
+ // Used to avoid evicting origins with open pages.
+ // A call to NotifyOriginInUse must be balanced by a later call
+ // to NotifyOriginNoLongerInUse.
+ void NotifyOriginInUse(const GURL& origin);
+ void NotifyOriginNoLongerInUse(const GURL& origin);
+ bool IsOriginInUse(const GURL& origin) const {
+ return origins_in_use_.find(origin) != origins_in_use_.end();
+ }
+
+ void SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ bool enabled);
+
+ // DeleteOriginData and DeleteHostData (surprisingly enough) delete data of a
+ // particular StorageType associated with either a specific origin or set of
+ // origins. Each method additionally requires a |quota_client_mask| which
+ // specifies the types of QuotaClients to delete from the origin. This is
+ // specified by the caller as a bitmask built from QuotaClient::IDs. Setting
+ // the mask to QuotaClient::kAllClientsMask will remove all clients from the
+ // origin, regardless of type.
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback);
+ void DeleteHostData(const std::string& host,
+ StorageType type,
+ int quota_client_mask,
+ const StatusCallback& callback);
+
+ // Called by UI and internal modules.
+ void GetAvailableSpace(const AvailableSpaceCallback& callback);
+ void GetTemporaryGlobalQuota(const QuotaCallback& callback);
+
+ // Ok to call with NULL callback.
+ void SetTemporaryGlobalOverrideQuota(int64 new_quota,
+ const QuotaCallback& callback);
+
+ void GetPersistentHostQuota(const std::string& host,
+ const QuotaCallback& callback);
+ void SetPersistentHostQuota(const std::string& host,
+ int64 new_quota,
+ const QuotaCallback& callback);
+ void GetGlobalUsage(StorageType type, const GlobalUsageCallback& callback);
+ void GetHostUsage(const std::string& host, StorageType type,
+ const UsageCallback& callback);
+
+ void GetStatistics(std::map<std::string, std::string>* statistics);
+
+ bool IsStorageUnlimited(const GURL& origin, StorageType type) const;
+
+ bool CanQueryDiskSize(const GURL& origin) const {
+ return special_storage_policy_.get() &&
+ special_storage_policy_->CanQueryDiskSize(origin);
+ }
+
+ virtual void GetOriginsModifiedSince(StorageType type,
+ base::Time modified_since,
+ const GetOriginsCallback& callback);
+
+ bool ResetUsageTracker(StorageType type);
+
+ // Determines the portion of the temp pool that can be
+ // utilized by a single host (ie. 5 for 20%).
+ static const int kPerHostTemporaryPortion;
+
+ static const char kDatabaseName[];
+
+ static const int64 kMinimumPreserveForSystem;
+
+ static const int kThresholdOfErrorsToBeBlacklisted;
+
+ static const int kEvictionIntervalInMilliSeconds;
+
+ // This is kept non-const so that test code can change the value.
+ // TODO(kinuko): Make this a real const value and add a proper way to set
+ // the quota for syncable storage. (http://crbug.com/155488)
+ static int64 kSyncableStorageDefaultHostQuota;
+
+ protected:
+ virtual ~QuotaManager();
+
+ private:
+ friend class base::DeleteHelper<QuotaManager>;
+ friend class base::RefCountedThreadSafe<QuotaManager, QuotaManagerDeleter>;
+ friend class MockQuotaManager;
+ friend class MockStorageClient;
+ friend class quota_internals::QuotaInternalsProxy;
+ friend class QuotaManagerProxy;
+ friend class QuotaManagerTest;
+ friend class QuotaTemporaryStorageEvictor;
+ friend struct QuotaManagerDeleter;
+
+ class GetUsageInfoTask;
+
+ class OriginDataDeleter;
+ class HostDataDeleter;
+
+ class GetModifiedSinceHelper;
+ class DumpQuotaTableHelper;
+ class DumpOriginInfoTableHelper;
+
+ typedef QuotaDatabase::QuotaTableEntry QuotaTableEntry;
+ typedef QuotaDatabase::OriginInfoTableEntry OriginInfoTableEntry;
+ typedef std::vector<QuotaTableEntry> QuotaTableEntries;
+ typedef std::vector<OriginInfoTableEntry> OriginInfoTableEntries;
+
+ // Function pointer type used to store the function which returns the
+ // available disk space for the disk containing the given FilePath.
+ typedef int64 (*GetAvailableDiskSpaceFn)(const base::FilePath&);
+
+ typedef base::Callback<void(const QuotaTableEntries&)>
+ DumpQuotaTableCallback;
+ typedef base::Callback<void(const OriginInfoTableEntries&)>
+ DumpOriginInfoTableCallback;
+
+ struct EvictionContext {
+ EvictionContext();
+ virtual ~EvictionContext();
+ GURL evicted_origin;
+ StorageType evicted_type;
+
+ EvictOriginDataCallback evict_origin_data_callback;
+ };
+
+ typedef QuotaEvictionHandler::UsageAndQuotaCallback
+ UsageAndQuotaDispatcherCallback;
+
+ // 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);
+
+ UsageTracker* GetUsageTracker(StorageType type) const;
+
+ // Extract cached origins list from the usage tracker.
+ // (Might return empty list if no origin is tracked by the tracker.)
+ void GetCachedOrigins(StorageType type, std::set<GURL>* origins);
+
+ // These internal methods are separately defined mainly for testing.
+ void NotifyStorageAccessedInternal(
+ QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ base::Time accessed_time);
+ void NotifyStorageModifiedInternal(
+ QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta,
+ base::Time modified_time);
+
+ void DumpQuotaTable(const DumpQuotaTableCallback& callback);
+ void DumpOriginInfoTable(const DumpOriginInfoTableCallback& callback);
+
+ // Methods for eviction logic.
+ void StartEviction();
+ void DeleteOriginFromDatabase(const GURL& origin, StorageType type);
+
+ void DidOriginDataEvicted(QuotaStatusCode status);
+
+ void ReportHistogram();
+ void DidGetTemporaryGlobalUsageForHistogram(int64 usage,
+ int64 unlimited_usage);
+ void DidGetPersistentGlobalUsageForHistogram(int64 usage,
+ int64 unlimited_usage);
+
+ // QuotaEvictionHandler.
+ virtual void GetLRUOrigin(
+ StorageType type,
+ const GetLRUOriginCallback& callback) OVERRIDE;
+ virtual void EvictOriginData(
+ const GURL& origin,
+ StorageType type,
+ const EvictOriginDataCallback& callback) OVERRIDE;
+ virtual void GetUsageAndQuotaForEviction(
+ const UsageAndQuotaCallback& callback) OVERRIDE;
+
+ void DidSetTemporaryGlobalOverrideQuota(const QuotaCallback& callback,
+ const int64* new_quota,
+ bool success);
+ void DidGetPersistentHostQuota(const std::string& host,
+ const int64* quota,
+ bool success);
+ void DidSetPersistentHostQuota(const std::string& host,
+ const QuotaCallback& callback,
+ const int64* new_quota,
+ bool success);
+ void DidInitialize(int64* temporary_quota_override,
+ int64* desired_available_space,
+ bool success);
+ void DidGetLRUOrigin(const GURL* origin,
+ bool success);
+ void DidGetInitialTemporaryGlobalQuota(QuotaStatusCode status,
+ int64 quota_unused);
+ void DidInitializeTemporaryOriginsInfo(bool success);
+ void DidGetAvailableSpace(int64 space);
+ void DidDatabaseWork(bool success);
+
+ void DeleteOnCorrectThread() const;
+
+ void PostTaskAndReplyWithResultForDBThread(
+ const tracked_objects::Location& from_here,
+ const base::Callback<bool(QuotaDatabase*)>& task,
+ const base::Callback<void(bool)>& reply);
+
+ const bool is_incognito_;
+ const base::FilePath profile_path_;
+
+ scoped_refptr<QuotaManagerProxy> proxy_;
+ bool db_disabled_;
+ bool eviction_disabled_;
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_;
+ scoped_refptr<base::SequencedTaskRunner> db_thread_;
+ mutable scoped_ptr<QuotaDatabase> database_;
+
+ GetLRUOriginCallback lru_origin_callback_;
+ std::set<GURL> access_notified_origins_;
+
+ QuotaClientList clients_;
+
+ scoped_ptr<UsageTracker> temporary_usage_tracker_;
+ scoped_ptr<UsageTracker> persistent_usage_tracker_;
+ scoped_ptr<UsageTracker> syncable_usage_tracker_;
+ // TODO(michaeln): Need a way to clear the cache, drop and
+ // reinstantiate the trackers when they're not handling requests.
+
+ scoped_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_;
+ EvictionContext eviction_context_;
+
+ ClosureQueue db_initialization_callbacks_;
+ AvailableSpaceCallbackQueue available_space_callbacks_;
+ GlobalQuotaCallbackQueue temporary_global_quota_callbacks_;
+ HostQuotaCallbackMap persistent_host_quota_callbacks_;
+
+ bool temporary_quota_initialized_;
+ int64 temporary_quota_override_;
+
+ int64 desired_available_space_;
+
+ // Map from origin to count.
+ std::map<GURL, int> origins_in_use_;
+ // Map from origin to error count.
+ std::map<GURL, int> origins_in_error_;
+
+ scoped_refptr<SpecialStoragePolicy> special_storage_policy_;
+
+ base::WeakPtrFactory<QuotaManager> weak_factory_;
+ base::RepeatingTimer<QuotaManager> histogram_timer_;
+
+ // Pointer to the function used to get the available disk space. This is
+ // overwritten by QuotaManagerTest in order to attain a deterministic reported
+ // value. The default value points to base::SysInfo::AmountOfFreeDiskSpace.
+ GetAvailableDiskSpaceFn get_disk_space_fn_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaManager);
+};
+
+struct QuotaManagerDeleter {
+ static void Destruct(const QuotaManager* manager) {
+ manager->DeleteOnCorrectThread();
+ }
+};
+
+// The proxy may be called and finally released on any thread.
+class WEBKIT_STORAGE_EXPORT QuotaManagerProxy
+ : public base::RefCountedThreadSafe<QuotaManagerProxy> {
+ public:
+ virtual void RegisterClient(QuotaClient* client);
+ virtual void NotifyStorageAccessed(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type);
+ virtual void NotifyStorageModified(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ int64 delta);
+ virtual void NotifyOriginInUse(const GURL& origin);
+ virtual void NotifyOriginNoLongerInUse(const GURL& origin);
+
+ virtual void SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ StorageType type,
+ bool enabled);
+
+ // This method may only be called on the IO thread.
+ // It may return NULL if the manager has already been deleted.
+ QuotaManager* quota_manager() const;
+
+ protected:
+ friend class QuotaManager;
+ friend class base::RefCountedThreadSafe<QuotaManagerProxy>;
+
+ QuotaManagerProxy(QuotaManager* manager,
+ base::SingleThreadTaskRunner* io_thread);
+ virtual ~QuotaManagerProxy();
+
+ QuotaManager* manager_; // only accessed on the io thread
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaManagerProxy);
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_QUOTA_MANAGER_H_
diff --git a/webkit/browser/quota/quota_manager_unittest.cc b/webkit/browser/quota/quota_manager_unittest.cc
new file mode 100644
index 0000000..4d902d0
--- /dev/null
+++ b/webkit/browser/quota/quota_manager_unittest.cc
@@ -0,0 +1,2165 @@
+// Copyright 2013 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 <set>
+#include <sstream>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "base/sys_info.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/mock_storage_client.h"
+#include "webkit/browser/quota/quota_database.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+using base::MessageLoopProxy;
+
+namespace quota {
+
+namespace {
+
+// For shorter names.
+const StorageType kTemp = kStorageTypeTemporary;
+const StorageType kPerm = kStorageTypePersistent;
+const StorageType kSync = kStorageTypeSyncable;
+
+const int kAllClients = QuotaClient::kAllClientsMask;
+
+const int64 kAvailableSpaceForApp = 13377331U;
+
+const int64 kMinimumPreserveForSystem = QuotaManager::kMinimumPreserveForSystem;
+const int kPerHostTemporaryPortion = QuotaManager::kPerHostTemporaryPortion;
+
+// Returns a deterministic value for the amount of available disk space.
+int64 GetAvailableDiskSpaceForTest(const base::FilePath&) {
+ return kAvailableSpaceForApp + kMinimumPreserveForSystem;
+}
+
+} // namespace
+
+class QuotaManagerTest : public testing::Test {
+ protected:
+ typedef QuotaManager::QuotaTableEntry QuotaTableEntry;
+ typedef QuotaManager::QuotaTableEntries QuotaTableEntries;
+ typedef QuotaManager::OriginInfoTableEntries OriginInfoTableEntries;
+
+ public:
+ QuotaManagerTest()
+ : weak_factory_(this),
+ mock_time_counter_(0) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ mock_special_storage_policy_ = new MockSpecialStoragePolicy;
+ ResetQuotaManager(false /* is_incognito */);
+ }
+
+ virtual void TearDown() {
+ // Make sure the quota manager cleans up correctly.
+ quota_manager_ = NULL;
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ protected:
+ void ResetQuotaManager(bool is_incognito) {
+ quota_manager_ = new QuotaManager(
+ is_incognito,
+ data_dir_.path(),
+ MessageLoopProxy::current(),
+ MessageLoopProxy::current(),
+ mock_special_storage_policy_);
+ // Don't (automatically) start the eviction for testing.
+ quota_manager_->eviction_disabled_ = true;
+ // Don't query the hard disk for remaining capacity.
+ quota_manager_->get_disk_space_fn_ = &GetAvailableDiskSpaceForTest;
+ additional_callback_count_ = 0;
+ }
+
+ MockStorageClient* CreateClient(
+ const MockOriginData* mock_data,
+ size_t mock_data_size,
+ QuotaClient::ID id) {
+ return new MockStorageClient(quota_manager_->proxy(),
+ mock_data, id, mock_data_size);
+ }
+
+ void RegisterClient(MockStorageClient* client) {
+ quota_manager_->proxy()->RegisterClient(client);
+ }
+
+ void GetUsageInfo() {
+ usage_info_.clear();
+ quota_manager_->GetUsageInfo(
+ base::Bind(&QuotaManagerTest::DidGetUsageInfo,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForWebApps(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ quota_ = -1;
+ quota_manager_->GetUsageAndQuotaForWebApps(
+ origin, type, base::Bind(&QuotaManagerTest::DidGetUsageAndQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForStorageClient(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ quota_ = -1;
+ quota_manager_->GetUsageAndQuota(
+ origin, type, base::Bind(&QuotaManagerTest::DidGetUsageAndQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetTemporaryGlobalQuota() {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->GetTemporaryGlobalQuota(
+ base::Bind(&QuotaManagerTest::DidGetQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void SetTemporaryGlobalQuota(int64 new_quota) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->SetTemporaryGlobalOverrideQuota(
+ new_quota,
+ base::Bind(&QuotaManagerTest::DidGetQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetPersistentHostQuota(const std::string& host) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->GetPersistentHostQuota(
+ host,
+ base::Bind(&QuotaManagerTest::DidGetHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void SetPersistentHostQuota(const std::string& host, int64 new_quota) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_ = -1;
+ quota_manager_->SetPersistentHostQuota(
+ host, new_quota,
+ base::Bind(&QuotaManagerTest::DidGetHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetGlobalUsage(StorageType type) {
+ usage_ = -1;
+ unlimited_usage_ = -1;
+ quota_manager_->GetGlobalUsage(
+ type,
+ base::Bind(&QuotaManagerTest::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetHostUsage(const std::string& host, StorageType type) {
+ usage_ = -1;
+ quota_manager_->GetHostUsage(
+ host, type,
+ base::Bind(&QuotaManagerTest::DidGetHostUsage,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void RunAdditionalUsageAndQuotaTask(const GURL& origin, StorageType type) {
+ quota_manager_->GetUsageAndQuota(
+ origin, type,
+ base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaAdditional,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteClientOriginData(QuotaClient* client,
+ const GURL& origin,
+ StorageType type) {
+ DCHECK(client);
+ quota_status_ = kQuotaStatusUnknown;
+ client->DeleteOriginData(
+ origin, type,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void EvictOriginData(const GURL& origin,
+ StorageType type) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->EvictOriginData(
+ origin, type,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ int quota_client_mask) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->DeleteOriginData(
+ origin, type, quota_client_mask,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DeleteHostData(const std::string& host,
+ StorageType type,
+ int quota_client_mask) {
+ quota_status_ = kQuotaStatusUnknown;
+ quota_manager_->DeleteHostData(
+ host, type, quota_client_mask,
+ base::Bind(&QuotaManagerTest::StatusCallback,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetAvailableSpace() {
+ quota_status_ = kQuotaStatusUnknown;
+ available_space_ = -1;
+ quota_manager_->GetAvailableSpace(
+ base::Bind(&QuotaManagerTest::DidGetAvailableSpace,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetUsageAndQuotaForEviction() {
+ quota_status_ = kQuotaStatusUnknown;
+ usage_ = -1;
+ unlimited_usage_ = -1;
+ quota_ = -1;
+ available_space_ = -1;
+ quota_manager_->GetUsageAndQuotaForEviction(
+ base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaForEviction,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetCachedOrigins(StorageType type, std::set<GURL>* origins) {
+ ASSERT_TRUE(origins != NULL);
+ origins->clear();
+ quota_manager_->GetCachedOrigins(type, origins);
+ }
+
+ void NotifyStorageAccessed(QuotaClient* client,
+ const GURL& origin,
+ StorageType type) {
+ DCHECK(client);
+ quota_manager_->NotifyStorageAccessedInternal(
+ client->id(), origin, type, IncrementMockTime());
+ }
+
+ void DeleteOriginFromDatabase(const GURL& origin, StorageType type) {
+ quota_manager_->DeleteOriginFromDatabase(origin, type);
+ }
+
+ void GetLRUOrigin(StorageType type) {
+ lru_origin_ = GURL();
+ quota_manager_->GetLRUOrigin(
+ type,
+ base::Bind(&QuotaManagerTest::DidGetLRUOrigin,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void NotifyOriginInUse(const GURL& origin) {
+ quota_manager_->NotifyOriginInUse(origin);
+ }
+
+ void NotifyOriginNoLongerInUse(const GURL& origin) {
+ quota_manager_->NotifyOriginNoLongerInUse(origin);
+ }
+
+ void GetOriginsModifiedSince(StorageType type, base::Time modified_since) {
+ modified_origins_.clear();
+ modified_origins_type_ = kStorageTypeUnknown;
+ quota_manager_->GetOriginsModifiedSince(
+ type, modified_since,
+ base::Bind(&QuotaManagerTest::DidGetModifiedOrigins,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DumpQuotaTable() {
+ quota_entries_.clear();
+ quota_manager_->DumpQuotaTable(
+ base::Bind(&QuotaManagerTest::DidDumpQuotaTable,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DumpOriginInfoTable() {
+ origin_info_entries_.clear();
+ quota_manager_->DumpOriginInfoTable(
+ base::Bind(&QuotaManagerTest::DidDumpOriginInfoTable,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void DidGetUsageInfo(const UsageInfoEntries& entries) {
+ usage_info_.insert(usage_info_.begin(), entries.begin(), entries.end());
+ }
+
+ void DidGetUsageAndQuota(QuotaStatusCode status, int64 usage, int64 quota) {
+ quota_status_ = status;
+ usage_ = usage;
+ quota_ = quota;
+ }
+
+ void DidGetQuota(QuotaStatusCode status,
+ int64 quota) {
+ quota_status_ = status;
+ quota_ = quota;
+ }
+
+ void DidGetAvailableSpace(QuotaStatusCode status, int64 available_space) {
+ quota_status_ = status;
+ available_space_ = available_space;
+ }
+
+ void DidGetHostQuota(QuotaStatusCode status,
+ int64 quota) {
+ quota_status_ = status;
+ quota_ = quota;
+ }
+
+ void DidGetGlobalUsage(int64 usage,
+ int64 unlimited_usage) {
+ usage_ = usage;
+ unlimited_usage_ = unlimited_usage;
+ }
+
+ void DidGetHostUsage(int64 usage) {
+ usage_ = usage;
+ }
+
+ void StatusCallback(QuotaStatusCode status) {
+ ++status_callback_count_;
+ quota_status_ = status;
+ }
+
+ void DidGetUsageAndQuotaForEviction(QuotaStatusCode status,
+ const UsageAndQuota& usage_and_quota) {
+ quota_status_ = status;
+ limited_usage_ = usage_and_quota.global_limited_usage;
+ quota_ = usage_and_quota.quota;
+ available_space_ = usage_and_quota.available_disk_space;
+ }
+
+ void DidGetLRUOrigin(const GURL& origin) {
+ lru_origin_ = origin;
+ }
+
+ void DidGetModifiedOrigins(const std::set<GURL>& origins, StorageType type) {
+ modified_origins_ = origins;
+ modified_origins_type_ = type;
+ }
+
+ void DidDumpQuotaTable(const QuotaTableEntries& entries) {
+ quota_entries_ = entries;
+ }
+
+ void DidDumpOriginInfoTable(const OriginInfoTableEntries& entries) {
+ origin_info_entries_ = entries;
+ }
+
+ void GetUsage_WithModifyTestBody(const StorageType type);
+
+ void set_additional_callback_count(int c) { additional_callback_count_ = c; }
+ int additional_callback_count() const { return additional_callback_count_; }
+ void DidGetUsageAndQuotaAdditional(
+ QuotaStatusCode status, int64 usage, int64 quota) {
+ ++additional_callback_count_;
+ }
+
+ QuotaManager* quota_manager() const { return quota_manager_.get(); }
+ void set_quota_manager(QuotaManager* quota_manager) {
+ quota_manager_ = quota_manager;
+ }
+
+ MockSpecialStoragePolicy* mock_special_storage_policy() const {
+ return mock_special_storage_policy_.get();
+ }
+
+ QuotaStatusCode status() const { return quota_status_; }
+ const UsageInfoEntries& usage_info() const { return usage_info_; }
+ int64 usage() const { return usage_; }
+ int64 limited_usage() const { return limited_usage_; }
+ int64 unlimited_usage() const { return unlimited_usage_; }
+ int64 quota() const { return quota_; }
+ int64 available_space() const { return available_space_; }
+ const GURL& lru_origin() const { return lru_origin_; }
+ const std::set<GURL>& modified_origins() const { return modified_origins_; }
+ StorageType modified_origins_type() const { return modified_origins_type_; }
+ const QuotaTableEntries& quota_entries() const { return quota_entries_; }
+ const OriginInfoTableEntries& origin_info_entries() const {
+ return origin_info_entries_;
+ }
+ base::FilePath profile_path() const { return data_dir_.path(); }
+ int status_callback_count() const { return status_callback_count_; }
+ void reset_status_callback_count() { status_callback_count_ = 0; }
+
+ private:
+ base::Time IncrementMockTime() {
+ ++mock_time_counter_;
+ return base::Time::FromDoubleT(mock_time_counter_ * 10.0);
+ }
+
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir data_dir_;
+ base::WeakPtrFactory<QuotaManagerTest> weak_factory_;
+
+ scoped_refptr<QuotaManager> quota_manager_;
+ scoped_refptr<MockSpecialStoragePolicy> mock_special_storage_policy_;
+
+ QuotaStatusCode quota_status_;
+ UsageInfoEntries usage_info_;
+ int64 usage_;
+ int64 limited_usage_;
+ int64 unlimited_usage_;
+ int64 quota_;
+ int64 available_space_;
+ GURL lru_origin_;
+ std::set<GURL> modified_origins_;
+ StorageType modified_origins_type_;
+ QuotaTableEntries quota_entries_;
+ OriginInfoTableEntries origin_info_entries_;
+ int status_callback_count_;
+
+ int additional_callback_count_;
+
+ int mock_time_counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaManagerTest);
+};
+
+TEST_F(QuotaManagerTest, GetUsageInfo) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 15 },
+ { "http://bar.com/", kTemp, 20 },
+ { "http://bar.com/", kPerm, 50 },
+ };
+ static const MockOriginData kData2[] = {
+ { "https://foo.com/", kTemp, 30 },
+ { "https://foo.com:8081/", kTemp, 35 },
+ { "http://bar.com/", kPerm, 40 },
+ { "http://example.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem));
+ RegisterClient(CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase));
+
+ GetUsageInfo();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(4U, usage_info().size());
+ for (size_t i = 0; i < usage_info().size(); ++i) {
+ const UsageInfo& info = usage_info()[i];
+ if (info.host == "foo.com" && info.type == kTemp) {
+ EXPECT_EQ(10 + 15 + 30 + 35, info.usage);
+ } else if (info.host == "bar.com" && info.type == kTemp) {
+ EXPECT_EQ(20, info.usage);
+ } else if (info.host == "bar.com" && info.type == kPerm) {
+ EXPECT_EQ(50 + 40, info.usage);
+ } else if (info.host == "example.com" && info.type == kPerm) {
+ EXPECT_EQ(40, info.usage);
+ } else {
+ ADD_FAILURE()
+ << "Unexpected host, type: " << info.host << ", " << info.type;
+ }
+ }
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuota_Simple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com/", kPerm, 80 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(0, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_LE(0, quota());
+ int64 quota_returned_for_foo = quota();
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(quota_returned_for_foo, quota());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_NoClient) {
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_EmptyClient) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_MultiOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 5 },
+ { "https://bar.com/", kTemp, 7 },
+ { "http://baz.com/", kTemp, 30 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ // This time explicitly sets a temporary global quota.
+ SetTemporaryGlobalQuota(100);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+
+ const int kPerHostQuota = 100 / kPerHostTemporaryPortion;
+
+ // The host's quota should be its full portion of the global quota
+ // since global usage is under the global quota.
+ EXPECT_EQ(kPerHostQuota, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(5 + 7, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_MultipleClients) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://bar.com/", kTemp, 2 },
+ { "http://bar.com/", kPerm, 4 },
+ { "http://unlimited/", kPerm, 8 },
+ { "http://installed/", kPerm, 16 },
+ };
+ static const MockOriginData kData2[] = {
+ { "https://foo.com/", kTemp, 128 },
+ { "http://example.com/", kPerm, 256 },
+ { "http://unlimited/", kTemp, 512 },
+ { "http://installed/", kTemp, 1024 },
+ };
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ RegisterClient(CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem));
+ RegisterClient(CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase));
+
+ const int64 kTempQuotaBase =
+ GetAvailableDiskSpaceForTest(base::FilePath()) / kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1 + 128, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4, usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(512, usage());
+ EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(8, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetAvailableSpace();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_LE(0, available_space());
+
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1024, usage());
+ EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(16, usage());
+ EXPECT_EQ(usage(), quota()); // Over-budget case.
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1 + 2 + 128 + 512 + 1024, usage());
+ EXPECT_EQ(512, unlimited_usage());
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4 + 8 + 16 + 256, usage());
+ EXPECT_EQ(8, unlimited_usage());
+}
+
+void QuotaManagerTest::GetUsage_WithModifyTestBody(const StorageType type) {
+ const MockOriginData data[] = {
+ { "http://foo.com/", type, 10 },
+ { "http://foo.com:1/", type, 20 },
+ };
+ MockStorageClient* client = CreateClient(data, ARRAYSIZE_UNSAFE(data),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), type);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+
+ client->ModifyOriginAndNotify(GURL("http://foo.com/"), type, 30);
+ client->ModifyOriginAndNotify(GURL("http://foo.com:1/"), type, -5);
+ client->AddOriginAndNotify(GURL("https://foo.com/"), type, 1);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), type);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20 + 30 - 5 + 1, usage());
+ int foo_usage = usage();
+
+ client->AddOriginAndNotify(GURL("http://bar.com/"), type, 40);
+ GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), type);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(40, usage());
+
+ GetGlobalUsage(type);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(foo_usage + 40, usage());
+ EXPECT_EQ(0, unlimited_usage());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsage_WithModify) {
+ GetUsage_WithModifyTestBody(kTemp);
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_WithAdditionalTasks) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 13 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+
+ set_additional_callback_count(0);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kTemp);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(kPerHostQuota, quota());
+ EXPECT_EQ(2, additional_callback_count());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com:8080/", kTemp, 20 },
+ { "http://bar.com/", kTemp, 13 },
+ { "http://foo.com/", kPerm, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ set_additional_callback_count(0);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kTemp);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"),
+ kTemp);
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://bar.com/"), kTemp, kAllClients);
+
+ // Nuke before waiting for callbacks.
+ set_quota_manager(NULL);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorAbort, status());
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Overbudget) {
+ static const MockOriginData kData[] = {
+ { "http://usage1/", kTemp, 1 },
+ { "http://usage10/", kTemp, 10 },
+ { "http://usage200/", kTemp, 200 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetTemporaryGlobalQuota(100);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage1/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(1, usage());
+ EXPECT_EQ(1, quota()); // should be clamped to our current usage
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(10, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage200/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(200, usage());
+ EXPECT_EQ(kPerHostQuota, quota()); // should be clamped to the nominal quota
+}
+
+TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_Unlimited) {
+ static const MockOriginData kData[] = {
+ { "http://usage10/", kTemp, 10 },
+ { "http://usage50/", kTemp, 50 },
+ { "http://unlimited/", kTemp, 4000 },
+ };
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ // Test when not overbugdet.
+ SetTemporaryGlobalQuota(1000);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(10 + 50 + 4000, usage());
+ EXPECT_EQ(4000, unlimited_usage());
+
+ const int kPerHostQuotaFor1000 =
+ 1000 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(kPerHostQuotaFor1000, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor1000, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+
+ // Test when overbugdet.
+ SetTemporaryGlobalQuota(100);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ const int kPerHostQuotaFor100 =
+ 100 / QuotaManager::kPerHostTemporaryPortion;
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+
+ // Revoke the unlimited rights and make sure the change is noticed.
+ mock_special_storage_policy()->Reset();
+ mock_special_storage_policy()->NotifyCleared();
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(10 + 50 + 4000, usage());
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(10, quota()); // should be clamped to our current usage
+
+ GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(50, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(4000, usage());
+ EXPECT_EQ(kPerHostQuotaFor100, quota());
+}
+
+TEST_F(QuotaManagerTest, OriginInUse) {
+ const GURL kFooOrigin("http://foo.com/");
+ const GURL kBarOrigin("http://bar.com/");
+
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 1
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginInUse(kFooOrigin); // count of 2
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+ quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin); // count of 1
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kFooOrigin));
+
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin));
+ quota_manager()->NotifyOriginInUse(kBarOrigin);
+ EXPECT_TRUE(quota_manager()->IsOriginInUse(kBarOrigin));
+ quota_manager()->NotifyOriginNoLongerInUse(kBarOrigin);
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kBarOrigin));
+
+ quota_manager()->NotifyOriginNoLongerInUse(kFooOrigin);
+ EXPECT_FALSE(quota_manager()->IsOriginInUse(kFooOrigin));
+}
+
+TEST_F(QuotaManagerTest, GetAndSetPerststentHostQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ GetPersistentHostQuota("foo.com");
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota("foo.com", 100);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(100, quota());
+
+ GetPersistentHostQuota("foo.com");
+ SetPersistentHostQuota("foo.com", 200);
+ GetPersistentHostQuota("foo.com");
+ SetPersistentHostQuota("foo.com", 300000000000ll);
+ GetPersistentHostQuota("foo.com");
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(300000000000ll, quota());
+}
+
+TEST_F(QuotaManagerTest, GetAndSetPersistentUsageAndQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota("foo.com", 100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(100, quota());
+
+ // For installed app GetUsageAndQuotaForWebApps returns the capped quota.
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ SetPersistentHostQuota("installed", kAvailableSpaceForApp + 100);
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // Ditto for unlimited apps.
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // GetUsageAndQuotaForStorageClient should just return 0 usage and
+ // kNoLimit quota.
+ GetUsageAndQuotaForStorageClient(GURL("http://unlimited/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kNoLimit, quota());
+}
+
+TEST_F(QuotaManagerTest, GetSyncableQuota) {
+ RegisterClient(CreateClient(NULL, 0, QuotaClient::kFileSystem));
+
+ // Pre-condition check: available disk space (for testing) is less than
+ // the default quota for syncable storage.
+ EXPECT_LE(kAvailableSpaceForApp,
+ QuotaManager::kSyncableStorageDefaultHostQuota);
+
+ // For installed apps the quota manager should return
+ // kAvailableSpaceForApp as syncable quota (because of the pre-condition).
+ mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
+ GetUsageAndQuotaForWebApps(GURL("http://installed/"), kSync);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(kAvailableSpaceForApp, quota());
+
+ // If it's not installed (which shouldn't happen in real case) it
+ // should just return the default host quota for syncable.
+ GetUsageAndQuotaForWebApps(GURL("http://foo/"), kSync);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, usage());
+ EXPECT_EQ(QuotaManager::kSyncableStorageDefaultHostQuota, quota());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_MultiOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "https://foo.com/", kPerm, 13 },
+ { "https://foo.com:8081/", kPerm, 19 },
+ { "http://bar.com/", kPerm, 5 },
+ { "https://bar.com/", kPerm, 7 },
+ { "http://baz.com/", kPerm, 30 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ SetPersistentHostQuota("foo.com", 100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20 + 13 + 19, usage());
+ EXPECT_EQ(100, quota());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsage_WithModify) {
+ GetUsage_WithModifyTestBody(kPerm);
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_WithAdditionalTasks) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "http://bar.com/", kPerm, 13 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetPersistentHostQuota("foo.com", 100);
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(100, quota());
+
+ set_additional_callback_count(0);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"),
+ kPerm);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10 + 20, usage());
+ EXPECT_EQ(2, additional_callback_count());
+}
+
+TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_NukeManager) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 10 },
+ { "http://foo.com:8080/", kPerm, 20 },
+ { "http://bar.com/", kPerm, 13 },
+ { "http://foo.com/", kTemp, 40 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+ SetPersistentHostQuota("foo.com", 100);
+
+ set_additional_callback_count(0);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://foo.com/"), kPerm);
+ RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kPerm);
+
+ // Nuke before waiting for callbacks.
+ set_quota_manager(NULL);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorAbort, status());
+}
+
+TEST_F(QuotaManagerTest, GetUsage_Simple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 1 },
+ { "http://foo.com:1/", kPerm, 20 },
+ { "http://bar.com/", kTemp, 300 },
+ { "https://buz.com/", kTemp, 4000 },
+ { "http://buz.com/", kTemp, 50000 },
+ { "http://bar.com:1/", kPerm, 600000 },
+ { "http://foo.com/", kTemp, 7000000 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20);
+
+ GetHostUsage("buz.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000);
+}
+
+TEST_F(QuotaManagerTest, GetUsage_WithModification) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kPerm, 1 },
+ { "http://foo.com:1/", kPerm, 20 },
+ { "http://bar.com/", kTemp, 300 },
+ { "https://buz.com/", kTemp, 4000 },
+ { "http://buz.com/", kTemp, 50000 },
+ { "http://bar.com:1/", kPerm, 600000 },
+ { "http://foo.com/", kTemp, 7000000 },
+ };
+
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ client->ModifyOriginAndNotify(
+ GURL("http://foo.com/"), kPerm, 80000000);
+
+ GetGlobalUsage(kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 1 + 20 + 600000 + 80000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000);
+ EXPECT_EQ(0, unlimited_usage());
+
+ client->ModifyOriginAndNotify(
+ GURL("http://foo.com/"), kTemp, 1);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 300 + 4000 + 50000 + 7000000 + 1);
+ EXPECT_EQ(0, unlimited_usage());
+
+ GetHostUsage("buz.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000);
+
+ client->ModifyOriginAndNotify(
+ GURL("http://buz.com/"), kTemp, 900000000);
+
+ GetHostUsage("buz.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(usage(), 4000 + 50000 + 900000000);
+}
+
+TEST_F(QuotaManagerTest, GetUsage_WithDeleteOrigin) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ DeleteClientOriginData(client, GURL("http://foo.com/"),
+ kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetAvailableSpaceTest) {
+ GetAvailableSpace();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_LE(0, available_space());
+}
+
+TEST_F(QuotaManagerTest, EvictOriginData) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData1); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData1[i].origin), kData1[i].type);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData2); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData2[i].origin), kData2[i].type);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EvictOriginData(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ DumpOriginInfoTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp)
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ }
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 50000), usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - (1 + 50000), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, EvictOriginDataWithDeletionError) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const int kNumberOfTemporaryOrigins = 3;
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i)
+ NotifyStorageAccessed(client, GURL(kData[i].origin), kData[i].type);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ client->AddOriginToErrorSet(GURL("http://foo.com/"), kTemp);
+
+ for (int i = 0;
+ i < QuotaManager::kThresholdOfErrorsToBeBlacklisted + 1;
+ ++i) {
+ EvictOriginData(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorInvalidModification, status());
+ }
+
+ DumpOriginInfoTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ bool found_origin_in_database = false;
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp &&
+ GURL("http://foo.com/") == itr->origin) {
+ found_origin_in_database = true;
+ break;
+ }
+ }
+ // The origin "http://foo.com/" should be in the database.
+ EXPECT_TRUE(found_origin_in_database);
+
+ for (size_t i = 0; i < kNumberOfTemporaryOrigins - 1; ++i) {
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(lru_origin().is_empty());
+ // The origin "http://foo.com/" should not be in the LRU list.
+ EXPECT_NE(std::string("http://foo.com/"), lru_origin().spec());
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ // Now the LRU list must be empty.
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ // Deleting origins from the database should not affect the results of the
+ // following checks.
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuotaForEviction) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://unlimited/", kTemp, 4000 },
+ };
+
+ mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ SetTemporaryGlobalQuota(10000000);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ GetUsageAndQuotaForEviction();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(21, limited_usage());
+ EXPECT_EQ(10000000, quota());
+ EXPECT_LE(0, available_space());
+}
+
+TEST_F(QuotaManagerTest, DeleteHostDataSimple) {
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ int64 predelete_host_pers = usage();
+
+ DeleteHostData(std::string(), kTemp, kAllClients);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_tmp - 1, usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_host_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteHostDataMultiple) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ GetHostUsage("bar.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_bar_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_pers = usage();
+
+ GetHostUsage("bar.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_bar_pers = usage();
+
+ reset_status_callback_count();
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ DeleteHostData("bar.com", kTemp, kAllClients);
+ DeleteHostData("foo.com", kTemp, kAllClients);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(3, status_callback_count());
+
+ DumpOriginInfoTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp) {
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://foo.com:1/"), itr->origin.spec());
+ EXPECT_NE(std::string("https://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://bar.com/"), itr->origin.spec());
+ }
+ }
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 20 + 4000 + 50000 + 6000 + 80 + 9),
+ usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - (1 + 20 + 50000 + 6000 + 80), usage());
+
+ GetHostUsage("bar.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_bar_tmp - (4000 + 9), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_pers, usage());
+
+ GetHostUsage("bar.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_bar_pers, usage());
+}
+
+// Single-run DeleteOriginData cases must be well covered by
+// EvictOriginData tests.
+TEST_F(QuotaManagerTest, DeleteOriginDataMultiple) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ { "http://foo.com:1/", kTemp, 20 },
+ { "http://foo.com/", kPerm, 300 },
+ { "http://bar.com/", kTemp, 4000 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 50000 },
+ { "http://foo.com:1/", kTemp, 6000 },
+ { "http://foo.com/", kPerm, 700 },
+ { "https://foo.com/", kTemp, 80 },
+ { "http://bar.com/", kTemp, 9 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_global_tmp = usage();
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ GetHostUsage("bar.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_bar_tmp = usage();
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_pers = usage();
+
+ GetHostUsage("bar.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_bar_pers = usage();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData1); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData1[i].origin), kData1[i].type);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData2); ++i)
+ quota_manager()->NotifyStorageAccessed(QuotaClient::kUnknown,
+ GURL(kData2[i].origin), kData2[i].type);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ reset_status_callback_count();
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://bar.com/"), kTemp, kAllClients);
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, kAllClients);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(3, status_callback_count());
+
+ DumpOriginInfoTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ if (itr->type == kTemp) {
+ EXPECT_NE(std::string("http://foo.com/"), itr->origin.spec());
+ EXPECT_NE(std::string("http://bar.com/"), itr->origin.spec());
+ }
+ }
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_global_tmp - (1 + 4000 + 50000 + 9), usage());
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - (1 + 50000), usage());
+
+ GetHostUsage("bar.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_bar_tmp - (4000 + 9), usage());
+
+ GetHostUsage("foo.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_pers, usage());
+
+ GetHostUsage("bar.com", kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_bar_pers, usage());
+}
+
+TEST_F(QuotaManagerTest, GetCachedOrigins) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 1 },
+ { "http://a.com:1/", kTemp, 20 },
+ { "http://b.com/", kPerm, 300 },
+ { "http://c.com/", kTemp, 4000 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ // TODO(kinuko): Be careful when we add cache pruner.
+
+ std::set<GURL> origins;
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_TRUE(origins.empty());
+
+ // No matter how we make queries the quota manager tries to cache all
+ // the origins at startup.
+ GetHostUsage("a.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ GetHostUsage("b.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ GetCachedOrigins(kPerm, &origins);
+ EXPECT_TRUE(origins.empty());
+
+ GetGlobalUsage(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetCachedOrigins(kTemp, &origins);
+ EXPECT_EQ(3U, origins.size());
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i) {
+ if (kData[i].type == kTemp)
+ EXPECT_TRUE(origins.find(GURL(kData[i].origin)) != origins.end());
+ }
+}
+
+TEST_F(QuotaManagerTest, NotifyAndLRUOrigin) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GURL origin;
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyStorageAccessed(client, GURL("http://a.com/"), kTemp);
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ NotifyStorageAccessed(client, GURL("http://b.com/"), kPerm);
+ NotifyStorageAccessed(client, GURL("https://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("https://a.com/", lru_origin().spec());
+
+ DeleteOriginFromDatabase(lru_origin(), kTemp);
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("http://c.com/", lru_origin().spec());
+}
+
+TEST_F(QuotaManagerTest, GetLRUOriginWithOriginInUse) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GURL origin;
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyStorageAccessed(client, GURL("http://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://b.com/"), kPerm);
+ NotifyStorageAccessed(client, GURL("https://a.com/"), kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+
+ // Notify origin http://a.com is in use.
+ NotifyOriginInUse(GURL("http://a.com/"));
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("https://a.com/", lru_origin().spec());
+
+ // Notify origin https://a.com is in use while GetLRUOrigin is running.
+ GetLRUOrigin(kTemp);
+ NotifyOriginInUse(GURL("https://a.com/"));
+ base::MessageLoop::current()->RunUntilIdle();
+ // Post-filtering must have excluded the returned origin, so we will
+ // see empty result here.
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ // Notify access for http://c.com while GetLRUOrigin is running.
+ GetLRUOrigin(kTemp);
+ NotifyStorageAccessed(client, GURL("http://c.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ // Post-filtering must have excluded the returned origin, so we will
+ // see empty result here.
+ EXPECT_TRUE(lru_origin().is_empty());
+
+ NotifyOriginNoLongerInUse(GURL("http://a.com/"));
+ NotifyOriginNoLongerInUse(GURL("https://a.com/"));
+ GetLRUOrigin(kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ("http://a.com/", lru_origin().spec());
+}
+
+TEST_F(QuotaManagerTest, GetOriginsModifiedSince) {
+ static const MockOriginData kData[] = {
+ { "http://a.com/", kTemp, 0 },
+ { "http://a.com:1/", kTemp, 0 },
+ { "https://a.com/", kTemp, 0 },
+ { "http://b.com/", kPerm, 0 }, // persistent
+ { "http://c.com/", kTemp, 0 },
+ };
+ MockStorageClient* client = CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem);
+ RegisterClient(client);
+
+ GetOriginsModifiedSince(kTemp, base::Time());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(modified_origins().empty());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+
+ base::Time time1 = client->IncrementMockTime();
+ client->ModifyOriginAndNotify(GURL("http://a.com/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://a.com:1/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://b.com/"), kPerm, 10);
+ base::Time time2 = client->IncrementMockTime();
+ client->ModifyOriginAndNotify(GURL("https://a.com/"), kTemp, 10);
+ client->ModifyOriginAndNotify(GURL("http://c.com/"), kTemp, 10);
+ base::Time time3 = client->IncrementMockTime();
+
+ GetOriginsModifiedSince(kTemp, time1);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(4U, modified_origins().size());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData); ++i) {
+ if (kData[i].type == kTemp)
+ EXPECT_EQ(1U, modified_origins().count(GURL(kData[i].origin)));
+ }
+
+ GetOriginsModifiedSince(kTemp, time2);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(2U, modified_origins().size());
+
+ GetOriginsModifiedSince(kTemp, time3);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(modified_origins().empty());
+ EXPECT_EQ(modified_origins_type(), kTemp);
+
+ client->ModifyOriginAndNotify(GURL("http://a.com/"), kTemp, 10);
+
+ GetOriginsModifiedSince(kTemp, time3);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(1U, modified_origins().size());
+ EXPECT_EQ(1U, modified_origins().count(GURL("http://a.com/")));
+ EXPECT_EQ(modified_origins_type(), kTemp);
+}
+
+TEST_F(QuotaManagerTest, DumpQuotaTable) {
+ SetPersistentHostQuota("example1.com", 1);
+ SetPersistentHostQuota("example2.com", 20);
+ SetPersistentHostQuota("example3.com", 300);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ DumpQuotaTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ const QuotaTableEntry kEntries[] = {
+ QuotaTableEntry("example1.com", kPerm, 1),
+ QuotaTableEntry("example2.com", kPerm, 20),
+ QuotaTableEntry("example3.com", kPerm, 300),
+ };
+ std::set<QuotaTableEntry> entries
+ (kEntries, kEntries + ARRAYSIZE_UNSAFE(kEntries));
+
+ typedef QuotaTableEntries::const_iterator iterator;
+ for (iterator itr(quota_entries().begin()), end(quota_entries().end());
+ itr != end; ++itr) {
+ SCOPED_TRACE(testing::Message()
+ << "host = " << itr->host << ", "
+ << "quota = " << itr->quota);
+ EXPECT_EQ(1u, entries.erase(*itr));
+ }
+ EXPECT_TRUE(entries.empty());
+}
+
+TEST_F(QuotaManagerTest, DumpOriginInfoTable) {
+ using std::make_pair;
+
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kTemp);
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kPerm);
+ quota_manager()->NotifyStorageAccessed(
+ QuotaClient::kUnknown,
+ GURL("http://example.com/"),
+ kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ DumpOriginInfoTable();
+ base::MessageLoop::current()->RunUntilIdle();
+
+ typedef std::pair<GURL, StorageType> TypedOrigin;
+ typedef std::pair<TypedOrigin, int> Entry;
+ const Entry kEntries[] = {
+ make_pair(make_pair(GURL("http://example.com/"), kTemp), 1),
+ make_pair(make_pair(GURL("http://example.com/"), kPerm), 2),
+ };
+ std::set<Entry> entries
+ (kEntries, kEntries + ARRAYSIZE_UNSAFE(kEntries));
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(origin_info_entries().begin()),
+ end(origin_info_entries().end());
+ itr != end; ++itr) {
+ SCOPED_TRACE(testing::Message()
+ << "host = " << itr->origin << ", "
+ << "type = " << itr->type << ", "
+ << "used_count = " << itr->used_count);
+ EXPECT_EQ(1u, entries.erase(
+ make_pair(make_pair(itr->origin, itr->type),
+ itr->used_count)));
+ }
+ EXPECT_TRUE(entries.empty());
+}
+
+TEST_F(QuotaManagerTest, QuotaForEmptyHost) {
+ GetPersistentHostQuota(std::string());
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(0, quota());
+
+ SetPersistentHostQuota(std::string(), 10);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaErrorNotSupported, status());
+}
+
+TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleOrigin) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kFileSystem);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kAppcache);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp, QuotaClient::kDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kIndexedDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteSpecificClientTypeSingleHost) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com:1111/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com:2222/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com:3333/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com:4444/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kFileSystem);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kAppcache);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp, QuotaClient::kIndexedDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleOrigin) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kFileSystem | QuotaClient::kDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 4 - 1, usage());
+
+ DeleteOriginData(GURL("http://foo.com/"), kTemp,
+ QuotaClient::kAppcache | QuotaClient::kIndexedDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, DeleteMultipleClientTypesSingleHost) {
+ static const MockOriginData kData1[] = {
+ { "http://foo.com:1111/", kTemp, 1 },
+ };
+ static const MockOriginData kData2[] = {
+ { "http://foo.com:2222/", kTemp, 2 },
+ };
+ static const MockOriginData kData3[] = {
+ { "http://foo.com:3333/", kTemp, 4 },
+ };
+ static const MockOriginData kData4[] = {
+ { "http://foo.com:4444/", kTemp, 8 },
+ };
+ MockStorageClient* client1 = CreateClient(kData1, ARRAYSIZE_UNSAFE(kData1),
+ QuotaClient::kFileSystem);
+ MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2),
+ QuotaClient::kAppcache);
+ MockStorageClient* client3 = CreateClient(kData3, ARRAYSIZE_UNSAFE(kData3),
+ QuotaClient::kDatabase);
+ MockStorageClient* client4 = CreateClient(kData4, ARRAYSIZE_UNSAFE(kData4),
+ QuotaClient::kIndexedDatabase);
+ RegisterClient(client1);
+ RegisterClient(client2);
+ RegisterClient(client3);
+ RegisterClient(client4);
+
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ const int64 predelete_foo_tmp = usage();
+
+ DeleteHostData("foo.com", kTemp,
+ QuotaClient::kFileSystem | QuotaClient::kAppcache);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 2 - 1, usage());
+
+ DeleteHostData("foo.com", kTemp,
+ QuotaClient::kDatabase | QuotaClient::kIndexedDatabase);
+ base::MessageLoop::current()->RunUntilIdle();
+ GetHostUsage("foo.com", kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(predelete_foo_tmp - 8 - 4 - 2 - 1, usage());
+}
+
+TEST_F(QuotaManagerTest, GetUsageAndQuota_Incognito) {
+ ResetQuotaManager(true);
+
+ static const MockOriginData kData[] = {
+ { "http://foo.com/", kTemp, 10 },
+ { "http://foo.com/", kPerm, 80 },
+ };
+ RegisterClient(CreateClient(kData, ARRAYSIZE_UNSAFE(kData),
+ QuotaClient::kFileSystem));
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(0, quota());
+
+ SetTemporaryGlobalQuota(100);
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_LE(std::min(static_cast<int64>(100 / kPerHostTemporaryPortion),
+ QuotaManager::kIncognitoDefaultQuotaLimit), quota());
+
+ mock_special_storage_policy()->AddUnlimited(GURL("http://foo.com/"));
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(80, usage());
+ EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
+
+ GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(kQuotaStatusOk, status());
+ EXPECT_EQ(10, usage());
+ EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/quota_task.cc b/webkit/browser/quota/quota_task.cc
new file mode 100644
index 0000000..95b02f1
--- /dev/null
+++ b/webkit/browser/quota/quota_task.cc
@@ -0,0 +1,79 @@
+// Copyright 2013 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/browser/quota/quota_task.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+
+using base::TaskRunner;
+
+namespace quota {
+
+// QuotaTask ---------------------------------------------------------------
+
+QuotaTask::~QuotaTask() {
+}
+
+void QuotaTask::Start() {
+ DCHECK(observer_);
+ observer()->RegisterTask(this);
+ Run();
+}
+
+QuotaTask::QuotaTask(QuotaTaskObserver* observer)
+ : observer_(observer),
+ original_task_runner_(base::MessageLoopProxy::current()),
+ delete_scheduled_(false) {
+}
+
+void QuotaTask::CallCompleted() {
+ DCHECK(original_task_runner_->BelongsToCurrentThread());
+ if (observer_) {
+ observer_->UnregisterTask(this);
+ Completed();
+ }
+}
+
+void QuotaTask::Abort() {
+ DCHECK(original_task_runner_->BelongsToCurrentThread());
+ observer_ = NULL;
+ Aborted();
+}
+
+void QuotaTask::DeleteSoon() {
+ DCHECK(original_task_runner_->BelongsToCurrentThread());
+ if (delete_scheduled_)
+ return;
+ delete_scheduled_ = true;
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+// QuotaTaskObserver -------------------------------------------------------
+
+QuotaTaskObserver::~QuotaTaskObserver() {
+ std::for_each(running_quota_tasks_.begin(),
+ running_quota_tasks_.end(),
+ std::mem_fun(&QuotaTask::Abort));
+}
+
+QuotaTaskObserver::QuotaTaskObserver() {
+}
+
+void QuotaTaskObserver::RegisterTask(QuotaTask* task) {
+ running_quota_tasks_.insert(task);
+}
+
+void QuotaTaskObserver::UnregisterTask(QuotaTask* task) {
+ DCHECK(running_quota_tasks_.find(task) != running_quota_tasks_.end());
+ running_quota_tasks_.erase(task);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/quota_task.h b/webkit/browser/quota/quota_task.h
new file mode 100644
index 0000000..0f245d6
--- /dev/null
+++ b/webkit/browser/quota/quota_task.h
@@ -0,0 +1,79 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_TASK_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_TASK_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class TaskRunner;
+}
+
+namespace quota {
+
+class QuotaTaskObserver;
+
+// A base class for quota tasks.
+// TODO(kinuko): Revise this using base::Callback.
+class QuotaTask {
+ public:
+ void Start();
+
+ protected:
+ explicit QuotaTask(QuotaTaskObserver* observer);
+ virtual ~QuotaTask();
+
+ // The task body.
+ virtual void Run() = 0;
+
+ // Called upon completion, on the original message loop.
+ virtual void Completed() = 0;
+
+ // Called when the task is aborted.
+ virtual void Aborted() {}
+
+ void CallCompleted();
+
+ // Call this to delete itself.
+ void DeleteSoon();
+
+ QuotaTaskObserver* observer() const { return observer_; }
+ base::SingleThreadTaskRunner* original_task_runner() const {
+ return original_task_runner_;
+ }
+
+ private:
+ friend class base::DeleteHelper<QuotaTask>;
+ friend class QuotaTaskObserver;
+
+ void Abort();
+ QuotaTaskObserver* observer_;
+ scoped_refptr<base::SingleThreadTaskRunner> original_task_runner_;
+ bool delete_scheduled_;
+};
+
+class WEBKIT_STORAGE_EXPORT QuotaTaskObserver {
+ protected:
+ friend class QuotaTask;
+
+ QuotaTaskObserver();
+ virtual ~QuotaTaskObserver();
+
+ void RegisterTask(QuotaTask* task);
+ void UnregisterTask(QuotaTask* task);
+
+ typedef std::set<QuotaTask*> TaskSet;
+ TaskSet running_quota_tasks_;
+};
+}
+
+#endif // WEBKIT_BROWSER_QUOTA_QUOTA_TASK_H_
diff --git a/webkit/browser/quota/quota_temporary_storage_evictor.cc b/webkit/browser/quota/quota_temporary_storage_evictor.cc
new file mode 100644
index 0000000..c0842a3
--- /dev/null
+++ b/webkit/browser/quota/quota_temporary_storage_evictor.cc
@@ -0,0 +1,261 @@
+// Copyright 2013 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/browser/quota/quota_temporary_storage_evictor.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/metrics/histogram.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/browser/quota/quota_manager.h"
+
+#define UMA_HISTOGRAM_MBYTES(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ (name), static_cast<int>((sample) / kMBytes), \
+ 1, 10 * 1024 * 1024 /* 10TB */, 100)
+
+#define UMA_HISTOGRAM_MINUTES(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_TIMES( \
+ (name), (sample), \
+ base::TimeDelta::FromMinutes(1), \
+ base::TimeDelta::FromDays(1), 50)
+
+namespace {
+const int64 kMBytes = 1024 * 1024;
+const double kUsageRatioToStartEviction = 0.7;
+const int kThresholdOfErrorsToStopEviction = 5;
+const int kHistogramReportIntervalMinutes = 60;
+}
+
+namespace quota {
+
+const int QuotaTemporaryStorageEvictor::
+ kMinAvailableDiskSpaceToStartEvictionNotSpecified = -1;
+
+QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics()
+ : in_round(false),
+ is_initialized(false),
+ usage_overage_at_round(-1),
+ diskspace_shortage_at_round(-1),
+ usage_on_beginning_of_round(-1),
+ usage_on_end_of_round(-1),
+ num_evicted_origins_in_round(0) {
+}
+
+QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor(
+ QuotaEvictionHandler* quota_eviction_handler,
+ int64 interval_ms)
+ : min_available_disk_space_to_start_eviction_(
+ kMinAvailableDiskSpaceToStartEvictionNotSpecified),
+ quota_eviction_handler_(quota_eviction_handler),
+ interval_ms_(interval_ms),
+ repeated_eviction_(true),
+ weak_factory_(this) {
+ DCHECK(quota_eviction_handler);
+}
+
+QuotaTemporaryStorageEvictor::~QuotaTemporaryStorageEvictor() {
+}
+
+void QuotaTemporaryStorageEvictor::GetStatistics(
+ std::map<std::string, int64>* statistics) {
+ DCHECK(statistics);
+
+ (*statistics)["errors-on-evicting-origin"] =
+ statistics_.num_errors_on_evicting_origin;
+ (*statistics)["errors-on-getting-usage-and-quota"] =
+ statistics_.num_errors_on_getting_usage_and_quota;
+ (*statistics)["evicted-origins"] =
+ statistics_.num_evicted_origins;
+ (*statistics)["eviction-rounds"] =
+ statistics_.num_eviction_rounds;
+ (*statistics)["skipped-eviction-rounds"] =
+ statistics_.num_skipped_eviction_rounds;
+}
+
+void QuotaTemporaryStorageEvictor::ReportPerRoundHistogram() {
+ DCHECK(round_statistics_.in_round);
+ DCHECK(round_statistics_.is_initialized);
+
+ base::Time now = base::Time::Now();
+ UMA_HISTOGRAM_TIMES("Quota.TimeSpentToAEvictionRound",
+ now - round_statistics_.start_time);
+ if (!time_of_end_of_last_round_.is_null())
+ UMA_HISTOGRAM_MINUTES("Quota.TimeDeltaOfEvictionRounds",
+ now - time_of_end_of_last_round_);
+ UMA_HISTOGRAM_MBYTES("Quota.UsageOverageOfTemporaryGlobalStorage",
+ round_statistics_.usage_overage_at_round);
+ UMA_HISTOGRAM_MBYTES("Quota.DiskspaceShortage",
+ round_statistics_.diskspace_shortage_at_round);
+ UMA_HISTOGRAM_MBYTES("Quota.EvictedBytesPerRound",
+ round_statistics_.usage_on_beginning_of_round -
+ round_statistics_.usage_on_end_of_round);
+ UMA_HISTOGRAM_COUNTS("Quota.NumberOfEvictedOriginsPerRound",
+ round_statistics_.num_evicted_origins_in_round);
+}
+
+void QuotaTemporaryStorageEvictor::ReportPerHourHistogram() {
+ Statistics stats_in_hour(statistics_);
+ stats_in_hour.subtract_assign(previous_statistics_);
+ previous_statistics_ = statistics_;
+
+ UMA_HISTOGRAM_COUNTS("Quota.ErrorsOnEvictingOriginPerHour",
+ stats_in_hour.num_errors_on_evicting_origin);
+ UMA_HISTOGRAM_COUNTS("Quota.ErrorsOnGettingUsageAndQuotaPerHour",
+ stats_in_hour.num_errors_on_getting_usage_and_quota);
+ UMA_HISTOGRAM_COUNTS("Quota.EvictedOriginsPerHour",
+ stats_in_hour.num_evicted_origins);
+ UMA_HISTOGRAM_COUNTS("Quota.EvictionRoundsPerHour",
+ stats_in_hour.num_eviction_rounds);
+ UMA_HISTOGRAM_COUNTS("Quota.SkippedEvictionRoundsPerHour",
+ stats_in_hour.num_skipped_eviction_rounds);
+}
+
+void QuotaTemporaryStorageEvictor::OnEvictionRoundStarted() {
+ if (round_statistics_.in_round)
+ return;
+ round_statistics_.in_round = true;
+ round_statistics_.start_time = base::Time::Now();
+ ++statistics_.num_eviction_rounds;
+}
+
+void QuotaTemporaryStorageEvictor::OnEvictionRoundFinished() {
+ // Check if skipped round
+ if (round_statistics_.num_evicted_origins_in_round) {
+ ReportPerRoundHistogram();
+ time_of_end_of_last_nonskipped_round_ = base::Time::Now();
+ } else {
+ ++statistics_.num_skipped_eviction_rounds;
+ }
+ // Reset stats for next round.
+ round_statistics_ = EvictionRoundStatistics();
+}
+
+void QuotaTemporaryStorageEvictor::Start() {
+ DCHECK(CalledOnValidThread());
+ StartEvictionTimerWithDelay(0);
+
+ if (histogram_timer_.IsRunning())
+ return;
+
+ histogram_timer_.Start(
+ FROM_HERE, base::TimeDelta::FromMinutes(kHistogramReportIntervalMinutes),
+ this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram);
+}
+
+void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) {
+ if (eviction_timer_.IsRunning())
+ return;
+ eviction_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms),
+ this, &QuotaTemporaryStorageEvictor::ConsiderEviction);
+}
+
+void QuotaTemporaryStorageEvictor::ConsiderEviction() {
+ OnEvictionRoundStarted();
+
+ // Get usage and disk space, then continue.
+ quota_eviction_handler_->GetUsageAndQuotaForEviction(
+ base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction(
+ QuotaStatusCode status,
+ const UsageAndQuota& qau) {
+ DCHECK(CalledOnValidThread());
+
+ int64 usage = qau.global_limited_usage;
+ DCHECK_GE(usage, 0);
+
+ if (status != kQuotaStatusOk)
+ ++statistics_.num_errors_on_getting_usage_and_quota;
+
+ int64 usage_overage = std::max(
+ static_cast<int64>(0),
+ usage - static_cast<int64>(qau.quota * kUsageRatioToStartEviction));
+
+ // min_available_disk_space_to_start_eviction_ might be < 0 if no value
+ // is explicitly configured yet.
+ int64 diskspace_shortage = std::max(
+ static_cast<int64>(0),
+ min_available_disk_space_to_start_eviction_ - qau.available_disk_space);
+
+ if (!round_statistics_.is_initialized) {
+ round_statistics_.usage_overage_at_round = usage_overage;
+ round_statistics_.diskspace_shortage_at_round = diskspace_shortage;
+ round_statistics_.usage_on_beginning_of_round = usage;
+ round_statistics_.is_initialized = true;
+ }
+ round_statistics_.usage_on_end_of_round = usage;
+
+ int64 amount_to_evict = std::max(usage_overage, diskspace_shortage);
+ if (status == kQuotaStatusOk && amount_to_evict > 0) {
+ // Space is getting tight. Get the least recently used origin and continue.
+ // TODO(michaeln): if the reason for eviction is low physical disk space,
+ // make 'unlimited' origins subject to eviction too.
+ quota_eviction_handler_->GetLRUOrigin(
+ kStorageTypeTemporary,
+ base::Bind(&QuotaTemporaryStorageEvictor::OnGotLRUOrigin,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ if (repeated_eviction_) {
+ // No action required, sleep for a while and check again later.
+ if (statistics_.num_errors_on_getting_usage_and_quota <
+ kThresholdOfErrorsToStopEviction) {
+ StartEvictionTimerWithDelay(interval_ms_);
+ } else {
+ // TODO(dmikurube): Try restarting eviction after a while.
+ LOG(WARNING) << "Stopped eviction of temporary storage due to errors "
+ "in GetUsageAndQuotaForEviction.";
+ }
+ }
+ OnEvictionRoundFinished();
+ }
+
+ // TODO(dmikurube): Add error handling for the case status != kQuotaStatusOk.
+}
+
+void QuotaTemporaryStorageEvictor::OnGotLRUOrigin(const GURL& origin) {
+ DCHECK(CalledOnValidThread());
+
+ if (origin.is_empty()) {
+ if (repeated_eviction_)
+ StartEvictionTimerWithDelay(interval_ms_);
+ OnEvictionRoundFinished();
+ return;
+ }
+
+ quota_eviction_handler_->EvictOriginData(origin, kStorageTypeTemporary,
+ base::Bind(
+ &QuotaTemporaryStorageEvictor::OnEvictionComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuotaTemporaryStorageEvictor::OnEvictionComplete(
+ QuotaStatusCode status) {
+ DCHECK(CalledOnValidThread());
+
+ // Just calling ConsiderEviction() or StartEvictionTimerWithDelay() here is
+ // ok. No need to deal with the case that all of the Delete operations fail
+ // for a certain origin. It doesn't result in trying to evict the same
+ // origin permanently. The evictor skips origins which had deletion errors
+ // a few times.
+
+ if (status == kQuotaStatusOk) {
+ ++statistics_.num_evicted_origins;
+ ++round_statistics_.num_evicted_origins_in_round;
+ // We many need to get rid of more space so reconsider immediately.
+ ConsiderEviction();
+ } else {
+ ++statistics_.num_errors_on_evicting_origin;
+ if (repeated_eviction_) {
+ // Sleep for a while and retry again until we see too many errors.
+ StartEvictionTimerWithDelay(interval_ms_);
+ }
+ OnEvictionRoundFinished();
+ }
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/quota_temporary_storage_evictor.h b/webkit/browser/quota/quota_temporary_storage_evictor.h
new file mode 100644
index 0000000..a4833b4
--- /dev/null
+++ b/webkit/browser/quota/quota_temporary_storage_evictor.h
@@ -0,0 +1,130 @@
+// Copyright 2013 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_BROWSER_QUOTA_QUOTA_TEMPORARY_STORAGE_EVICTOR_H_
+#define WEBKIT_BROWSER_QUOTA_QUOTA_TEMPORARY_STORAGE_EVICTOR_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/timer.h"
+#include "webkit/common/quota/quota_types.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+class GURL;
+
+namespace quota {
+
+class QuotaEvictionHandler;
+struct UsageAndQuota;
+
+class WEBKIT_STORAGE_EXPORT_PRIVATE QuotaTemporaryStorageEvictor
+ : public base::NonThreadSafe {
+ public:
+ struct Statistics {
+ Statistics()
+ : num_errors_on_evicting_origin(0),
+ num_errors_on_getting_usage_and_quota(0),
+ num_evicted_origins(0),
+ num_eviction_rounds(0),
+ num_skipped_eviction_rounds(0) {}
+ int64 num_errors_on_evicting_origin;
+ int64 num_errors_on_getting_usage_and_quota;
+ int64 num_evicted_origins;
+ int64 num_eviction_rounds;
+ int64 num_skipped_eviction_rounds;
+
+ void subtract_assign(const Statistics& rhs) {
+ num_errors_on_evicting_origin -= rhs.num_errors_on_evicting_origin;
+ num_errors_on_getting_usage_and_quota -=
+ rhs.num_errors_on_getting_usage_and_quota;
+ num_evicted_origins -= rhs.num_evicted_origins;
+ num_eviction_rounds -= rhs.num_eviction_rounds;
+ num_skipped_eviction_rounds -= rhs.num_skipped_eviction_rounds;
+ }
+ };
+
+ struct EvictionRoundStatistics {
+ EvictionRoundStatistics();
+
+ bool in_round;
+ bool is_initialized;
+
+ base::Time start_time;
+ int64 usage_overage_at_round;
+ int64 diskspace_shortage_at_round;
+
+ int64 usage_on_beginning_of_round;
+ int64 usage_on_end_of_round;
+ int64 num_evicted_origins_in_round;
+ };
+
+ QuotaTemporaryStorageEvictor(
+ QuotaEvictionHandler* quota_eviction_handler,
+ int64 interval_ms);
+ virtual ~QuotaTemporaryStorageEvictor();
+
+ void GetStatistics(std::map<std::string, int64>* statistics);
+ void ReportPerRoundHistogram();
+ void ReportPerHourHistogram();
+ void Start();
+
+ int64 min_available_disk_space_to_start_eviction() {
+ return min_available_disk_space_to_start_eviction_;
+ }
+ void reset_min_available_disk_space_to_start_eviction() {
+ min_available_disk_space_to_start_eviction_ =
+ kMinAvailableDiskSpaceToStartEvictionNotSpecified;
+ }
+ void set_min_available_disk_space_to_start_eviction(int64 value) {
+ min_available_disk_space_to_start_eviction_ = value;
+ }
+
+ private:
+ friend class QuotaTemporaryStorageEvictorTest;
+
+ void StartEvictionTimerWithDelay(int delay_ms);
+ void ConsiderEviction();
+ void OnGotUsageAndQuotaForEviction(
+ QuotaStatusCode status,
+ const UsageAndQuota& quota_and_usage);
+ void OnGotLRUOrigin(const GURL& origin);
+ void OnEvictionComplete(QuotaStatusCode status);
+
+ void OnEvictionRoundStarted();
+ void OnEvictionRoundFinished();
+
+ // This is only used for tests.
+ void set_repeated_eviction(bool repeated_eviction) {
+ repeated_eviction_ = repeated_eviction;
+ }
+
+ static const int kMinAvailableDiskSpaceToStartEvictionNotSpecified;
+
+ int64 min_available_disk_space_to_start_eviction_;
+
+ // Not owned; quota_eviction_handler owns us.
+ QuotaEvictionHandler* quota_eviction_handler_;
+
+ Statistics statistics_;
+ Statistics previous_statistics_;
+ EvictionRoundStatistics round_statistics_;
+ base::Time time_of_end_of_last_nonskipped_round_;
+ base::Time time_of_end_of_last_round_;
+
+ int64 interval_ms_;
+ bool repeated_eviction_;
+
+ base::OneShotTimer<QuotaTemporaryStorageEvictor> eviction_timer_;
+ base::RepeatingTimer<QuotaTemporaryStorageEvictor> histogram_timer_;
+ base::WeakPtrFactory<QuotaTemporaryStorageEvictor> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictor);
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_QUOTA_TEMPORARY_STORAGE_EVICTOR_H_
diff --git a/webkit/browser/quota/quota_temporary_storage_evictor_unittest.cc b/webkit/browser/quota/quota_temporary_storage_evictor_unittest.cc
new file mode 100644
index 0000000..59acd79
--- /dev/null
+++ b/webkit/browser/quota/quota_temporary_storage_evictor_unittest.cc
@@ -0,0 +1,410 @@
+// Copyright 2013 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 <list>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/mock_storage_client.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_temporary_storage_evictor.h"
+
+namespace quota {
+
+class QuotaTemporaryStorageEvictorTest;
+
+namespace {
+
+class MockQuotaEvictionHandler : public quota::QuotaEvictionHandler {
+ public:
+ explicit MockQuotaEvictionHandler(QuotaTemporaryStorageEvictorTest *test)
+ : quota_(0),
+ available_space_(0),
+ error_on_evict_origin_data_(false),
+ error_on_get_usage_and_quota_(false) {}
+
+ virtual void EvictOriginData(
+ const GURL& origin,
+ StorageType type,
+ const EvictOriginDataCallback& callback) OVERRIDE {
+ if (error_on_evict_origin_data_) {
+ callback.Run(quota::kQuotaErrorInvalidModification);
+ return;
+ }
+ int64 origin_usage = EnsureOriginRemoved(origin);
+ if (origin_usage >= 0)
+ available_space_ += origin_usage;
+ callback.Run(quota::kQuotaStatusOk);
+ }
+
+ virtual void GetUsageAndQuotaForEviction(
+ const UsageAndQuotaCallback& callback) OVERRIDE {
+ if (error_on_get_usage_and_quota_) {
+ callback.Run(quota::kQuotaErrorInvalidAccess, UsageAndQuota());
+ return;
+ }
+ if (!task_for_get_usage_and_quota_.is_null())
+ task_for_get_usage_and_quota_.Run();
+ UsageAndQuota quota_and_usage(-1, GetUsage(), quota_, available_space_);
+ callback.Run(quota::kQuotaStatusOk, quota_and_usage);
+ }
+
+ virtual void GetLRUOrigin(
+ StorageType type,
+ const GetLRUOriginCallback& callback) OVERRIDE {
+ if (origin_order_.empty())
+ callback.Run(GURL());
+ else
+ callback.Run(GURL(origin_order_.front()));
+ }
+
+ int64 GetUsage() const {
+ int64 total_usage = 0;
+ for (std::map<GURL, int64>::const_iterator p = origins_.begin();
+ p != origins_.end();
+ ++p)
+ total_usage += p->second;
+ return total_usage;
+ }
+
+ void set_quota(int64 quota) {
+ quota_ = quota;
+ }
+ void set_available_space(int64 available_space) {
+ available_space_ = available_space;
+ }
+ void set_task_for_get_usage_and_quota(const base::Closure& task) {
+ task_for_get_usage_and_quota_= task;
+ }
+ void set_error_on_evict_origin_data(bool error_on_evict_origin_data) {
+ error_on_evict_origin_data_ = error_on_evict_origin_data;
+ }
+ void set_error_on_get_usage_and_quota(bool error_on_get_usage_and_quota) {
+ error_on_get_usage_and_quota_ = error_on_get_usage_and_quota;
+ }
+
+ // Simulates an access to |origin|. It reorders the internal LRU list.
+ // It internally uses AddOrigin().
+ void AccessOrigin(const GURL& origin) {
+ std::map<GURL, int64>::iterator found = origins_.find(origin);
+ EXPECT_TRUE(origins_.end() != found);
+ AddOrigin(origin, found->second);
+ }
+
+ // Simulates adding or overwriting the |origin| to the internal origin set
+ // with the |usage|. It also adds or moves the |origin| to the end of the
+ // LRU list.
+ void AddOrigin(const GURL& origin, int64 usage) {
+ EnsureOriginRemoved(origin);
+ origin_order_.push_back(origin);
+ origins_[origin] = usage;
+ }
+
+ private:
+ int64 EnsureOriginRemoved(const GURL& origin) {
+ int64 origin_usage;
+ if (origins_.find(origin) == origins_.end())
+ return -1;
+ else
+ origin_usage = origins_[origin];
+
+ origins_.erase(origin);
+ origin_order_.remove(origin);
+ return origin_usage;
+ }
+
+ int64 quota_;
+ int64 available_space_;
+ std::list<GURL> origin_order_;
+ std::map<GURL, int64> origins_;
+ bool error_on_evict_origin_data_;
+ bool error_on_get_usage_and_quota_;
+
+ base::Closure task_for_get_usage_and_quota_;
+};
+
+} // anonymous namespace
+
+class QuotaTemporaryStorageEvictorTest : public testing::Test {
+ public:
+ QuotaTemporaryStorageEvictorTest()
+ : num_get_usage_and_quota_for_eviction_(0),
+ weak_factory_(this) {}
+
+ virtual void SetUp() {
+ quota_eviction_handler_.reset(new MockQuotaEvictionHandler(this));
+
+ // Run multiple evictions in a single RunUntilIdle() when interval_ms == 0
+ temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
+ quota_eviction_handler_.get(), 0));
+ }
+
+ virtual void TearDown() {
+ temporary_storage_evictor_.reset();
+ quota_eviction_handler_.reset();
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ void TaskForRepeatedEvictionTest(
+ const std::pair<GURL, int64>& origin_to_be_added,
+ const GURL& origin_to_be_accessed,
+ int expected_usage_after_first,
+ int expected_usage_after_second) {
+ EXPECT_GE(4, num_get_usage_and_quota_for_eviction_);
+ switch (num_get_usage_and_quota_for_eviction_) {
+ case 2:
+ EXPECT_EQ(expected_usage_after_first,
+ quota_eviction_handler()->GetUsage());
+ if (!origin_to_be_added.first.is_empty())
+ quota_eviction_handler()->AddOrigin(origin_to_be_added.first,
+ origin_to_be_added.second);
+ if (!origin_to_be_accessed.is_empty())
+ quota_eviction_handler()->AccessOrigin(origin_to_be_accessed);
+ break;
+ case 3:
+ EXPECT_EQ(expected_usage_after_second,
+ quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->set_repeated_eviction(false);
+ break;
+ }
+ ++num_get_usage_and_quota_for_eviction_;
+ }
+
+ protected:
+ MockQuotaEvictionHandler* quota_eviction_handler() const {
+ return static_cast<MockQuotaEvictionHandler*>(
+ quota_eviction_handler_.get());
+ }
+
+ QuotaTemporaryStorageEvictor* temporary_storage_evictor() const {
+ return temporary_storage_evictor_.get();
+ }
+
+ const QuotaTemporaryStorageEvictor::Statistics& statistics() const {
+ return temporary_storage_evictor()->statistics_;
+ }
+
+ void set_repeated_eviction(bool repeated_eviction) const {
+ return temporary_storage_evictor_->set_repeated_eviction(repeated_eviction);
+ }
+
+ int num_get_usage_and_quota_for_eviction() const {
+ return num_get_usage_and_quota_for_eviction_;
+ }
+
+ int64 default_min_available_disk_space_to_start_eviction() const {
+ return 1000 * 1000 * 500;
+ }
+
+ void set_min_available_disk_space_to_start_eviction(int64 value) const {
+ temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
+ value);
+ }
+
+ void reset_min_available_disk_space_to_start_eviction() const {
+ temporary_storage_evictor_->
+ reset_min_available_disk_space_to_start_eviction();
+ }
+
+ base::MessageLoop message_loop_;
+ scoped_ptr<MockQuotaEvictionHandler> quota_eviction_handler_;
+ scoped_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_;
+
+ int num_get_usage_and_quota_for_eviction_;
+
+ base::WeakPtrFactory<QuotaTemporaryStorageEvictorTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictorTest);
+};
+
+TEST_F(QuotaTemporaryStorageEvictorTest, SimpleEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 3000);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 200);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 500);
+ quota_eviction_handler()->set_quota(4000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ EXPECT_EQ(3000 + 200 + 500, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(200 + 500, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(1, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, MultipleEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 20);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 2900);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 400);
+ quota_eviction_handler()->set_quota(4000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ EXPECT_EQ(20 + 2900 + 450 + 400, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(450 + 400, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(2, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+ const int64 e_size = 275;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(),
+ std::make_pair(GURL("http://www.e.com"), e_size), GURL(),
+ initial_total_size - d_size,
+ initial_total_size - d_size + e_size - c_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size + e_size - c_size - b_size,
+ quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(5, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(3, statistics().num_evicted_origins);
+ EXPECT_EQ(2, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionSkippedTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(), std::make_pair(GURL(), 0), GURL(),
+ initial_total_size - d_size, initial_total_size - d_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ set_repeated_eviction(true);
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size, quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(4, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(1, statistics().num_evicted_origins);
+ EXPECT_EQ(3, statistics().num_eviction_rounds);
+ EXPECT_EQ(2, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, RepeatedEvictionWithAccessOriginTest) {
+ const int64 a_size = 400;
+ const int64 b_size = 150;
+ const int64 c_size = 120;
+ const int64 d_size = 292;
+ const int64 initial_total_size = a_size + b_size + c_size + d_size;
+ const int64 e_size = 275;
+
+ quota_eviction_handler()->AddOrigin(GURL("http://www.d.com"), d_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
+ quota_eviction_handler()->set_quota(1000);
+ quota_eviction_handler()->set_available_space(1000000000);
+ quota_eviction_handler()->set_task_for_get_usage_and_quota(
+ base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
+ weak_factory_.GetWeakPtr(),
+ std::make_pair(GURL("http://www.e.com"), e_size),
+ GURL("http://www.c.com"),
+ initial_total_size - d_size,
+ initial_total_size - d_size + e_size - b_size));
+ EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(initial_total_size - d_size + e_size - b_size - a_size,
+ quota_eviction_handler()->GetUsage());
+ EXPECT_EQ(5, num_get_usage_and_quota_for_eviction());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(3, statistics().num_evicted_origins);
+ EXPECT_EQ(2, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceNonEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 414);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
+ quota_eviction_handler()->set_quota(10000);
+ quota_eviction_handler()->set_available_space(
+ default_min_available_disk_space_to_start_eviction() - 350);
+ EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
+ reset_min_available_disk_space_to_start_eviction();
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(0, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(1, statistics().num_skipped_eviction_rounds);
+}
+
+TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceEvictionTest) {
+ quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 294);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 120);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 150);
+ quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 300);
+ quota_eviction_handler()->set_quota(10000);
+ quota_eviction_handler()->set_available_space(
+ default_min_available_disk_space_to_start_eviction() - 350);
+ EXPECT_EQ(294 + 120 + 150 + 300, quota_eviction_handler()->GetUsage());
+ set_min_available_disk_space_to_start_eviction(
+ default_min_available_disk_space_to_start_eviction());
+ set_repeated_eviction(false);
+ temporary_storage_evictor()->Start();
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(150 + 300, quota_eviction_handler()->GetUsage());
+
+ EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
+ EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
+ EXPECT_EQ(2, statistics().num_evicted_origins);
+ EXPECT_EQ(1, statistics().num_eviction_rounds);
+ EXPECT_EQ(0, statistics().num_skipped_eviction_rounds);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/special_storage_policy.cc b/webkit/browser/quota/special_storage_policy.cc
new file mode 100644
index 0000000..9aacb04
--- /dev/null
+++ b/webkit/browser/quota/special_storage_policy.cc
@@ -0,0 +1,38 @@
+// Copyright 2013 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/browser/quota/special_storage_policy.h"
+
+namespace quota {
+
+SpecialStoragePolicy::Observer::~Observer() {}
+
+SpecialStoragePolicy::SpecialStoragePolicy() {}
+
+SpecialStoragePolicy::~SpecialStoragePolicy() {}
+
+void SpecialStoragePolicy::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void SpecialStoragePolicy::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void SpecialStoragePolicy::NotifyGranted(const GURL& origin, int change_flags) {
+ scoped_refptr<SpecialStoragePolicy> protect(this);
+ FOR_EACH_OBSERVER(Observer, observers_, OnGranted(origin, change_flags));
+}
+
+void SpecialStoragePolicy::NotifyRevoked(const GURL& origin, int change_flags) {
+ scoped_refptr<SpecialStoragePolicy> protect(this);
+ FOR_EACH_OBSERVER(Observer, observers_, OnRevoked(origin, change_flags));
+}
+
+void SpecialStoragePolicy::NotifyCleared() {
+ scoped_refptr<SpecialStoragePolicy> protect(this);
+ FOR_EACH_OBSERVER(Observer, observers_, OnCleared());
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/special_storage_policy.h b/webkit/browser/quota/special_storage_policy.h
new file mode 100644
index 0000000..a16fed2
--- /dev/null
+++ b/webkit/browser/quota/special_storage_policy.h
@@ -0,0 +1,83 @@
+// Copyright 2013 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_BROWSER_QUOTA_SPECIAL_STORAGE_POLICY_H_
+#define WEBKIT_BROWSER_QUOTA_SPECIAL_STORAGE_POLICY_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "webkit/storage/webkit_storage_export.h"
+
+class GURL;
+
+namespace quota {
+
+// Special rights are granted to 'extensions' and 'applications'. The
+// storage subsystems query this interface to determine which origins
+// have these rights. Chrome provides an impl that is cognizant of what
+// is currently installed in the extensions system.
+// The IsSomething() methods must be thread-safe, however Observers should
+// only be notified, added, and removed on the IO thead.
+class WEBKIT_STORAGE_EXPORT SpecialStoragePolicy
+ : public base::RefCountedThreadSafe<SpecialStoragePolicy> {
+ public:
+ typedef int StoragePolicy;
+ enum ChangeFlags {
+ STORAGE_PROTECTED = 1 << 0,
+ STORAGE_UNLIMITED = 1 << 1,
+ };
+
+ class WEBKIT_STORAGE_EXPORT Observer {
+ public:
+ virtual void OnGranted(const GURL& origin, int change_flags) = 0;
+ virtual void OnRevoked(const GURL& origin, int change_flags) = 0;
+ virtual void OnCleared() = 0;
+
+ protected:
+ virtual ~Observer();
+ };
+
+ SpecialStoragePolicy();
+
+ // Protected storage is not subject to removal by the browsing data remover.
+ virtual bool IsStorageProtected(const GURL& origin) = 0;
+
+ // Unlimited storage is not subject to 'quotas'.
+ virtual bool IsStorageUnlimited(const GURL& origin) = 0;
+
+ // Some origins (e.g. installed apps) have access to the size of the remaining
+ // disk capacity.
+ virtual bool CanQueryDiskSize(const GURL& origin) = 0;
+
+ // Checks if extension identified with |extension_id| is registered as
+ // file handler.
+ virtual bool IsFileHandler(const std::string& extension_id) = 0;
+
+ // Some origins are only allowed to store session-only data which is deleted
+ // when the session ends.
+ virtual bool IsStorageSessionOnly(const GURL& origin) = 0;
+
+ // Returns true if some origins are only allowed session-only storage.
+ virtual bool HasSessionOnlyOrigins() = 0;
+
+ // Adds/removes an observer, the policy does not take
+ // ownership of the observer. Should only be called on the IO thread.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ friend class base::RefCountedThreadSafe<SpecialStoragePolicy>;
+ virtual ~SpecialStoragePolicy();
+ void NotifyGranted(const GURL& origin, int change_flags);
+ void NotifyRevoked(const GURL& origin, int change_flags);
+ void NotifyCleared();
+
+ ObserverList<Observer> observers_;
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_SPECIAL_STORAGE_POLICY_H_
diff --git a/webkit/browser/quota/usage_tracker.cc b/webkit/browser/quota/usage_tracker.cc
new file mode 100644
index 0000000..6026d38
--- /dev/null
+++ b/webkit/browser/quota/usage_tracker.cc
@@ -0,0 +1,597 @@
+// Copyright 2013 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/browser/quota/usage_tracker.h"
+
+#include <algorithm>
+#include <deque>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "net/base/net_util.h"
+
+namespace quota {
+
+namespace {
+
+typedef ClientUsageTracker::OriginUsageAccumulator OriginUsageAccumulator;
+typedef ClientUsageTracker::OriginSetByHost OriginSetByHost;
+
+void DidGetOriginUsage(const OriginUsageAccumulator& accumulator,
+ const GURL& origin,
+ int64 usage) {
+ accumulator.Run(origin, usage);
+}
+
+void DidGetHostUsage(const UsageCallback& callback,
+ int64 cached_usage,
+ int64 non_cached_usage) {
+ DCHECK_GE(cached_usage, 0);
+ DCHECK_GE(non_cached_usage, 0);
+ callback.Run(cached_usage + non_cached_usage);
+}
+
+void NoopHostUsageCallback(int64 usage) {}
+
+bool EraseOriginFromOriginSet(OriginSetByHost* origins_by_host,
+ const std::string& host,
+ const GURL& origin) {
+ OriginSetByHost::iterator found = origins_by_host->find(host);
+ if (found == origins_by_host->end())
+ return false;
+
+ if (!found->second.erase(origin))
+ return false;
+
+ if (found->second.empty())
+ origins_by_host->erase(host);
+ return true;
+}
+
+bool OriginSetContainsOrigin(const OriginSetByHost& origins,
+ const std::string& host,
+ const GURL& origin) {
+ OriginSetByHost::const_iterator itr = origins.find(host);
+ return itr != origins.end() && ContainsKey(itr->second, origin);
+}
+
+void DidGetGlobalUsageForLimitedGlobalUsage(const UsageCallback& callback,
+ int64 total_global_usage,
+ int64 global_unlimited_usage) {
+ callback.Run(total_global_usage - global_unlimited_usage);
+}
+
+} // namespace
+
+// UsageTracker ----------------------------------------------------------
+
+UsageTracker::UsageTracker(const QuotaClientList& clients,
+ StorageType type,
+ SpecialStoragePolicy* special_storage_policy)
+ : type_(type),
+ weak_factory_(this) {
+ for (QuotaClientList::const_iterator iter = clients.begin();
+ iter != clients.end();
+ ++iter) {
+ client_tracker_map_[(*iter)->id()] =
+ new ClientUsageTracker(this, *iter, type, special_storage_policy);
+ }
+}
+
+UsageTracker::~UsageTracker() {
+ STLDeleteValues(&client_tracker_map_);
+}
+
+ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) {
+ ClientTrackerMap::iterator found = client_tracker_map_.find(client_id);
+ if (found != client_tracker_map_.end())
+ return found->second;
+ return NULL;
+}
+
+void UsageTracker::GetGlobalLimitedUsage(const UsageCallback& callback) {
+ GetGlobalUsage(base::Bind(&DidGetGlobalUsageForLimitedGlobalUsage, callback));
+}
+
+void UsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
+ if (!global_usage_callbacks_.Add(callback))
+ return;
+
+ AccumulateInfo* info = new AccumulateInfo;
+ // Calling GetGlobalUsage(accumulator) may synchronously
+ // return if the usage is cached, which may in turn dispatch
+ // the completion callback before we finish looping over
+ // all clients (because info->pending_clients may reach 0
+ // during the loop).
+ // To avoid this, we add one more pending client as a sentinel
+ // and fire the sentinel callback at the end.
+ info->pending_clients = client_tracker_map_.size() + 1;
+ GlobalUsageCallback accumulator = base::Bind(
+ &UsageTracker::AccumulateClientGlobalUsage, weak_factory_.GetWeakPtr(),
+ base::Owned(info));
+
+ for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
+ iter != client_tracker_map_.end();
+ ++iter)
+ iter->second->GetGlobalUsage(accumulator);
+
+ // Fire the sentinel as we've now called GetGlobalUsage for all clients.
+ accumulator.Run(0, 0);
+}
+
+void UsageTracker::GetHostUsage(const std::string& host,
+ const UsageCallback& callback) {
+ if (!host_usage_callbacks_.Add(host, callback))
+ return;
+
+ AccumulateInfo* info = new AccumulateInfo;
+ // Calling GetHostUsage(accumulator) may synchronously
+ // return if the usage is cached, which may in turn dispatch
+ // the completion callback before we finish looping over
+ // all clients (because info->pending_clients may reach 0
+ // during the loop).
+ // To avoid this, we add one more pending client as a sentinel
+ // and fire the sentinel callback at the end.
+ info->pending_clients = client_tracker_map_.size() + 1;
+ UsageCallback accumulator = base::Bind(
+ &UsageTracker::AccumulateClientHostUsage, weak_factory_.GetWeakPtr(),
+ base::Owned(info), host);
+
+ for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
+ iter != client_tracker_map_.end();
+ ++iter)
+ iter->second->GetHostUsage(host, accumulator);
+
+ // Fire the sentinel as we've now called GetHostUsage for all clients.
+ accumulator.Run(0);
+}
+
+void UsageTracker::UpdateUsageCache(
+ QuotaClient::ID client_id, const GURL& origin, int64 delta) {
+ ClientUsageTracker* client_tracker = GetClientTracker(client_id);
+ DCHECK(client_tracker);
+ client_tracker->UpdateUsageCache(origin, delta);
+}
+
+void UsageTracker::GetCachedHostsUsage(
+ std::map<std::string, int64>* host_usage) const {
+ DCHECK(host_usage);
+ host_usage->clear();
+ for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
+ iter != client_tracker_map_.end(); ++iter) {
+ iter->second->GetCachedHostsUsage(host_usage);
+ }
+}
+
+void UsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
+ DCHECK(origins);
+ origins->clear();
+ for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
+ iter != client_tracker_map_.end(); ++iter) {
+ iter->second->GetCachedOrigins(origins);
+ }
+}
+
+void UsageTracker::SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ bool enabled) {
+ ClientUsageTracker* client_tracker = GetClientTracker(client_id);
+ DCHECK(client_tracker);
+
+ client_tracker->SetUsageCacheEnabled(origin, enabled);
+}
+
+void UsageTracker::AccumulateClientGlobalUsage(AccumulateInfo* info,
+ int64 usage,
+ int64 unlimited_usage) {
+ info->usage += usage;
+ info->unlimited_usage += unlimited_usage;
+
+ if (--info->pending_clients)
+ return;
+
+ // Defend against confusing inputs from clients.
+ if (info->usage < 0)
+ info->usage = 0;
+
+ // TODO(michaeln): The unlimited number is not trustworthy, it
+ // can get out of whack when apps are installed or uninstalled.
+ if (info->unlimited_usage > info->usage)
+ info->unlimited_usage = info->usage;
+ else if (info->unlimited_usage < 0)
+ info->unlimited_usage = 0;
+
+ // All the clients have returned their usage data. Dispatch the
+ // pending callbacks.
+ global_usage_callbacks_.Run(MakeTuple(info->usage, info->unlimited_usage));
+}
+
+void UsageTracker::AccumulateClientHostUsage(AccumulateInfo* info,
+ const std::string& host,
+ int64 usage) {
+ info->usage += usage;
+ if (--info->pending_clients)
+ return;
+
+ // Defend against confusing inputs from clients.
+ if (info->usage < 0)
+ info->usage = 0;
+
+ // All the clients have returned their usage data. Dispatch the
+ // pending callbacks.
+ host_usage_callbacks_.Run(host, MakeTuple(info->usage));
+}
+
+// ClientUsageTracker ----------------------------------------------------
+
+ClientUsageTracker::ClientUsageTracker(
+ UsageTracker* tracker, QuotaClient* client, StorageType type,
+ SpecialStoragePolicy* special_storage_policy)
+ : tracker_(tracker),
+ client_(client),
+ type_(type),
+ global_limited_usage_(0),
+ global_unlimited_usage_(0),
+ global_usage_retrieved_(false),
+ special_storage_policy_(special_storage_policy) {
+ DCHECK(tracker_);
+ DCHECK(client_);
+ if (special_storage_policy_)
+ special_storage_policy_->AddObserver(this);
+}
+
+ClientUsageTracker::~ClientUsageTracker() {
+ if (special_storage_policy_)
+ special_storage_policy_->RemoveObserver(this);
+}
+
+void ClientUsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
+ if (global_usage_retrieved_ &&
+ non_cached_limited_origins_by_host_.empty() &&
+ non_cached_unlimited_origins_by_host_.empty()) {
+ callback.Run(global_limited_usage_ + global_unlimited_usage_,
+ global_unlimited_usage_);
+ return;
+ }
+
+ client_->GetOriginsForType(type_, base::Bind(
+ &ClientUsageTracker::DidGetOriginsForGlobalUsage, AsWeakPtr(),
+ callback));
+}
+
+void ClientUsageTracker::GetHostUsage(
+ const std::string& host, const UsageCallback& callback) {
+ if (ContainsKey(cached_hosts_, host) &&
+ !ContainsKey(non_cached_limited_origins_by_host_, host) &&
+ !ContainsKey(non_cached_unlimited_origins_by_host_, host)) {
+ // TODO(kinuko): Drop host_usage_map_ cache periodically.
+ callback.Run(GetCachedHostUsage(host));
+ return;
+ }
+
+ if (!host_usage_accumulators_.Add(host, base::Bind(
+ &DidGetHostUsage, callback)))
+ return;
+ client_->GetOriginsForHost(type_, host, base::Bind(
+ &ClientUsageTracker::DidGetOriginsForHostUsage, AsWeakPtr(), host));
+}
+
+void ClientUsageTracker::UpdateUsageCache(
+ const GURL& origin, int64 delta) {
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ if (cached_hosts_.find(host) != cached_hosts_.end()) {
+ if (!IsUsageCacheEnabledForOrigin(origin))
+ return;
+
+ cached_usage_by_host_[host][origin] += delta;
+ if (IsStorageUnlimited(origin))
+ global_unlimited_usage_ += delta;
+ else
+ global_limited_usage_ += delta;
+ DCHECK_GE(cached_usage_by_host_[host][origin], 0);
+ DCHECK_GE(global_limited_usage_, 0);
+ return;
+ }
+
+ // We don't know about this host yet, so populate our cache for it.
+ GetHostUsage(host, base::Bind(&NoopHostUsageCallback));
+}
+
+void ClientUsageTracker::GetCachedHostsUsage(
+ std::map<std::string, int64>* host_usage) const {
+ DCHECK(host_usage);
+ for (HostUsageMap::const_iterator host_iter = cached_usage_by_host_.begin();
+ host_iter != cached_usage_by_host_.end(); host_iter++) {
+ const std::string& host = host_iter->first;
+ (*host_usage)[host] += GetCachedHostUsage(host);
+ }
+}
+
+void ClientUsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
+ DCHECK(origins);
+ for (HostUsageMap::const_iterator host_iter = cached_usage_by_host_.begin();
+ host_iter != cached_usage_by_host_.end(); host_iter++) {
+ const UsageMap& origin_map = host_iter->second;
+ for (UsageMap::const_iterator origin_iter = origin_map.begin();
+ origin_iter != origin_map.end(); origin_iter++) {
+ origins->insert(origin_iter->first);
+ }
+ }
+}
+
+void ClientUsageTracker::SetUsageCacheEnabled(const GURL& origin,
+ bool enabled) {
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ if (!enabled) {
+ // Erase |origin| from cache and subtract its usage.
+ HostUsageMap::iterator found_host = cached_usage_by_host_.find(host);
+ if (found_host != cached_usage_by_host_.end()) {
+ UsageMap& cached_usage_for_host = found_host->second;
+
+ UsageMap::iterator found = cached_usage_for_host.find(origin);
+ if (found != cached_usage_for_host.end()) {
+ int64 usage = found->second;
+ UpdateUsageCache(origin, -usage);
+ cached_usage_for_host.erase(found);
+ if (cached_usage_for_host.empty()) {
+ cached_usage_by_host_.erase(found_host);
+ cached_hosts_.erase(host);
+ }
+ }
+ }
+
+ if (IsStorageUnlimited(origin))
+ non_cached_unlimited_origins_by_host_[host].insert(origin);
+ else
+ non_cached_limited_origins_by_host_[host].insert(origin);
+ } else {
+ // Erase |origin| from |non_cached_origins_| and invalidate the usage cache
+ // for the host.
+ if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_,
+ host, origin) ||
+ EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_,
+ host, origin)) {
+ cached_hosts_.erase(host);
+ global_usage_retrieved_ = false;
+ }
+ }
+}
+
+void ClientUsageTracker::DidGetOriginsForGlobalUsage(
+ const GlobalUsageCallback& callback,
+ const std::set<GURL>& origins) {
+ OriginSetByHost origins_by_host;
+ for (std::set<GURL>::const_iterator itr = origins.begin();
+ itr != origins.end(); ++itr)
+ origins_by_host[net::GetHostOrSpecFromURL(*itr)].insert(*itr);
+
+ AccumulateInfo* info = new AccumulateInfo;
+ // Getting host usage may synchronously return the result if the usage is
+ // cached, which may in turn dispatch the completion callback before we finish
+ // looping over all hosts (because info->pending_jobs may reach 0 during the
+ // loop). To avoid this, we add one more pending host as a sentinel and
+ // fire the sentinel callback at the end.
+ info->pending_jobs = origins_by_host.size() + 1;
+ HostUsageAccumulator accumulator =
+ base::Bind(&ClientUsageTracker::AccumulateHostUsage, AsWeakPtr(),
+ base::Owned(info), callback);
+
+ for (OriginSetByHost::iterator itr = origins_by_host.begin();
+ itr != origins_by_host.end(); ++itr) {
+ if (host_usage_accumulators_.Add(itr->first, accumulator))
+ GetUsageForOrigins(itr->first, itr->second);
+ }
+
+ // Fire the sentinel as we've now called GetUsageForOrigins for all clients.
+ accumulator.Run(0, 0);
+}
+
+void ClientUsageTracker::AccumulateHostUsage(
+ AccumulateInfo* info,
+ const GlobalUsageCallback& callback,
+ int64 cached_usage,
+ int64 non_cached_usage) {
+ info->cached_usage += cached_usage;
+ info->non_cached_usage += non_cached_usage;
+ if (--info->pending_jobs)
+ return;
+
+ int64 total_usage = info->cached_usage + info->non_cached_usage;
+ int64 unlimited_usage = global_unlimited_usage_ + info->non_cached_usage;
+
+ DCHECK_GE(total_usage, 0);
+ DCHECK_GE(unlimited_usage, 0);
+ if (unlimited_usage > total_usage)
+ unlimited_usage = total_usage;
+
+ global_usage_retrieved_ = true;
+ callback.Run(total_usage, unlimited_usage);
+}
+
+void ClientUsageTracker::DidGetOriginsForHostUsage(
+ const std::string& host,
+ const std::set<GURL>& origins) {
+ GetUsageForOrigins(host, origins);
+}
+
+void ClientUsageTracker::GetUsageForOrigins(
+ const std::string& host,
+ const std::set<GURL>& origins) {
+ AccumulateInfo* info = new AccumulateInfo;
+ // Getting origin usage may synchronously return the result if the usage is
+ // cached, which may in turn dispatch the completion callback before we finish
+ // looping over all origins (because info->pending_jobs may reach 0 during the
+ // loop). To avoid this, we add one more pending origin as a sentinel and
+ // fire the sentinel callback at the end.
+ info->pending_jobs = origins.size() + 1;
+ OriginUsageAccumulator accumulator =
+ base::Bind(&ClientUsageTracker::AccumulateOriginUsage, AsWeakPtr(),
+ base::Owned(info), host);
+
+ for (std::set<GURL>::const_iterator itr = origins.begin();
+ itr != origins.end(); ++itr) {
+ DCHECK_EQ(host, net::GetHostOrSpecFromURL(*itr));
+
+ int64 origin_usage = 0;
+ if (GetCachedOriginUsage(*itr, &origin_usage)) {
+ accumulator.Run(*itr, origin_usage);
+ } else {
+ client_->GetOriginUsage(*itr, type_, base::Bind(
+ &DidGetOriginUsage, accumulator, *itr));
+ }
+ }
+
+ // Fire the sentinel as we've now called GetOriginUsage for all clients.
+ accumulator.Run(GURL(), 0);
+}
+
+void ClientUsageTracker::AccumulateOriginUsage(AccumulateInfo* info,
+ const std::string& host,
+ const GURL& origin,
+ int64 usage) {
+ if (!origin.is_empty()) {
+ if (usage < 0)
+ usage = 0;
+
+ if (IsUsageCacheEnabledForOrigin(origin)) {
+ info->cached_usage += usage;
+ AddCachedOrigin(origin, usage);
+ } else {
+ info->non_cached_usage += usage;
+ }
+ }
+ if (--info->pending_jobs)
+ return;
+
+ AddCachedHost(host);
+ host_usage_accumulators_.Run(
+ host, MakeTuple(info->cached_usage, info->non_cached_usage));
+}
+
+void ClientUsageTracker::AddCachedOrigin(
+ const GURL& origin, int64 new_usage) {
+ if (!IsUsageCacheEnabledForOrigin(origin))
+ return;
+
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ int64* usage = &cached_usage_by_host_[host][origin];
+ int64 delta = new_usage - *usage;
+ *usage = new_usage;
+ if (delta) {
+ if (IsStorageUnlimited(origin))
+ global_unlimited_usage_ += delta;
+ else
+ global_limited_usage_ += delta;
+ }
+ DCHECK_GE(*usage, 0);
+ DCHECK_GE(global_limited_usage_, 0);
+}
+
+void ClientUsageTracker::AddCachedHost(const std::string& host) {
+ cached_hosts_.insert(host);
+}
+
+int64 ClientUsageTracker::GetCachedHostUsage(const std::string& host) const {
+ HostUsageMap::const_iterator found = cached_usage_by_host_.find(host);
+ if (found == cached_usage_by_host_.end())
+ return 0;
+
+ int64 usage = 0;
+ const UsageMap& map = found->second;
+ for (UsageMap::const_iterator iter = map.begin();
+ iter != map.end(); ++iter) {
+ usage += iter->second;
+ }
+ return usage;
+}
+
+bool ClientUsageTracker::GetCachedOriginUsage(
+ const GURL& origin,
+ int64* usage) const {
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ HostUsageMap::const_iterator found_host = cached_usage_by_host_.find(host);
+ if (found_host == cached_usage_by_host_.end())
+ return false;
+
+ UsageMap::const_iterator found = found_host->second.find(origin);
+ if (found == found_host->second.end())
+ return false;
+
+ DCHECK(IsUsageCacheEnabledForOrigin(origin));
+ *usage = found->second;
+ return true;
+}
+
+bool ClientUsageTracker::IsUsageCacheEnabledForOrigin(
+ const GURL& origin) const {
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ return !OriginSetContainsOrigin(non_cached_limited_origins_by_host_,
+ host, origin) &&
+ !OriginSetContainsOrigin(non_cached_unlimited_origins_by_host_,
+ host, origin);
+}
+
+void ClientUsageTracker::OnGranted(const GURL& origin,
+ int change_flags) {
+ DCHECK(CalledOnValidThread());
+ if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
+ int64 usage = 0;
+ if (GetCachedOriginUsage(origin, &usage)) {
+ global_unlimited_usage_ += usage;
+ global_limited_usage_ -= usage;
+ }
+
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_,
+ host, origin))
+ non_cached_unlimited_origins_by_host_[host].insert(origin);
+ }
+}
+
+void ClientUsageTracker::OnRevoked(const GURL& origin,
+ int change_flags) {
+ DCHECK(CalledOnValidThread());
+ if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
+ int64 usage = 0;
+ if (GetCachedOriginUsage(origin, &usage)) {
+ global_unlimited_usage_ -= usage;
+ global_limited_usage_ += usage;
+ }
+
+ std::string host = net::GetHostOrSpecFromURL(origin);
+ if (EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_,
+ host, origin))
+ non_cached_limited_origins_by_host_[host].insert(origin);
+ }
+}
+
+void ClientUsageTracker::OnCleared() {
+ DCHECK(CalledOnValidThread());
+ global_limited_usage_ += global_unlimited_usage_;
+ global_unlimited_usage_ = 0;
+
+ for (OriginSetByHost::const_iterator host_itr =
+ non_cached_unlimited_origins_by_host_.begin();
+ host_itr != non_cached_unlimited_origins_by_host_.end();
+ ++host_itr) {
+ for (std::set<GURL>::const_iterator origin_itr = host_itr->second.begin();
+ origin_itr != host_itr->second.end();
+ ++origin_itr)
+ non_cached_limited_origins_by_host_[host_itr->first].insert(*origin_itr);
+ }
+ non_cached_unlimited_origins_by_host_.clear();
+}
+
+bool ClientUsageTracker::IsStorageUnlimited(const GURL& origin) const {
+ if (type_ == kStorageTypeSyncable)
+ return false;
+ return special_storage_policy_.get() &&
+ special_storage_policy_->IsStorageUnlimited(origin);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/usage_tracker.h b/webkit/browser/quota/usage_tracker.h
new file mode 100644
index 0000000..973ceea
--- /dev/null
+++ b/webkit/browser/quota/usage_tracker.h
@@ -0,0 +1,185 @@
+// Copyright 2013 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_BROWSER_QUOTA_USAGE_TRACKER_H_
+#define WEBKIT_BROWSER_QUOTA_USAGE_TRACKER_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/browser/quota/quota_callbacks.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_task.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+#include "webkit/common/quota/quota_types.h"
+
+namespace quota {
+
+class ClientUsageTracker;
+
+// A helper class that gathers and tracks the amount of data stored in
+// all quota clients.
+// An instance of this class is created per storage type.
+class WEBKIT_STORAGE_EXPORT UsageTracker : public QuotaTaskObserver {
+ public:
+ UsageTracker(const QuotaClientList& clients, StorageType type,
+ SpecialStoragePolicy* special_storage_policy);
+ virtual ~UsageTracker();
+
+ StorageType type() const { return type_; }
+ ClientUsageTracker* GetClientTracker(QuotaClient::ID client_id);
+
+ void GetGlobalLimitedUsage(const UsageCallback& callback);
+ void GetGlobalUsage(const GlobalUsageCallback& callback);
+ void GetHostUsage(const std::string& host, const UsageCallback& callback);
+ void UpdateUsageCache(QuotaClient::ID client_id,
+ const GURL& origin,
+ int64 delta);
+ void GetCachedHostsUsage(std::map<std::string, int64>* host_usage) const;
+ void GetCachedOrigins(std::set<GURL>* origins) const;
+ bool IsWorking() const {
+ return global_usage_callbacks_.HasCallbacks() ||
+ host_usage_callbacks_.HasAnyCallbacks();
+ }
+
+ void SetUsageCacheEnabled(QuotaClient::ID client_id,
+ const GURL& origin,
+ bool enabled);
+
+ private:
+ struct AccumulateInfo {
+ AccumulateInfo() : pending_clients(0), usage(0), unlimited_usage(0) {}
+ int pending_clients;
+ int64 usage;
+ int64 unlimited_usage;
+ };
+
+ typedef std::map<QuotaClient::ID, ClientUsageTracker*> ClientTrackerMap;
+
+ friend class ClientUsageTracker;
+ void AccumulateClientGlobalUsage(AccumulateInfo* info,
+ int64 usage,
+ int64 unlimited_usage);
+ void AccumulateClientHostUsage(AccumulateInfo* info,
+ const std::string& host,
+ int64 usage);
+
+ const StorageType type_;
+ ClientTrackerMap client_tracker_map_;
+
+ GlobalUsageCallbackQueue global_usage_callbacks_;
+ HostUsageCallbackMap host_usage_callbacks_;
+
+ base::WeakPtrFactory<UsageTracker> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(UsageTracker);
+};
+
+// This class holds per-client usage tracking information and caches per-host
+// usage data. An instance of this class is created per client.
+class ClientUsageTracker : public SpecialStoragePolicy::Observer,
+ public base::NonThreadSafe,
+ public base::SupportsWeakPtr<ClientUsageTracker> {
+ public:
+ typedef base::Callback<void(int64 cached_usage,
+ int64 non_cached_usage)> HostUsageAccumulator;
+ typedef base::Callback<void(const GURL& origin,
+ int64 usage)> OriginUsageAccumulator;
+ typedef std::map<std::string, std::set<GURL> > OriginSetByHost;
+
+ ClientUsageTracker(UsageTracker* tracker,
+ QuotaClient* client,
+ StorageType type,
+ SpecialStoragePolicy* special_storage_policy);
+ virtual ~ClientUsageTracker();
+
+ void GetGlobalUsage(const GlobalUsageCallback& callback);
+ void GetHostUsage(const std::string& host, const UsageCallback& callback);
+ void UpdateUsageCache(const GURL& origin, int64 delta);
+ void GetCachedHostsUsage(std::map<std::string, int64>* host_usage) const;
+ void GetCachedOrigins(std::set<GURL>* origins) const;
+ int64 GetCachedOriginsUsage(const std::set<GURL>& origins,
+ std::vector<GURL>* origins_not_in_cache);
+ bool IsUsageCacheEnabledForOrigin(const GURL& origin) const;
+ void SetUsageCacheEnabled(const GURL& origin, bool enabled);
+
+ private:
+ typedef CallbackQueueMap<HostUsageAccumulator, std::string,
+ Tuple2<int64, int64> > HostUsageAccumulatorMap;
+
+ typedef std::set<std::string> HostSet;
+ typedef std::map<GURL, int64> UsageMap;
+ typedef std::map<std::string, UsageMap> HostUsageMap;
+
+ struct AccumulateInfo {
+ int pending_jobs;
+ int64 cached_usage;
+ int64 non_cached_usage;
+
+ AccumulateInfo() : pending_jobs(0), cached_usage(0), non_cached_usage(0) {}
+ };
+
+ void DidGetOriginsForGlobalUsage(const GlobalUsageCallback& callback,
+ const std::set<GURL>& origins);
+ void AccumulateHostUsage(AccumulateInfo* info,
+ const GlobalUsageCallback& callback,
+ int64 cached_usage,
+ int64 non_cached_usage);
+
+ void DidGetOriginsForHostUsage(const std::string& host,
+ const std::set<GURL>& origins);
+
+ void GetUsageForOrigins(const std::string& host,
+ const std::set<GURL>& origins);
+ void AccumulateOriginUsage(AccumulateInfo* info,
+ const std::string& host,
+ const GURL& origin,
+ int64 usage);
+
+ // Methods used by our GatherUsage tasks, as a task makes progress
+ // origins and hosts are added incrementally to the cache.
+ void AddCachedOrigin(const GURL& origin, int64 usage);
+ void AddCachedHost(const std::string& host);
+
+ int64 GetCachedHostUsage(const std::string& host) const;
+ int64 GetCachedGlobalUnlimitedUsage();
+ bool GetCachedOriginUsage(const GURL& origin, int64* usage) const;
+
+ // SpecialStoragePolicy::Observer overrides
+ virtual void OnGranted(const GURL& origin, int change_flags) OVERRIDE;
+ virtual void OnRevoked(const GURL& origin, int change_flags) OVERRIDE;
+ virtual void OnCleared() OVERRIDE;
+
+ bool IsStorageUnlimited(const GURL& origin) const;
+
+ UsageTracker* tracker_;
+ QuotaClient* client_;
+ const StorageType type_;
+
+ int64 global_limited_usage_;
+ int64 global_unlimited_usage_;
+ bool global_usage_retrieved_;
+ HostSet cached_hosts_;
+ HostUsageMap cached_usage_by_host_;
+
+ OriginSetByHost non_cached_limited_origins_by_host_;
+ OriginSetByHost non_cached_unlimited_origins_by_host_;
+
+ GlobalUsageCallbackQueue global_usage_callback_;
+ HostUsageAccumulatorMap host_usage_accumulators_;
+
+ scoped_refptr<SpecialStoragePolicy> special_storage_policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientUsageTracker);
+};
+
+} // namespace quota
+
+#endif // WEBKIT_BROWSER_QUOTA_USAGE_TRACKER_H_
diff --git a/webkit/browser/quota/usage_tracker_unittest.cc b/webkit/browser/quota/usage_tracker_unittest.cc
new file mode 100644
index 0000000..b897cfc
--- /dev/null
+++ b/webkit/browser/quota/usage_tracker_unittest.cc
@@ -0,0 +1,277 @@
+// Copyright 2013 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/bind.h"
+#include "base/message_loop.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/quota/mock_special_storage_policy.h"
+#include "webkit/browser/quota/usage_tracker.h"
+
+namespace quota {
+
+namespace {
+
+void DidGetGlobalUsage(bool* done,
+ int64* usage_out,
+ int64* unlimited_usage_out,
+ int64 usage,
+ int64 unlimited_usage) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ *usage_out = usage;
+ *unlimited_usage_out = unlimited_usage;
+}
+
+void DidGetUsage(bool* done,
+ int64* usage_out,
+ int64 usage) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ *usage_out = usage;
+}
+
+} // namespace
+
+class MockQuotaClient : public QuotaClient {
+ public:
+ MockQuotaClient() {}
+ virtual ~MockQuotaClient() {}
+
+ virtual ID id() const OVERRIDE {
+ return kFileSystem;
+ }
+
+ virtual void OnQuotaManagerDestroyed() OVERRIDE {}
+
+ virtual void GetOriginUsage(const GURL& origin,
+ StorageType type,
+ const GetUsageCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ int64 usage = GetUsage(origin);
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, usage));
+ }
+
+ virtual void GetOriginsForType(StorageType type,
+ const GetOriginsCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ std::set<GURL> origins;
+ for (UsageMap::const_iterator itr = usage_map_.begin();
+ itr != usage_map_.end(); ++itr) {
+ origins.insert(itr->first);
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, origins));
+ }
+
+ virtual void GetOriginsForHost(StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ std::set<GURL> origins;
+ for (UsageMap::const_iterator itr = usage_map_.begin();
+ itr != usage_map_.end(); ++itr) {
+ if (net::GetHostOrSpecFromURL(itr->first) == host)
+ origins.insert(itr->first);
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, origins));
+ }
+
+ virtual void DeleteOriginData(const GURL& origin,
+ StorageType type,
+ const DeletionCallback& callback) OVERRIDE {
+ EXPECT_EQ(kStorageTypeTemporary, type);
+ usage_map_.erase(origin);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, kQuotaStatusOk));
+ }
+
+ int64 GetUsage(const GURL& origin) {
+ UsageMap::const_iterator found = usage_map_.find(origin);
+ if (found == usage_map_.end())
+ return 0;
+ return found->second;
+ }
+
+ void SetUsage(const GURL& origin, int64 usage) {
+ usage_map_[origin] = usage;
+ }
+
+ int64 UpdateUsage(const GURL& origin, int64 delta) {
+ return usage_map_[origin] += delta;
+ }
+
+ private:
+ typedef std::map<GURL, int64> UsageMap;
+
+ UsageMap usage_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuotaClient);
+};
+
+class UsageTrackerTest : public testing::Test {
+ public:
+ UsageTrackerTest()
+ : storage_policy_(new MockSpecialStoragePolicy()),
+ usage_tracker_(GetUsageTrackerList(), kStorageTypeTemporary,
+ storage_policy_.get()) {
+ }
+
+ virtual ~UsageTrackerTest() {}
+
+ UsageTracker* usage_tracker() {
+ return &usage_tracker_;
+ }
+
+ void UpdateUsage(const GURL& origin, int64 delta) {
+ quota_client_.UpdateUsage(origin, delta);
+ usage_tracker_.UpdateUsageCache(quota_client_.id(), origin, delta);
+ message_loop_.RunUntilIdle();
+ }
+
+ void UpdateUsageWithoutNotification(const GURL& origin, int64 delta) {
+ quota_client_.UpdateUsage(origin, delta);
+ }
+
+ void GetGlobalUsage(int64* usage, int64* unlimited_usage) {
+ bool done = false;
+ usage_tracker_.GetGlobalUsage(base::Bind(
+ &DidGetGlobalUsage,
+ &done, usage, unlimited_usage));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_TRUE(done);
+ }
+
+ void GetHostUsage(const std::string& host, int64* usage) {
+ bool done = false;
+ usage_tracker_.GetHostUsage(host, base::Bind(&DidGetUsage, &done, usage));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_TRUE(done);
+ }
+
+ void GrantUnlimitedStoragePolicy(const GURL& origin) {
+ if (!storage_policy_->IsStorageUnlimited(origin)) {
+ storage_policy_->AddUnlimited(origin);
+ storage_policy_->NotifyGranted(
+ origin, SpecialStoragePolicy::STORAGE_UNLIMITED);
+ }
+ }
+
+ void RevokeUnlimitedStoragePolicy(const GURL& origin) {
+ if (storage_policy_->IsStorageUnlimited(origin)) {
+ storage_policy_->RemoveUnlimited(origin);
+ storage_policy_->NotifyRevoked(
+ origin, SpecialStoragePolicy::STORAGE_UNLIMITED);
+ }
+ }
+
+ void SetUsageCacheEnabled(const GURL& origin, bool enabled) {
+ usage_tracker_.SetUsageCacheEnabled(
+ quota_client_.id(), origin, enabled);
+ }
+
+ private:
+ QuotaClientList GetUsageTrackerList() {
+ QuotaClientList client_list;
+ client_list.push_back(&quota_client_);
+ return client_list;
+ }
+
+ base::MessageLoop message_loop_;
+
+ scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
+ MockQuotaClient quota_client_;
+ UsageTracker usage_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsageTrackerTest);
+};
+
+TEST_F(UsageTrackerTest, GrantAndRevokeUnlimitedStorage) {
+ int64 usage = 0;
+ int64 unlimited_usage = 0;
+ int64 host_usage = 0;
+ GetGlobalUsage(&usage, &unlimited_usage);
+ EXPECT_EQ(0, usage);
+ EXPECT_EQ(0, unlimited_usage);
+
+ const GURL origin("http://example.com");
+ const std::string host(net::GetHostOrSpecFromURL(origin));
+
+ UpdateUsage(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ GrantUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(100, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ RevokeUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+}
+
+TEST_F(UsageTrackerTest, CacheDisabledClientTest) {
+ int64 usage = 0;
+ int64 unlimited_usage = 0;
+ int64 host_usage = 0;
+
+ const GURL origin("http://example.com");
+ const std::string host(net::GetHostOrSpecFromURL(origin));
+
+ UpdateUsage(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ UpdateUsageWithoutNotification(origin, 100);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(100, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(100, host_usage);
+
+ GrantUnlimitedStoragePolicy(origin);
+ UpdateUsageWithoutNotification(origin, 100);
+ SetUsageCacheEnabled(origin, false);
+ UpdateUsageWithoutNotification(origin, 100);
+
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(400, usage);
+ EXPECT_EQ(400, unlimited_usage);
+ EXPECT_EQ(400, host_usage);
+
+ RevokeUnlimitedStoragePolicy(origin);
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(400, usage);
+ EXPECT_EQ(400, unlimited_usage);
+ EXPECT_EQ(400, host_usage);
+
+ SetUsageCacheEnabled(origin, true);
+ UpdateUsage(origin, 100);
+
+ GetGlobalUsage(&usage, &unlimited_usage);
+ GetHostUsage(host, &host_usage);
+ EXPECT_EQ(500, usage);
+ EXPECT_EQ(0, unlimited_usage);
+ EXPECT_EQ(500, host_usage);
+}
+
+} // namespace quota
diff --git a/webkit/browser/quota/webkit_browser_quota.gypi b/webkit/browser/quota/webkit_browser_quota.gypi
new file mode 100644
index 0000000..a277afd
--- /dev/null
+++ b/webkit/browser/quota/webkit_browser_quota.gypi
@@ -0,0 +1,24 @@
+# Copyright 2013 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.
+
+{
+ 'variables': {
+ 'webkit_browser_quota_sources': [
+ '../browser/quota/quota_callbacks.h',
+ '../browser/quota/quota_client.h',
+ '../browser/quota/quota_database.cc',
+ '../browser/quota/quota_database.h',
+ '../browser/quota/quota_manager.cc',
+ '../browser/quota/quota_manager.h',
+ '../browser/quota/quota_task.cc',
+ '../browser/quota/quota_task.h',
+ '../browser/quota/quota_temporary_storage_evictor.cc',
+ '../browser/quota/quota_temporary_storage_evictor.h',
+ '../browser/quota/special_storage_policy.cc',
+ '../browser/quota/special_storage_policy.h',
+ '../browser/quota/usage_tracker.cc',
+ '../browser/quota/usage_tracker.h',
+ ],
+ },
+}
diff --git a/webkit/browser/webkit_browser.gypi b/webkit/browser/webkit_browser.gypi
index 3fe985d..94a4b9e 100644
--- a/webkit/browser/webkit_browser.gypi
+++ b/webkit/browser/webkit_browser.gypi
@@ -7,6 +7,7 @@
'../browser/blob/webkit_browser_blob.gypi',
'../browser/database/webkit_browser_database.gypi',
'../browser/fileapi/webkit_browser_fileapi.gypi',
+ '../browser/quota/webkit_browser_quota.gypi',
],
# TODO(kinuko): Have webkit_browser target and deprecate old gypis like
# webkit_storage.gypi.
@@ -15,6 +16,7 @@
'<@(webkit_browser_blob_sources)',
'<@(webkit_browser_database_sources)',
'<@(webkit_browser_fileapi_sources)',
+ '<@(webkit_browser_quota_sources)',
],
},
}