diff options
-rw-r--r-- | webkit/quota/quota_manager.cc | 133 | ||||
-rw-r--r-- | webkit/quota/quota_manager.h | 15 | ||||
-rw-r--r-- | webkit/quota/quota_manager_unittest.cc | 116 | ||||
-rw-r--r-- | webkit/quota/quota_types.h | 1 |
4 files changed, 209 insertions, 56 deletions
diff --git a/webkit/quota/quota_manager.cc b/webkit/quota/quota_manager.cc index 671e599b..027df27 100644 --- a/webkit/quota/quota_manager.cc +++ b/webkit/quota/quota_manager.cc @@ -345,9 +345,73 @@ QuotaManager::UsageAndQuotaDispatcherTask::Create( return NULL; } +class QuotaManager::OriginDataDeleter : public QuotaTask { + public: + OriginDataDeleter(QuotaManager* manager, + const GURL& origin, + StorageType type, + StatusCallback* callback) + : QuotaTask(manager), + origin_(origin), + type_(type), + error_count_(0), + remaining_clients_(-1), + callback_(callback), + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} + + protected: + virtual void Run() OVERRIDE { + error_count_ = 0; + remaining_clients_ = manager()->clients_.size(); + for (QuotaClientList::iterator iter = manager()->clients_.begin(); + iter != manager()->clients_.end(); ++iter) { + (*iter)->DeleteOriginData(origin_, type_, callback_factory_.NewCallback( + &OriginDataDeleter::DidDeleteOriginData)); + } + } + + virtual void Completed() OVERRIDE { + if (error_count_ == 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 error_count_; + int remaining_clients_; + scoped_ptr<StatusCallback> callback_; + + ScopedCallbackFactory<OriginDataDeleter> callback_factory_; + DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter); +}; + class QuotaManager::DatabaseTaskBase : public QuotaThreadTask { public: - DatabaseTaskBase(QuotaManager* manager) + explicit DatabaseTaskBase(QuotaManager* manager) : QuotaThreadTask(manager, manager->db_thread_), manager_(manager), database_(manager->database_.get()), @@ -994,6 +1058,21 @@ void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) { origins_in_use_.erase(origin); } +void QuotaManager::DeleteOriginData( + const GURL& origin, StorageType type, StatusCallback* callback) { + LazyInitialize(); + + if (origin.is_empty() || clients_.empty()) { + callback->Run(kQuotaStatusOk); + delete callback; + return; + } + + OriginDataDeleter* deleter = + new OriginDataDeleter(this, origin, type, callback); + deleter->Start(); +} + UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const { switch (type) { case kStorageTypeTemporary: @@ -1085,28 +1164,15 @@ 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) - ++eviction_context_.num_eviction_error; - - ++eviction_context_.num_evicted_clients; - DCHECK(eviction_context_.num_evicted_clients <= - eviction_context_.num_eviction_requested_clients); - if (eviction_context_.num_evicted_clients == - eviction_context_.num_eviction_requested_clients) { - eviction_context_.num_eviction_requested_clients = 0; - eviction_context_.num_evicted_clients = 0; - - if (eviction_context_.num_eviction_error == 0) { - DeleteOriginFromDatabase(eviction_context_.evicted_origin, - eviction_context_.evicted_type); - eviction_context_.evict_origin_data_callback->Run(kQuotaStatusOk); - } else { - origins_in_error_[eviction_context_.evicted_origin]++; - eviction_context_.evict_origin_data_callback->Run( - kQuotaErrorInvalidModification); - } - eviction_context_.evict_origin_data_callback.reset(); - } + origins_in_error_[eviction_context_.evicted_origin]++; + + eviction_context_.evict_origin_data_callback->Run(status); + eviction_context_.evict_origin_data_callback.reset(); } void QuotaManager::EvictOriginData( @@ -1114,31 +1180,14 @@ void QuotaManager::EvictOriginData( StorageType type, EvictOriginDataCallback* callback) { DCHECK(io_thread_->BelongsToCurrentThread()); - DCHECK(database_.get()); - DCHECK_EQ(eviction_context_.num_eviction_requested_clients, 0); DCHECK_EQ(type, kStorageTypeTemporary); - int num_clients = clients_.size(); - - if (origin.is_empty() || num_clients == 0) { - callback->Run(kQuotaStatusOk); - delete callback; - return; - } - - eviction_context_.num_eviction_requested_clients = num_clients; - eviction_context_.num_evicted_clients = 0; - eviction_context_.num_eviction_error = 0; - eviction_context_.evicted_origin = origin; eviction_context_.evicted_type = type; eviction_context_.evict_origin_data_callback.reset(callback); - for (QuotaClientList::iterator p = clients_.begin(); - p != clients_.end(); - ++p) { - (*p)->DeleteOriginData(origin, type, callback_factory_. - NewCallback(&QuotaManager::DidOriginDataEvicted)); - } + + DeleteOriginData(origin, type, callback_factory_.NewCallback( + &QuotaManager::DidOriginDataEvicted)); } void QuotaManager::DidGetAvailableSpaceForEviction( diff --git a/webkit/quota/quota_manager.h b/webkit/quota/quota_manager.h index beca602..19ab9d9 100644 --- a/webkit/quota/quota_manager.h +++ b/webkit/quota/quota_manager.h @@ -45,7 +45,7 @@ class QuotaEvictionHandler { virtual ~QuotaEvictionHandler() {} typedef Callback1<const GURL&>::Type GetLRUOriginCallback; - typedef Callback1<QuotaStatusCode>::Type EvictOriginDataCallback; + typedef StatusCallback EvictOriginDataCallback; typedef Callback5<QuotaStatusCode, int64 /* usage */, int64 /* unlimited_usage */, @@ -121,6 +121,11 @@ class QuotaManager : public QuotaTaskObserver, return origins_in_use_.find(origin) != origins_in_use_.end(); } + // Called by UI. + void DeleteOriginData(const GURL& origin, + StorageType type, + StatusCallback* callback); + // Called by UI and internal modules. void GetAvailableSpace(AvailableSpaceCallback* callback); void GetTemporaryGlobalQuota(QuotaCallback* callback); @@ -171,6 +176,8 @@ class QuotaManager : public QuotaTaskObserver, class UsageAndQuotaDispatcherTaskForTemporary; class UsageAndQuotaDispatcherTaskForPersistent; + class OriginDataDeleter; + class AvailableSpaceQueryTask; class DumpQuotaTableTask; class DumpLastAccessTimeTableTask; @@ -187,9 +194,6 @@ class QuotaManager : public QuotaTaskObserver, struct EvictionContext { EvictionContext() : evicted_type(kStorageTypeUnknown), - num_eviction_requested_clients(0), - num_evicted_clients(0), - num_eviction_error(0), usage(0), unlimited_usage(0), quota(0) {} @@ -199,9 +203,6 @@ class QuotaManager : public QuotaTaskObserver, StorageType evicted_type; scoped_ptr<EvictOriginDataCallback> evict_origin_data_callback; - int num_eviction_requested_clients; - int num_evicted_clients; - int num_eviction_error; scoped_ptr<GetUsageAndQuotaForEvictionCallback> get_usage_and_quota_callback; diff --git a/webkit/quota/quota_manager_unittest.cc b/webkit/quota/quota_manager_unittest.cc index 92b69896..6cd482d 100644 --- a/webkit/quota/quota_manager_unittest.cc +++ b/webkit/quota/quota_manager_unittest.cc @@ -153,7 +153,7 @@ class QuotaManagerTest : public testing::Test { quota_status_ = kQuotaStatusUnknown; client->DeleteOriginData(origin, type, callback_factory_.NewCallback( - &QuotaManagerTest::DidDeleteClientOriginData)); + &QuotaManagerTest::StatusCallback)); } void EvictOriginData(const GURL& origin, @@ -161,7 +161,15 @@ class QuotaManagerTest : public testing::Test { quota_status_ = kQuotaStatusUnknown; quota_manager_->EvictOriginData(origin, type, callback_factory_.NewCallback( - &QuotaManagerTest::DidEvictOriginData)); + &QuotaManagerTest::StatusCallback)); + } + + void DeleteOriginData(const GURL& origin, + StorageType type) { + quota_status_ = kQuotaStatusUnknown; + quota_manager_->DeleteOriginData(origin, type, + callback_factory_.NewCallback( + &QuotaManagerTest::StatusCallback)); } void GetAvailableSpace() { @@ -273,11 +281,8 @@ class QuotaManagerTest : public testing::Test { usage_ = usage; } - void DidDeleteClientOriginData(QuotaStatusCode status) { - quota_status_ = status; - } - - void DidEvictOriginData(QuotaStatusCode status) { + void StatusCallback(QuotaStatusCode status) { + ++status_callback_count_; quota_status_ = status; } @@ -333,6 +338,8 @@ class QuotaManagerTest : public testing::Test { return last_access_time_table_; } 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 GetCurrentMockTime() { @@ -356,6 +363,7 @@ class QuotaManagerTest : public testing::Test { GURL lru_origin_; QuotaTableEntries quota_table_; LastAccessTimeTableEntries last_access_time_table_; + int status_callback_count_; int additional_callback_count_; @@ -631,6 +639,9 @@ TEST_F(QuotaManagerTest, GetTemporaryUsageAndQuota_NukeManager) { RunAdditionalUsageAndQuotaTask(GURL("http://bar.com/"), kTemp); + DeleteOriginData(GURL("http://foo.com/"), kTemp); + DeleteOriginData(GURL("http://bar.com/"), kTemp); + // Nuke before waiting for callbacks. set_quota_manager(NULL); MessageLoop::current()->RunAllPending(); @@ -1172,6 +1183,97 @@ TEST_F(QuotaManagerTest, GetUsageAndQuotaForEviction) { EXPECT_LE(0, available_space()); } +// 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)); + MockStorageClient* client2 = CreateClient(kData2, ARRAYSIZE_UNSAFE(kData2)); + RegisterClient(client1); + RegisterClient(client2); + + GetGlobalUsage(kTemp); + MessageLoop::current()->RunAllPending(); + const int64 predelete_global_tmp = usage(); + + GetHostUsage("foo.com", kTemp); + MessageLoop::current()->RunAllPending(); + const int64 predelete_foo_tmp = usage(); + + GetHostUsage("bar.com", kTemp); + MessageLoop::current()->RunAllPending(); + const int64 predelete_bar_tmp = usage(); + + GetHostUsage("foo.com", kPerm); + MessageLoop::current()->RunAllPending(); + const int64 predelete_foo_pers = usage(); + + GetHostUsage("bar.com", kPerm); + MessageLoop::current()->RunAllPending(); + const int64 predelete_bar_pers = usage(); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData1); ++i) + quota_manager()->NotifyStorageAccessed(QuotaClient::kMockStart, + GURL(kData1[i].origin), kData1[i].type); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kData2); ++i) + quota_manager()->NotifyStorageAccessed(QuotaClient::kMockStart, + GURL(kData2[i].origin), kData2[i].type); + MessageLoop::current()->RunAllPending(); + + reset_status_callback_count(); + DeleteOriginData(GURL("http://foo.com/"), kTemp); + DeleteOriginData(GURL("http://bar.com/"), kTemp); + DeleteOriginData(GURL("http://foo.com/"), kTemp); + MessageLoop::current()->RunAllPending(); + + EXPECT_EQ(3, status_callback_count()); + + DumpLastAccessTimeTable(); + MessageLoop::current()->RunAllPending(); + + typedef LastAccessTimeTableEntries::const_iterator iterator; + for (iterator itr(last_access_time_table().begin()), + end(last_access_time_table().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); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(predelete_global_tmp - (1 + 4000 + 50000 + 9), usage()); + + GetHostUsage("foo.com", kTemp); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(predelete_foo_tmp - (1 + 50000), usage()); + + GetHostUsage("bar.com", kTemp); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(predelete_bar_tmp - (4000 + 9), usage()); + + GetHostUsage("foo.com", kPerm); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(predelete_foo_pers, usage()); + + GetHostUsage("bar.com", kPerm); + MessageLoop::current()->RunAllPending(); + EXPECT_EQ(predelete_bar_pers, usage()); +} + TEST_F(QuotaManagerTest, GetCachedOrigins) { static const MockOriginData kData[] = { { "http://a.com/", kTemp, 1 }, diff --git a/webkit/quota/quota_types.h b/webkit/quota/quota_types.h index 449e717..62f29e7 100644 --- a/webkit/quota/quota_types.h +++ b/webkit/quota/quota_types.h @@ -49,6 +49,7 @@ typedef Callback4<QuotaStatusCode, int64>::Type HostQuotaCallback; typedef Callback2<QuotaStatusCode, int64>::Type AvailableSpaceCallback; +typedef Callback1<QuotaStatusCode>::Type StatusCallback; // Simple template wrapper for a callback queue. template <typename CallbackType> |