summaryrefslogtreecommitdiffstats
path: root/webkit/database
diff options
context:
space:
mode:
authorjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-08 08:00:50 +0000
committerjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-08 08:00:50 +0000
commit800ad566b10caee04ca18c7eb1796b21c1766c37 (patch)
tree276a35a8565d9bb981bb4ec656db26be3e8bce14 /webkit/database
parente6487b915297e168448ea29d81421a3f72a26c10 (diff)
downloadchromium_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/database')
-rw-r--r--webkit/database/database_quota_client_unittest.cc2
-rw-r--r--webkit/database/database_tracker.cc150
-rw-r--r--webkit/database/database_tracker.h30
-rw-r--r--webkit/database/database_tracker_unittest.cc113
-rw-r--r--webkit/database/vfs_backend.cc4
5 files changed, 242 insertions, 57 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);