summaryrefslogtreecommitdiffstats
path: root/webkit/database
diff options
context:
space:
mode:
authorjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-18 10:02:26 +0000
committerjochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-18 10:02:26 +0000
commitec3d1456bdbfd7d7806e51dd1e59088f691495ec (patch)
tree7f85308cde8eab9ffebcb4910894c184060ba15f /webkit/database
parent4236df757e45014700da962b9a20d46f61b4440f (diff)
downloadchromium_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.cc103
-rw-r--r--webkit/database/database_tracker.h44
-rw-r--r--webkit/database/database_tracker_unittest.cc106
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));