diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-08 08:00:50 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-08 08:00:50 +0000 |
commit | 800ad566b10caee04ca18c7eb1796b21c1766c37 (patch) | |
tree | 276a35a8565d9bb981bb4ec656db26be3e8bce14 /webkit | |
parent | e6487b915297e168448ea29d81421a3f72a26c10 (diff) | |
download | chromium_src-800ad566b10caee04ca18c7eb1796b21c1766c37.zip chromium_src-800ad566b10caee04ca18c7eb1796b21c1766c37.tar.gz chromium_src-800ad566b10caee04ca18c7eb1796b21c1766c37.tar.bz2 |
Move code to clear web databases on shutdown to the database tracker
BUG=86928
TEST=test_shell_tests
Review URL: http://codereview.chromium.org/7234014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@91817 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/database/database_quota_client_unittest.cc | 2 | ||||
-rw-r--r-- | webkit/database/database_tracker.cc | 150 | ||||
-rw-r--r-- | webkit/database/database_tracker.h | 30 | ||||
-rw-r--r-- | webkit/database/database_tracker_unittest.cc | 113 | ||||
-rw-r--r-- | webkit/database/vfs_backend.cc | 4 | ||||
-rw-r--r-- | webkit/support/simple_database_system.cc | 3 |
6 files changed, 244 insertions, 58 deletions
diff --git a/webkit/database/database_quota_client_unittest.cc b/webkit/database/database_quota_client_unittest.cc index 6c9f293..9bbae9e 100644 --- a/webkit/database/database_quota_client_unittest.cc +++ b/webkit/database/database_quota_client_unittest.cc @@ -26,7 +26,7 @@ static const quota::StorageType kPerm = quota::kStorageTypePersistent; class MockDatabaseTracker : public DatabaseTracker { public: MockDatabaseTracker() - : DatabaseTracker(FilePath(), false, NULL, NULL, NULL), + : DatabaseTracker(FilePath(), false, false, NULL, NULL, NULL), delete_called_count_(0), async_delete_(false) {} diff --git a/webkit/database/database_tracker.cc b/webkit/database/database_tracker.cc index 7497c38..d01acda 100644 --- a/webkit/database/database_tracker.cc +++ b/webkit/database/database_tracker.cc @@ -15,6 +15,7 @@ #include "base/basictypes.h" #include "base/file_util.h" #include "base/message_loop_proxy.h" +#include "base/platform_file.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "net/base/net_errors.h" @@ -47,7 +48,11 @@ const FilePath::CharType kTrackerDatabaseFileName[] = FILE_PATH_LITERAL("Databases.db"); static const int kCurrentVersion = 2; static const int kCompatibleVersion = 1; -static const char* kExtensionOriginIdentifierPrefix = "chrome-extension_"; + +const FilePath::CharType kTemporaryDirectoryPrefix[] = + FILE_PATH_LITERAL("DeleteMe"); +const FilePath::CharType kTemporaryDirectoryPattern[] = + FILE_PATH_LITERAL("DeleteMe*"); OriginInfo::OriginInfo() : total_size_(0) {} @@ -87,11 +92,13 @@ OriginInfo::OriginInfo(const string16& origin, int64 total_size) DatabaseTracker::DatabaseTracker( const FilePath& profile_path, bool is_incognito, + bool clear_local_state_on_exit, quota::SpecialStoragePolicy* special_storage_policy, quota::QuotaManagerProxy* quota_manager_proxy, base::MessageLoopProxy* db_tracker_thread) : is_initialized_(false), is_incognito_(is_incognito), + clear_local_state_on_exit_(clear_local_state_on_exit), shutting_down_(false), profile_path_(profile_path), db_dir_(is_incognito_ ? @@ -102,6 +109,7 @@ DatabaseTracker::DatabaseTracker( meta_table_(NULL), special_storage_policy_(special_storage_policy), quota_manager_proxy_(quota_manager_proxy), + db_tracker_thread_(db_tracker_thread), incognito_origin_directories_generator_(0) { if (quota_manager_proxy) { quota_manager_proxy->RegisterClient( @@ -119,7 +127,7 @@ void DatabaseTracker::DatabaseOpened(const string16& origin_identifier, const string16& database_description, int64 estimated_size, int64* database_size) { - if (!LazyInit()) { + if (shutting_down_ || !LazyInit()) { *database_size = 0; return; } @@ -363,17 +371,18 @@ bool DatabaseTracker::DeleteClosedDatabase(const string16& origin_identifier, if (databases_table_->GetAllDatabaseDetailsForOrigin( origin_identifier, &details) && details.empty()) { // Try to delete the origin in case this was the last database. - DeleteOrigin(origin_identifier); + DeleteOrigin(origin_identifier, false); } return true; } -bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { +bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier, + bool force) { if (!LazyInit()) return false; // Check if any database in this origin is opened by any renderer. - if (database_connections_.IsOriginUsed(origin_identifier)) + if (database_connections_.IsOriginUsed(origin_identifier) && !force) return false; int64 deleted_size = 0; @@ -383,14 +392,28 @@ bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { deleted_size = origin_info->TotalSize(); } - // We need to invalidate the cached record whether file_util::Delete() - // succeeds or not, because even if it fails, it might still delete some - // DB files on the hard drive. origins_info_map_.erase(origin_identifier); FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( UTF16ToWide(origin_identifier))); - if (!file_util::Delete(origin_dir, true)) - return false; + + // Create a temporary directory to move possibly still existing databases to, + // as we can't delete the origin directory on windows if it contains opened + // files. + FilePath new_origin_dir; + file_util::CreateTemporaryDirInDir(db_dir_, + kTemporaryDirectoryPrefix, + &new_origin_dir); + file_util::FileEnumerator databases( + origin_dir, + false, + file_util::FileEnumerator::FILES); + for (FilePath database = databases.Next(); !database.empty(); + database = databases.Next()) { + FilePath new_file = new_origin_dir.Append(database.BaseName()); + file_util::Move(database, new_file); + } + file_util::Delete(origin_dir, true); + file_util::Delete(new_origin_dir, true); // might fail on windows. databases_table_->DeleteOrigin(origin_identifier); @@ -422,6 +445,20 @@ bool DatabaseTracker::LazyInit() { DCHECK(!databases_table_.get()); DCHECK(!meta_table_.get()); + // If there are left-over directories from failed deletion attempts, clean + // them up. + if (file_util::DirectoryExists(db_dir_)) { + file_util::FileEnumerator directories( + db_dir_, + false, + file_util::FileEnumerator::DIRECTORIES, + kTemporaryDirectoryPattern); + for (FilePath directory = directories.Next(); !directory.empty(); + directory = directories.Next()) { + file_util::Delete(directory, true); + } + } + // If the tracker database exists, but it's corrupt or doesn't // have a meta table, delete the database directory. const FilePath kTrackerDatabaseFullPath = @@ -747,44 +784,67 @@ void DatabaseTracker::DeleteIncognitoDBDirectory() { file_util::Delete(incognito_db_dir, true); } -// static -void DatabaseTracker::ClearLocalState(const FilePath& profile_path) { - // TODO(michaeln): use SpecialStoragePolicy instead of kExtensionOriginPrefix - FilePath db_dir = profile_path.Append(FilePath(kDatabaseDirectoryName)); - FilePath db_tracker = db_dir.Append(FilePath(kTrackerDatabaseFileName)); - if (file_util::DirectoryExists(db_dir) && - file_util::PathExists(db_tracker)) { - scoped_ptr<sql::Connection> db_(new sql::Connection); - if (!db_->Open(db_tracker) || - !db_->DoesTableExist("Databases")) { - db_->Close(); - file_util::Delete(db_dir, true); - return; - } else { - sql::Statement delete_statement(db_->GetCachedStatement( - SQL_FROM_HERE, "DELETE FROM Databases WHERE origin NOT LIKE ?")); - std::string filter(kExtensionOriginIdentifierPrefix); - filter += "%"; - delete_statement.BindString(0, filter); - if (!delete_statement.Run()) { - db_->Close(); - file_util::Delete(db_dir, true); - return; - } +void DatabaseTracker::ClearLocalState() { + shutting_down_ = true; + + std::vector<string16> origin_identifiers; + GetAllOriginIdentifiers(&origin_identifiers); + + for (std::vector<string16>::iterator origin = origin_identifiers.begin(); + origin != origin_identifiers.end(); ++origin) { + if (special_storage_policy_.get() && + special_storage_policy_->IsStorageProtected( + webkit_database::DatabaseUtil::GetOriginFromIdentifier(*origin))) { + continue; } - } - file_util::FileEnumerator file_enumerator(db_dir, false, - file_util::FileEnumerator::DIRECTORIES); - for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); - file_path = file_enumerator.Next()) { - if (file_path.BaseName() != FilePath(kTrackerDatabaseFileName)) { - std::string basename = file_path.BaseName().MaybeAsASCII(); - if (!basename.empty() && - !StartsWithASCII(basename, kExtensionOriginIdentifierPrefix, true)) { - file_util::Delete(file_path, true); - } + webkit_database::OriginInfo origin_info; + std::vector<string16> databases; + GetOriginInfo(*origin, &origin_info); + origin_info.GetAllDatabaseNames(&databases); + + for (std::vector<string16>::iterator database = databases.begin(); + database != databases.end(); ++database) { + base::PlatformFile file_handle = base::CreatePlatformFile( + GetFullDBFilePath(*origin, *database), + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_SHARE_DELETE | + base::PLATFORM_FILE_DELETE_ON_CLOSE | + base::PLATFORM_FILE_READ, + NULL, NULL); + base::ClosePlatformFile(file_handle); } + DeleteOrigin(*origin, true); + } +} + + +void DatabaseTracker::Shutdown() { + DCHECK(db_tracker_thread_.get()); + DCHECK(db_tracker_thread_->BelongsToCurrentThread()); + if (shutting_down_) { + NOTREACHED(); + return; + } + if (is_incognito_) + DeleteIncognitoDBDirectory(); + else if (clear_local_state_on_exit_ && LazyInit()) + ClearLocalState(); +} + +void DatabaseTracker::SetClearLocalStateOnExit(bool clear_local_state_on_exit) { + DCHECK(db_tracker_thread_.get()); + if (!db_tracker_thread_->BelongsToCurrentThread()) { + db_tracker_thread_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &DatabaseTracker::SetClearLocalStateOnExit, + clear_local_state_on_exit)); + return; + } + if (shutting_down_) { + NOTREACHED(); + return; } + clear_local_state_on_exit_ = clear_local_state_on_exit; } } // namespace webkit_database diff --git a/webkit/database/database_tracker.h b/webkit/database/database_tracker.h index bbc308a..6c6abe0 100644 --- a/webkit/database/database_tracker.h +++ b/webkit/database/database_tracker.h @@ -88,7 +88,9 @@ class DatabaseTracker virtual ~Observer() {} }; - DatabaseTracker(const FilePath& profile_path, bool is_incognito, + DatabaseTracker(const FilePath& profile_path, + bool is_incognito, + bool clear_local_state_on_exit, quota::SpecialStoragePolicy* special_storage_policy, quota::QuotaManagerProxy* quota_manager_proxy, base::MessageLoopProxy* db_tracker_thread); @@ -158,10 +160,10 @@ class DatabaseTracker bool CloseIncognitoFileHandle(const string16& vfs_file_path); bool HasSavedIncognitoFileHandle(const string16& vfs_file_path) const; - // Deletes the directory that stores all DBs in incognito mode, if it exists. - void DeleteIncognitoDBDirectory(); - - static void ClearLocalState(const FilePath& profile_path); + // Shutdown the database tracker, deleting database files if the tracker is + // used for an incognito profile or |clear_local_state_on_exit_| is true. + void Shutdown(); + void SetClearLocalStateOnExit(bool clear_local_state_on_exit); private: friend class base::RefCountedThreadSafe<DatabaseTracker>; @@ -193,9 +195,21 @@ class DatabaseTracker // virtual for unittesting only virtual ~DatabaseTracker(); + // Deletes the directory that stores all DBs in incognito mode, if it exists. + void DeleteIncognitoDBDirectory(); + + // Deletes databases not protected by the special storage policy if + // |clear_local_state_on_exit_| is true and blocks databases from being + // created/opened. + void ClearLocalState(); + bool DeleteClosedDatabase(const string16& origin_identifier, const string16& database_name); - bool DeleteOrigin(const string16& origin_identifier); + + // Delete all files belonging to the given origin given that no database + // connections within this origin are open, or if |force| is true, delete + // the meta data and rename the associated directory. + bool DeleteOrigin(const string16& origin_identifier, bool force); void DeleteDatabaseIfNeeded(const string16& origin_identifier, const string16& database_name); @@ -228,6 +242,7 @@ class DatabaseTracker bool is_initialized_; const bool is_incognito_; + bool clear_local_state_on_exit_; bool shutting_down_; const FilePath profile_path_; const FilePath db_dir_; @@ -247,6 +262,9 @@ class DatabaseTracker scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; + // The database tracker thread we're supposed to run file IO on. + scoped_refptr<base::MessageLoopProxy> db_tracker_thread_; + // When in incognito mode, store a DELETE_ON_CLOSE handle to each // main DB and journal file that was accessed. When the incognito profile // goes away (or when the browser crashes), all these handles will be diff --git a/webkit/database/database_tracker_unittest.cc b/webkit/database/database_tracker_unittest.cc index f10a8ce..c6404b1 100644 --- a/webkit/database/database_tracker_unittest.cc +++ b/webkit/database/database_tracker_unittest.cc @@ -5,6 +5,8 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/platform_file.h" #include "base/scoped_temp_dir.h" #include "base/time.h" #include "base/utf_string_conversions.h" @@ -184,7 +186,7 @@ class DatabaseTracker_TestHelper_Test { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); scoped_refptr<DatabaseTracker> tracker( - new DatabaseTracker(temp_dir.path(), incognito_mode, + new DatabaseTracker(temp_dir.path(), incognito_mode, false, new TestSpecialStoragePolicy, NULL, NULL)); @@ -287,7 +289,7 @@ class DatabaseTracker_TestHelper_Test { ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); scoped_refptr<DatabaseTracker> tracker( - new DatabaseTracker(temp_dir.path(), incognito_mode, + new DatabaseTracker(temp_dir.path(), incognito_mode, false, new TestSpecialStoragePolicy, NULL, NULL)); @@ -407,14 +409,14 @@ class DatabaseTracker_TestHelper_Test { // Trying to delete an origin with databases in use should fail tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, &database_size); - EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1)); + EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1, false)); origin1_info = tracker->GetCachedOriginInfo(kOrigin1); EXPECT_TRUE(origin1_info); EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1)); tracker->DatabaseClosed(kOrigin1, kDB1); // Delete an origin that doesn't have any database in use - EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1)); + EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1, false)); origins_info.clear(); EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); EXPECT_EQ(size_t(1), origins_info.size()); @@ -438,7 +440,7 @@ class DatabaseTracker_TestHelper_Test { scoped_refptr<TestQuotaManagerProxy> test_quota_proxy( new TestQuotaManagerProxy); scoped_refptr<DatabaseTracker> tracker( - new DatabaseTracker(temp_dir.path(), false /* incognito */, + new DatabaseTracker(temp_dir.path(), false /* incognito */, false, NULL, test_quota_proxy, NULL)); EXPECT_TRUE(test_quota_proxy->registered_client_); @@ -516,6 +518,102 @@ class DatabaseTracker_TestHelper_Test { crashed_renderer_connections.RemoveAllConnections(); test_quota_proxy->SimulateQuotaManagerDestroyed(); } + + static void DatabaseTrackerClearLocalStateOnExit() { + int64 database_size = 0; + const string16 kOrigin1 = + DatabaseUtil::GetOriginIdentifier(GURL(kOrigin1Url)); + const string16 kOrigin2 = + DatabaseUtil::GetOriginIdentifier(GURL(kOrigin2Url)); + const string16 kDB1 = ASCIIToUTF16("db1"); + const string16 kDB2 = ASCIIToUTF16("db2"); + const string16 kDB3 = ASCIIToUTF16("db3"); + const string16 kDescription = ASCIIToUTF16("database_description"); + + // Initialize the tracker database. + ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath origin1_db_dir; + { + scoped_refptr<DatabaseTracker> tracker( + new DatabaseTracker( + temp_dir.path(), false, true, + new TestSpecialStoragePolicy, + NULL, + base::MessageLoopProxy::CreateForCurrentThread())); + + // Open three new databases. + tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, + &database_size); + EXPECT_EQ(0, database_size); + tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, + &database_size); + EXPECT_EQ(0, database_size); + tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0, + &database_size); + EXPECT_EQ(0, database_size); + + // Write some data to each file. + FilePath db_file; + db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1); + EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); + EXPECT_TRUE(EnsureFileOfSize(db_file, 1)); + + db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2); + EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); + EXPECT_TRUE(EnsureFileOfSize(db_file, 2)); + + db_file = tracker->GetFullDBFilePath(kOrigin1, kDB3); + EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName())); + EXPECT_TRUE(EnsureFileOfSize(db_file, 3)); + + // Store the origin database directory as long as it still exists. + origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB3).DirName(); + + tracker->DatabaseModified(kOrigin1, kDB1); + tracker->DatabaseModified(kOrigin2, kDB2); + tracker->DatabaseModified(kOrigin1, kDB3); + + // Close all databases but one database. + tracker->DatabaseClosed(kOrigin1, kDB1); + tracker->DatabaseClosed(kOrigin2, kDB2); + + // Keep an open file handle to the last database. + base::PlatformFile file_handle = base::CreatePlatformFile( + tracker->GetFullDBFilePath(kOrigin1, kDB3), + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_EXCLUSIVE_WRITE | + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_SHARE_DELETE, + NULL, NULL); + + tracker->Shutdown(); + + base::ClosePlatformFile(file_handle); + tracker->DatabaseClosed(kOrigin1, kDB3); + } + + // At this point, the database tracker should be gone. Create a new one. + scoped_refptr<DatabaseTracker> tracker( + new DatabaseTracker(temp_dir.path(), false, false, + new TestSpecialStoragePolicy, + NULL, NULL)); + + // Get all data for all origins. + std::vector<OriginInfo> origins_info; + EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info)); + EXPECT_EQ(size_t(1), origins_info.size()); + EXPECT_EQ(kOrigin2, origins_info[0].GetOrigin()); + EXPECT_EQ(FilePath(), tracker->GetFullDBFilePath(kOrigin1, kDB1)); + EXPECT_TRUE( + file_util::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); + EXPECT_EQ(FilePath(), tracker->GetFullDBFilePath(kOrigin1, kDB3)); + + // The origin directory should be gone as well. + EXPECT_FALSE(file_util::PathExists(origin1_db_dir)); + } }; TEST(DatabaseTrackerTest, DeleteOpenDatabase) { @@ -539,4 +637,9 @@ TEST(DatabaseTrackerTest, DatabaseTrackerQuotaIntegration) { DatabaseTracker_TestHelper_Test::DatabaseTrackerQuotaIntegration(); } +TEST(DatabaseTrackerTest, DatabaseTrackerClearLocalStateOnExit) { + // Only works for regular mode. + DatabaseTracker_TestHelper_Test::DatabaseTrackerClearLocalStateOnExit(); +} + } // namespace webkit_database diff --git a/webkit/database/vfs_backend.cc b/webkit/database/vfs_backend.cc index b3879ce..774180e 100644 --- a/webkit/database/vfs_backend.cc +++ b/webkit/database/vfs_backend.cc @@ -103,6 +103,10 @@ void VfsBackend::OpenFile(const FilePath& file_path, base::PLATFORM_FILE_DELETE_ON_CLOSE; } + // This flag will allow us to delete the file later on from the browser + // process. + flags |= base::PLATFORM_FILE_SHARE_DELETE; + // Try to open/create the DB file. *file_handle = base::CreatePlatformFile(file_path, flags, NULL, NULL); diff --git a/webkit/support/simple_database_system.cc b/webkit/support/simple_database_system.cc index da5f688..73a2452 100644 --- a/webkit/support/simple_database_system.cc +++ b/webkit/support/simple_database_system.cc @@ -36,7 +36,8 @@ SimpleDatabaseSystem::SimpleDatabaseSystem() DCHECK(!instance_); instance_ = this; CHECK(temp_dir_.CreateUniqueTempDir()); - db_tracker_ = new DatabaseTracker(temp_dir_.path(), false, NULL, NULL, NULL); + db_tracker_ = + new DatabaseTracker(temp_dir_.path(), false, false, NULL, NULL, NULL); db_tracker_->AddObserver(this); db_thread_.Start(); db_thread_proxy_ = db_thread_.message_loop_proxy(); |