diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-18 10:02:26 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-18 10:02:26 +0000 |
commit | ec3d1456bdbfd7d7806e51dd1e59088f691495ec (patch) | |
tree | 7f85308cde8eab9ffebcb4910894c184060ba15f /webkit/database | |
parent | 4236df757e45014700da962b9a20d46f61b4440f (diff) | |
download | chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.zip chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.tar.gz chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.tar.bz2 |
Actually delete databases in CookiesTreeModel.
BUG=34633
TEST=delete a database while it's opened in the renderer
Review URL: http://codereview.chromium.org/600104
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39346 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/database')
-rw-r--r-- | webkit/database/database_tracker.cc | 103 | ||||
-rw-r--r-- | webkit/database/database_tracker.h | 44 | ||||
-rw-r--r-- | webkit/database/database_tracker_unittest.cc | 106 |
3 files changed, 202 insertions, 51 deletions
diff --git a/webkit/database/database_tracker.cc b/webkit/database/database_tracker.cc index f2bb4ae..1d75260 100644 --- a/webkit/database/database_tracker.cc +++ b/webkit/database/database_tracker.cc @@ -36,14 +36,13 @@ DatabaseTracker::DatabaseTracker(const FilePath& profile_path) db_(new sql::Connection()), databases_table_(NULL), meta_table_(NULL), - dbs_deleted_callback_(NULL), default_quota_(5 * 1024 * 1024) { } DatabaseTracker::~DatabaseTracker() { DCHECK(observers_.size() == 0); DCHECK(dbs_to_be_deleted_.empty()); - DCHECK(!dbs_deleted_callback_); + DCHECK(deletion_callbacks_.empty()); } void DatabaseTracker::SetDefaultQuota(int64 quota) { @@ -105,15 +104,32 @@ void DatabaseTracker::DeleteDatabaseIfNeeded(const string16& origin_identifier, DCHECK(!database_connections_.IsDatabaseOpened(origin_identifier, database_name)); if (IsDatabaseScheduledForDeletion(origin_identifier, database_name)) { - DeleteDatabase(origin_identifier, database_name); + DeleteClosedDatabase(origin_identifier, database_name); dbs_to_be_deleted_[origin_identifier].erase(database_name); if (dbs_to_be_deleted_[origin_identifier].empty()) dbs_to_be_deleted_.erase(origin_identifier); - if (dbs_to_be_deleted_.empty()) { - DCHECK(dbs_deleted_callback_); - dbs_deleted_callback_->Run(net::OK); - dbs_deleted_callback_ = NULL; + + std::vector<net::CompletionCallback*> to_be_deleted; + for (PendingCompletionMap::iterator callback = deletion_callbacks_.begin(); + callback != deletion_callbacks_.end(); ++callback) { + DatabaseSet::iterator found_origin = + callback->second.find(origin_identifier); + if (found_origin != callback->second.end()) { + std::set<string16>& databases = found_origin->second; + databases.erase(database_name); + if (databases.empty()) { + callback->second.erase(found_origin); + if (callback->second.empty()) { + net::CompletionCallback* cb = callback->first; + cb->Run(net::OK); + to_be_deleted.push_back(cb); + } + } + } } + for (std::vector<net::CompletionCallback*>::iterator cb = + to_be_deleted.begin(); cb != to_be_deleted.end(); ++cb) + deletion_callbacks_.erase(*cb); } } @@ -185,14 +201,13 @@ void DatabaseTracker::SetOriginQuota(const string16& origin_identifier, } } -bool DatabaseTracker::DeleteDatabase(const string16& origin_identifier, - const string16& database_name) { +bool DatabaseTracker::DeleteClosedDatabase(const string16& origin_identifier, + const string16& database_name) { // Check if the database is opened by any renderer. if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) return false; // Try to delete the file on the hard drive. - // TODO(jochen): Delete directory if this was the last database. // TODO(jochen): Delete journal files associated with this database. FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); if (file_util::PathExists(db_file) && !file_util::Delete(db_file, false)) @@ -201,6 +216,12 @@ bool DatabaseTracker::DeleteDatabase(const string16& origin_identifier, // Clean up the main database and invalidate the cached record. databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); origins_info_map_.erase(origin_identifier); + + // Try to delete the origin in case this was the last database. + std::vector<DatabaseDetails> details; + if (databases_table_->GetAllDatabaseDetailsForOrigin( + origin_identifier, &details) && details.empty()) + DeleteOrigin(origin_identifier); return true; } @@ -225,8 +246,7 @@ bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { bool DatabaseTracker::IsDatabaseScheduledForDeletion( const string16& origin_identifier, const string16& database_name) { - std::map<string16, std::set<string16> >::iterator it = - dbs_to_be_deleted_.find(origin_identifier); + DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier); if (it == dbs_to_be_deleted_.end()) return false; @@ -388,31 +408,47 @@ void DatabaseTracker::ScheduleDatabaseForDeletion( origin_identifier, database_name)); } +int DatabaseTracker::DeleteDatabase(const string16& origin_identifier, + const string16& database_name, + net::CompletionCallback* callback) { + if (!LazyInit()) + return net::ERR_FAILED; + + DCHECK(!callback || + deletion_callbacks_.find(callback) == deletion_callbacks_.end()); + + if (database_connections_.IsDatabaseOpened(origin_identifier, + database_name)) { + if (callback) + deletion_callbacks_[callback][origin_identifier].insert(database_name); + ScheduleDatabaseForDeletion(origin_identifier, database_name); + return net::ERR_IO_PENDING; + } + DeleteClosedDatabase(origin_identifier, database_name); + return net::OK; +} + int DatabaseTracker::DeleteDataModifiedSince( const base::Time& cutoff, net::CompletionCallback* callback) { - // Check for reentrancy. - if (dbs_deleted_callback_ || !LazyInit()) + if (!LazyInit()) return net::ERR_FAILED; - DCHECK(callback); - dbs_deleted_callback_ = callback; + DCHECK(!callback || + deletion_callbacks_.find(callback) == deletion_callbacks_.end()); + DatabaseSet to_be_deleted; std::vector<string16> origins; - if (!databases_table_->GetAllOrigins(&origins)) { - dbs_deleted_callback_ = NULL; + if (!databases_table_->GetAllOrigins(&origins)) return net::ERR_FAILED; - } int rv = net::OK; for (std::vector<string16>::const_iterator ori = origins.begin(); ori != origins.end(); ++ori) { if (StartsWith(*ori, ASCIIToUTF16(kExtensionOriginIdentifierPrefix), true)) continue; std::vector<DatabaseDetails> details; - if (!databases_table_->GetAllDatabaseDetailsForOrigin(*ori, &details)) { + if (!databases_table_->GetAllDatabaseDetailsForOrigin(*ori, &details)) rv = net::ERR_FAILED; - continue; - } for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); db != details.end(); ++db) { FilePath db_file = GetFullDBFilePath(*ori, db->database_name); @@ -422,18 +458,23 @@ int DatabaseTracker::DeleteDataModifiedSince( continue; // Check if the database is opened by any renderer. - if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) { - ScheduleDatabaseForDeletion(*ori, db->database_name); - rv = net::ERR_IO_PENDING; - } else { - DeleteDatabase(*ori, db->database_name); - } + if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) + to_be_deleted[*ori].insert(db->database_name); + else + DeleteClosedDatabase(*ori, db->database_name); } } - if (rv != net::ERR_IO_PENDING) { - dbs_to_be_deleted_.clear(); - dbs_deleted_callback_ = NULL; + if (!to_be_deleted.empty()) { + if (callback) + deletion_callbacks_[callback] = to_be_deleted; + for (DatabaseSet::iterator ori = to_be_deleted.begin(); + ori != to_be_deleted.end(); ++ori) { + for (std::set<string16>::iterator db = ori->second.begin(); + db != ori->second.end(); ++db) + ScheduleDatabaseForDeletion(ori->first, *db); + } + rv = net::ERR_IO_PENDING; } return rv; } diff --git a/webkit/database/database_tracker.h b/webkit/database/database_tracker.h index a6c7720..139e5fe 100644 --- a/webkit/database/database_tracker.h +++ b/webkit/database/database_tracker.h @@ -41,28 +41,26 @@ class OriginInfo { int64 TotalSize() const { return total_size_; } int64 Quota() const { return quota_; } void GetAllDatabaseNames(std::vector<string16>* databases) const { - for (std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.begin(); it != database_info_.end(); it++) { + for (DatabaseInfoMap::const_iterator it = database_info_.begin(); + it != database_info_.end(); it++) { databases->push_back(it->first); } } int64 GetDatabaseSize(const string16& database_name) const { - std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.find(database_name); + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); if (it != database_info_.end()) return it->second.first; return 0; } string16 GetDatabaseDescription(const string16& database_name) const { - std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.find(database_name); + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); if (it != database_info_.end()) return it->second.second; return string16(); } protected: - typedef std::pair<int64, string16> DatabaseInfo; + typedef std::map<string16, std::pair<int64, string16> > DatabaseInfoMap; OriginInfo(const string16& origin, int64 total_size, int64 quota) : origin_(origin), total_size_(total_size), quota_(quota) { } @@ -70,7 +68,7 @@ class OriginInfo { string16 origin_; int64 total_size_; int64 quota_; - std::map<string16, DatabaseInfo> database_info_; + DatabaseInfoMap database_info_; }; // This class manages the main database, and keeps track of per origin quotas. @@ -101,9 +99,6 @@ class DatabaseTracker explicit DatabaseTracker(const FilePath& profile_path); - // Sets the default quota for all origins. Should be used in tests only. - void SetDefaultQuota(int64 quota); - void DatabaseOpened(const string16& origin_identifier, const string16& database_name, const string16& database_details, @@ -129,16 +124,24 @@ class DatabaseTracker bool GetAllOriginsInfo(std::vector<OriginInfo>* origins_info); void SetOriginQuota(const string16& origin_identifier, int64 new_quota); - bool DeleteDatabase(const string16& origin_identifier, - const string16& database_name); - bool DeleteOrigin(const string16& origin_identifier); + + // Sets the default quota for all origins. Should be used in tests only. + void SetDefaultQuota(int64 quota); + bool IsDatabaseScheduledForDeletion(const string16& origin_identifier, const string16& database_name); + // Deletes a single database. Returns net::OK on success, net::FAILED on + // failure, or net::ERR_IO_PENDING and |callback| is invoked upon completion, + // if non-NULL. + int DeleteDatabase(const string16& origin_identifier, + const string16& database_name, + net::CompletionCallback* callback); + // Delete any databases that have been touched since the cutoff date that's // supplied. Returns net::OK on success, net::FAILED if not all databases // could be deleted, and net::ERR_IO_PENDING and |callback| is invoked upon - // completion. + // completion, if non-NULL. int DeleteDataModifiedSince(const base::Time& cutoff, net::CompletionCallback* callback); @@ -148,6 +151,9 @@ class DatabaseTracker // Need this here to allow RefCountedThreadSafe to call ~DatabaseTracker(). friend class base::RefCountedThreadSafe<DatabaseTracker>; + typedef std::map<string16, std::set<string16> > DatabaseSet; + typedef std::map<net::CompletionCallback*, DatabaseSet> PendingCompletionMap; + class CachedOriginInfo : public OriginInfo { public: CachedOriginInfo() : OriginInfo(string16(), 0, 0) {} @@ -169,6 +175,10 @@ class DatabaseTracker ~DatabaseTracker(); + bool DeleteClosedDatabase(const string16& origin_identifier, + const string16& database_name); + bool DeleteOrigin(const string16& origin_identifier); + bool LazyInit(); bool UpgradeToCurrentVersion(); void InsertOrUpdateDatabaseDetails(const string16& origin_identifier, @@ -200,8 +210,8 @@ class DatabaseTracker DatabaseConnections database_connections_; // The set of databases that should be deleted but are still opened - std::map<string16, std::set<string16> > dbs_to_be_deleted_; - net::CompletionCallback* dbs_deleted_callback_; + DatabaseSet dbs_to_be_deleted_; + PendingCompletionMap deletion_callbacks_; // Default quota for all origins; changed only by tests int64 default_quota_; diff --git a/webkit/database/database_tracker_unittest.cc b/webkit/database/database_tracker_unittest.cc index 0ed707d..55d13e1 100644 --- a/webkit/database/database_tracker_unittest.cc +++ b/webkit/database/database_tracker_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -7,6 +7,8 @@ #include "base/scoped_ptr.h" #include "base/scoped_temp_dir.h" #include "base/string_util.h" +#include "base/time.h" +#include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/database/database_tracker.h" @@ -28,6 +30,9 @@ class TestObserver : public webkit_database::DatabaseTracker::Observer { } virtual void OnDatabaseScheduledForDeletion(const string16& origin_identifier, const string16& database_name) { + new_notification_received_ = true; + origin_identifier_ = origin_identifier; + database_name_ = database_name; } bool DidReceiveNewNotification() { bool temp_new_notification_received = new_notification_received_; @@ -67,6 +72,101 @@ void CheckNotificationReceived(TestObserver* observer, namespace webkit_database { +TEST(DatabaseTrackerTest, DeleteOpenDatabase) { + // Initialize the tracker database. + ScopedTempDir temp_dir; + EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); + scoped_refptr<DatabaseTracker> tracker(new DatabaseTracker(temp_dir.path())); + + // Create and open three databases. + int64 database_size = 0; + int64 space_available = 0; + const string16 kOrigin1 = ASCIIToUTF16("origin1"); + const string16 kOrigin2 = ASCIIToUTF16("origin2"); + const string16 kDB1 = ASCIIToUTF16("db1"); + const string16 kDB2 = ASCIIToUTF16("db2"); + const string16 kDB3 = ASCIIToUTF16("db3"); + const string16 kDescription = ASCIIToUTF16("database_description"); + + tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, + &database_size, &space_available); + tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, + &database_size, &space_available); + tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0, + &database_size, &space_available); + + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin2))))); + EXPECT_EQ(1, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); + EXPECT_EQ(2, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2)); + EXPECT_EQ(3, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin2, kDB3), "aaa", 3)); + tracker->DatabaseModified(kOrigin1, kDB1); + tracker->DatabaseModified(kOrigin2, kDB2); + tracker->DatabaseModified(kOrigin2, kDB3); + + // Delete db1. Should also delete origin1. + TestObserver observer; + tracker->AddObserver(&observer); + TestCompletionCallback callback; + int result = tracker->DeleteDatabase(kOrigin1, kDB1, &callback); + EXPECT_EQ(net::ERR_IO_PENDING, result); + ASSERT_FALSE(callback.have_result()); + EXPECT_TRUE(observer.DidReceiveNewNotification()); + EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier()); + EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName()); + tracker->DatabaseClosed(kOrigin1, kDB1); + result = callback.GetResult(result); + EXPECT_EQ(net::OK, result); + EXPECT_FALSE(file_util::PathExists(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + + // Recreate db1. + tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, + &database_size, &space_available); + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_EQ(1, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); + tracker->DatabaseModified(kOrigin1, kDB1); + + // Setup file modification times. db1 and db2 are modified now, db3 three + // days ago. + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin1, kDB1), base::Time::Now())); + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin2, kDB2), base::Time::Now())); + base::Time three_days_ago = base::Time::Now(); + three_days_ago -= base::TimeDelta::FromDays(3); + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin2, kDB3), three_days_ago)); + + // Delete databases modified since yesterday. + base::Time yesterday = base::Time::Now(); + yesterday -= base::TimeDelta::FromDays(1); + result = tracker->DeleteDataModifiedSince(yesterday, &callback); + EXPECT_EQ(net::ERR_IO_PENDING, result); + ASSERT_FALSE(callback.have_result()); + EXPECT_TRUE(observer.DidReceiveNewNotification()); + tracker->DatabaseClosed(kOrigin1, kDB1); + tracker->DatabaseClosed(kOrigin2, kDB2); + result = callback.GetResult(result); + EXPECT_EQ(net::OK, result); + EXPECT_FALSE(file_util::PathExists(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_FALSE( + file_util::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); + EXPECT_TRUE( + file_util::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3))); + + tracker->DatabaseClosed(kOrigin2, kDB3); + tracker->RemoveObserver(&observer); +} + TEST(DatabaseTrackerTest, TestIt) { // Initialize the tracker database. ScopedTempDir temp_dir; @@ -210,14 +310,14 @@ TEST(DatabaseTrackerTest, TestIt) { // Trying to delete a database in use should fail tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0, &database_size, &space_available); - EXPECT_FALSE(tracker->DeleteDatabase(kOrigin1, kDB3)); + EXPECT_FALSE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); origin1_info = tracker->GetCachedOriginInfo(kOrigin1); EXPECT_TRUE(origin1_info); EXPECT_EQ(6, origin1_info->GetDatabaseSize(kDB3)); tracker->DatabaseClosed(kOrigin1, kDB3); // Delete a database and make sure the space used by that origin is updated - EXPECT_TRUE(tracker->DeleteDatabase(kOrigin1, kDB3)); + EXPECT_TRUE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); origin1_info = tracker->GetCachedOriginInfo(kOrigin1); EXPECT_TRUE(origin1_info); EXPECT_EQ(origin1_quota - 5, tracker->GetOriginSpaceAvailable(kOrigin1)); |