summaryrefslogtreecommitdiffstats
path: root/webkit/database
diff options
context:
space:
mode:
authordumi@chromium.org <dumi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-06 21:15:55 +0000
committerdumi@chromium.org <dumi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-06 21:15:55 +0000
commit3ccfe535658c8852c1cc1df01372ecc602f376cd (patch)
tree47582af5ba2a4f0658126d564d618c18a93173cf /webkit/database
parent6d8e2a64b6961331816689850192a8ee864f9a97 (diff)
downloadchromium_src-3ccfe535658c8852c1cc1df01372ecc602f376cd.zip
chromium_src-3ccfe535658c8852c1cc1df01372ecc602f376cd.tar.gz
chromium_src-3ccfe535658c8852c1cc1df01372ecc602f376cd.tar.bz2
Adding methods that will be used by the quota management UI.
TEST=none BUG=none Review URL: http://codereview.chromium.org/507014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35651 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/database')
-rw-r--r--webkit/database/database_connections.cc78
-rw-r--r--webkit/database/database_connections.h41
-rw-r--r--webkit/database/database_tracker.cc176
-rw-r--r--webkit/database/database_tracker.h84
-rw-r--r--webkit/database/database_tracker_unittest.cc123
-rw-r--r--webkit/database/databases_table.cc25
-rw-r--r--webkit/database/databases_table.h2
-rw-r--r--webkit/database/databases_table_unittest.cc28
-rw-r--r--webkit/database/quota_table.cc63
-rw-r--r--webkit/database/quota_table.h33
-rw-r--r--webkit/database/quota_table_unittest.cc65
11 files changed, 622 insertions, 96 deletions
diff --git a/webkit/database/database_connections.cc b/webkit/database/database_connections.cc
new file mode 100644
index 0000000..a6e6d3d
--- /dev/null
+++ b/webkit/database/database_connections.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2009 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.
+
+#include "webkit/database/database_connections.h"
+
+#include "base/logging.h"
+
+namespace webkit_database {
+
+DatabaseConnections::DatabaseConnections() {
+}
+
+DatabaseConnections::~DatabaseConnections() {
+ DCHECK(connections_.empty());
+}
+
+bool DatabaseConnections::IsDatabaseOpened(const string16& origin_identifier,
+ const string16& database_name) {
+ OriginConnections::const_iterator origin_it =
+ connections_.find(origin_identifier);
+ if (origin_it == connections_.end())
+ return false;
+ const DBConnections& origin_connections = origin_it->second;
+ return (origin_connections.find(database_name) != origin_connections.end());
+}
+
+bool DatabaseConnections::IsOriginUsed(const string16& origin_identifier) {
+ return (connections_.find(origin_identifier) != connections_.end());
+}
+
+void DatabaseConnections::AddConnection(const string16& origin_identifier,
+ const string16& database_name) {
+ connections_[origin_identifier][database_name]++;
+}
+
+void DatabaseConnections::RemoveConnection(const string16& origin_identifier,
+ const string16& database_name) {
+ RemoveConnectionsHelper(origin_identifier, database_name, 1);
+}
+
+void DatabaseConnections::RemoveAllConnections() {
+ connections_.clear();
+}
+
+void DatabaseConnections::RemoveConnections(
+ const DatabaseConnections& connections) {
+ for (OriginConnections::const_iterator origin_it =
+ connections.connections_.begin();
+ origin_it != connections.connections_.end();
+ origin_it++) {
+ const DBConnections& db_connections = origin_it->second;
+ for (DBConnections::const_iterator db_it = db_connections.begin();
+ db_it != db_connections.end(); db_it++) {
+ RemoveConnectionsHelper(origin_it->first, db_it->first, db_it->second);
+ }
+ }
+}
+
+void DatabaseConnections::RemoveConnectionsHelper(
+ const string16& origin_identifier,
+ const string16& database_name,
+ int num_connections) {
+ OriginConnections::iterator origin_iterator =
+ connections_.find(origin_identifier);
+ DCHECK(origin_iterator != connections_.end());
+ DBConnections& db_connections = origin_iterator->second;
+ int& count = db_connections[database_name];
+ DCHECK(count >= num_connections);
+ count -= num_connections;
+ if (!count) {
+ db_connections.erase(database_name);
+ if (db_connections.empty())
+ connections_.erase(origin_iterator);
+ }
+}
+
+} // namespace webkit_database
diff --git a/webkit/database/database_connections.h b/webkit/database/database_connections.h
new file mode 100644
index 0000000..e036940
--- /dev/null
+++ b/webkit/database/database_connections.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 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.
+
+#ifndef WEBKIT_DATABASE_DATABASE_CONNECTIONS_H_
+#define WEBKIT_DATABASE_DATABASE_CONNECTIONS_H_
+
+#include "base/string16.h"
+
+#include <map>
+
+namespace webkit_database {
+
+class DatabaseConnections {
+ public:
+ DatabaseConnections();
+ ~DatabaseConnections();
+
+ bool IsDatabaseOpened(const string16& origin_identifier,
+ const string16& database_name);
+ bool IsOriginUsed(const string16& origin_identifier);
+ void AddConnection(const string16& origin_identifier,
+ const string16& database_name);
+ void RemoveConnection(const string16& origin_identifier,
+ const string16& database_name);
+ void RemoveAllConnections();
+ void RemoveConnections(const DatabaseConnections& connections);
+
+ private:
+ typedef std::map<string16, int> DBConnections;
+ typedef std::map<string16, DBConnections> OriginConnections;
+ OriginConnections connections_;
+
+ void RemoveConnectionsHelper(const string16& origin_identifier,
+ const string16& database_name,
+ int num_connections);
+};
+
+} // namespace webkit_database
+
+#endif // WEBKIT_DATABASE_DATABASE_CONNECTIONS_H_
diff --git a/webkit/database/database_tracker.cc b/webkit/database/database_tracker.cc
index efece6d6..16766b4 100644
--- a/webkit/database/database_tracker.cc
+++ b/webkit/database/database_tracker.cc
@@ -9,11 +9,13 @@
#include "app/sql/connection.h"
#include "app/sql/meta_table.h"
#include "app/sql/statement.h"
+#include "app/sql/transaction.h"
#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "webkit/database/databases_table.h"
+#include "webkit/database/quota_table.h"
namespace webkit_database {
@@ -21,7 +23,7 @@ const FilePath::CharType kDatabaseDirectoryName[] =
FILE_PATH_LITERAL("databases");
const FilePath::CharType kTrackerDatabaseFileName[] =
FILE_PATH_LITERAL("Databases.db");
-const int kCurrentVersion = 1;
+const int kCurrentVersion = 2;
const int kCompatibleVersion = 1;
const int64 kDefaultQuota = 5 * 1024 * 1024;
const int64 kDefaultExtensionQuota = 50 * 1024 * 1024;
@@ -53,8 +55,10 @@ void DatabaseTracker::DatabaseOpened(const string16& origin_identifier,
InsertOrUpdateDatabaseDetails(origin_identifier, database_name,
database_description, estimated_size);
+ database_connections_.AddConnection(origin_identifier, database_name);
- *database_size = GetCachedDatabaseFileSize(origin_identifier, database_name);
+ CachedOriginInfo* info = GetCachedOriginInfo(origin_identifier);
+ *database_size = (info ? info->GetDatabaseSize(database_name) : 0);
*space_available = GetOriginSpaceAvailable(origin_identifier);
}
@@ -72,7 +76,11 @@ void DatabaseTracker::DatabaseModified(const string16& origin_identifier,
void DatabaseTracker::DatabaseClosed(const string16& origin_identifier,
const string16& database_name) {
- // TODO(dumi): figure out how to use this information at a later time
+ database_connections_.RemoveConnection(origin_identifier, database_name);
+}
+
+void DatabaseTracker::CloseDatabases(const DatabaseConnections& connections) {
+ database_connections_.RemoveConnections(connections);
}
void DatabaseTracker::AddObserver(Observer* observer) {
@@ -91,6 +99,7 @@ void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() {
ClearAllCachedOriginInfo();
meta_table_.reset(NULL);
databases_table_.reset(NULL);
+ quota_table_.reset(NULL);
db_->Close();
initialized_ = false;
}
@@ -110,36 +119,101 @@ FilePath DatabaseTracker::GetFullDBFilePath(
UTF16ToWide(origin_identifier))).Append(file_name);
}
+bool DatabaseTracker::GetAllOriginsInfo(std::vector<OriginInfo>* origins_info) {
+ DCHECK(origins_info);
+ DCHECK(origins_info->empty());
+ std::vector<string16> origins;
+ if (!databases_table_->GetAllOrigins(&origins))
+ return false;
+
+ for (std::vector<string16>::const_iterator it = origins.begin();
+ it != origins.end(); it++) {
+ CachedOriginInfo* origin_info = GetCachedOriginInfo(*it);
+ if (!origin_info) {
+ // Restore 'origins_info' to its initial state.
+ origins_info->clear();
+ return false;
+ }
+ origins_info->push_back(OriginInfo(*origin_info));
+ }
+
+ return true;
+}
+
+void DatabaseTracker::SetOriginQuota(const string16& origin_identifier,
+ int64 new_quota) {
+ if (quota_table_->SetOriginQuota(origin_identifier, new_quota) &&
+ (origins_info_map_.find(origin_identifier) != origins_info_map_.end())) {
+ origins_info_map_[origin_identifier].SetQuota(new_quota);
+ }
+}
+
+bool DatabaseTracker::DeleteDatabase(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.
+ FilePath db_file = GetFullDBFilePath(origin_identifier, database_name);
+ if (file_util::PathExists(db_file) && !file_util::Delete(db_file, false))
+ return false;
+
+ // Clean up the main database and invalidate the cached record.
+ databases_table_->DeleteDatabaseDetails(origin_identifier, database_name);
+ origins_info_map_.erase(origin_identifier);
+ return true;
+}
+
+bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) {
+ // Check if any database in this origin is opened by any renderer.
+ if (database_connections_.IsOriginUsed(origin_identifier))
+ return false;
+
+ // 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;
+
+ databases_table_->DeleteOrigin(origin_identifier);
+ return true;
+}
+
bool DatabaseTracker::LazyInit() {
if (!initialized_) {
DCHECK(!db_->is_open());
DCHECK(!databases_table_.get());
+ DCHECK(!quota_table_.get());
DCHECK(!meta_table_.get());
// If the tracker database exists, but it's corrupt or doesn't
- // have a meta table, delete the database directory
+ // have a meta table, delete the database directory.
const FilePath kTrackerDatabaseFullPath =
db_dir_.Append(FilePath(kTrackerDatabaseFileName));
if (file_util::DirectoryExists(db_dir_) &&
file_util::PathExists(kTrackerDatabaseFullPath) &&
(!db_->Open(kTrackerDatabaseFullPath) ||
- !db_->DoesTableExist("meta"))) {
+ !sql::MetaTable::DoesTableExist(db_.get()))) {
db_->Close();
if (!file_util::Delete(db_dir_, true))
return false;
}
databases_table_.reset(new DatabasesTable(db_.get()));
+ quota_table_.reset(new QuotaTable(db_.get()));
meta_table_.reset(new sql::MetaTable());
initialized_ =
file_util::CreateDirectory(db_dir_) &&
(db_->is_open() || db_->Open(kTrackerDatabaseFullPath)) &&
- meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) &&
- (meta_table_->GetCompatibleVersionNumber() <= kCurrentVersion) &&
- databases_table_->Init();
+ UpgradeToCurrentVersion();
if (!initialized_) {
databases_table_.reset(NULL);
+ quota_table_.reset(NULL);
meta_table_.reset(NULL);
db_->Close();
}
@@ -147,6 +221,21 @@ bool DatabaseTracker::LazyInit() {
return initialized_;
}
+bool DatabaseTracker::UpgradeToCurrentVersion() {
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin() ||
+ !meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) ||
+ (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) ||
+ !databases_table_->Init() ||
+ !quota_table_->Init())
+ return false;
+
+ if (meta_table_->GetVersionNumber() < kCurrentVersion)
+ meta_table_->SetVersionNumber(kCurrentVersion);
+
+ return transaction.Commit();
+}
+
void DatabaseTracker::InsertOrUpdateDatabaseDetails(
const string16& origin_identifier,
const string16& database_name,
@@ -168,21 +257,15 @@ void DatabaseTracker::InsertOrUpdateDatabaseDetails(
}
}
-int64 DatabaseTracker::GetDBFileSize(const string16& origin_identifier,
- const string16& database_name) const {
- FilePath db_file_name = GetFullDBFilePath(origin_identifier, database_name);
- int64 db_file_size = 0;
- if (!file_util::GetFileSize(db_file_name, &db_file_size))
- db_file_size = 0;
- return db_file_size;
-}
-
void DatabaseTracker::ClearAllCachedOriginInfo() {
origins_info_map_.clear();
}
DatabaseTracker::CachedOriginInfo* DatabaseTracker::GetCachedOriginInfo(
const string16& origin_identifier) {
+ if (!LazyInit())
+ return NULL;
+
// Populate the cache with data for this origin if needed.
if (origins_info_map_.find(origin_identifier) == origins_info_map_.end()) {
std::vector<DatabaseDetails> details;
@@ -192,24 +275,45 @@ DatabaseTracker::CachedOriginInfo* DatabaseTracker::GetCachedOriginInfo(
}
CachedOriginInfo& origin_info = origins_info_map_[origin_identifier];
+ origin_info.SetOrigin(origin_identifier);
for (std::vector<DatabaseDetails>::const_iterator it = details.begin();
it != details.end(); it++) {
int64 db_file_size =
- GetDBFileSize(it->origin_identifier, it->database_name);
- origin_info.SetCachedDatabaseSize(it->database_name, db_file_size);
+ GetDBFileSize(origin_identifier, it->database_name);
+ origin_info.SetDatabaseSize(it->database_name, db_file_size);
+ }
+
+ int64 origin_quota = quota_table_->GetOriginQuota(origin_identifier);
+ if (origin_quota > 0) {
+ origin_info.SetQuota(origin_quota);
+ } else if (StartsWith(origin_identifier,
+ ASCIIToUTF16(kExtensionOriginIdentifierPrefix),
+ true)) {
+ origin_info.SetQuota(kDefaultExtensionQuota);
+ } else {
+ origin_info.SetQuota(kDefaultQuota);
}
}
return &origins_info_map_[origin_identifier];
}
-int64 DatabaseTracker::GetCachedDatabaseFileSize(
- const string16& origin_identifier,
- const string16& database_name) {
+int64 DatabaseTracker::GetDBFileSize(const string16& origin_identifier,
+ const string16& database_name) const {
+ FilePath db_file_name = GetFullDBFilePath(origin_identifier, database_name);
+ int64 db_file_size = 0;
+ if (!file_util::GetFileSize(db_file_name, &db_file_size))
+ db_file_size = 0;
+ return db_file_size;
+}
+
+int64 DatabaseTracker::GetOriginSpaceAvailable(
+ const string16& origin_identifier) {
CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
if (!origin_info)
return 0;
- return origin_info->GetCachedDatabaseSize(database_name);
+ int64 space_available = origin_info->Quota() - origin_info->TotalSize();
+ return (space_available < 0 ? 0 : space_available);
}
int64 DatabaseTracker::UpdateCachedDatabaseFileSize(
@@ -218,32 +322,8 @@ int64 DatabaseTracker::UpdateCachedDatabaseFileSize(
int64 new_size = GetDBFileSize(origin_identifier, database_name);
CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
if (origin_info)
- origin_info->SetCachedDatabaseSize(database_name, new_size);
+ origin_info->SetDatabaseSize(database_name, new_size);
return new_size;
}
-int64 DatabaseTracker::GetOriginUsage(const string16& origin_identifier) {
- CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
- if (!origin_info)
- return kint64max;
- return origin_info->TotalSize();
-}
-
-int64 DatabaseTracker::GetOriginQuota(
- const string16& origin_identifier) const {
- if (StartsWith(origin_identifier,
- ASCIIToUTF16(kExtensionOriginIdentifierPrefix), true)) {
- return kDefaultExtensionQuota;
- }
-
- return kDefaultQuota;
-}
-
-int64 DatabaseTracker::GetOriginSpaceAvailable(
- const string16& origin_identifier) {
- int64 space_available = GetOriginQuota(origin_identifier) -
- GetOriginUsage(origin_identifier);
- return (space_available < 0 ? 0 : space_available);
-}
-
} // namespace webkit_database
diff --git a/webkit/database/database_tracker.h b/webkit/database/database_tracker.h
index 2bcfc6f..8d65109 100644
--- a/webkit/database/database_tracker.h
+++ b/webkit/database/database_tracker.h
@@ -12,7 +12,9 @@
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
+#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "webkit/database/database_connections.h"
namespace sql {
class Connection;
@@ -22,6 +24,42 @@ class MetaTable;
namespace webkit_database {
class DatabasesTable;
+class QuotaTable;
+
+// This class is used to store information about all databases in an origin.
+class OriginInfo {
+ public:
+ OriginInfo(const OriginInfo& origin_info)
+ : origin_(origin_info.origin_),
+ total_size_(origin_info.total_size_),
+ quota_(origin_info.quota_),
+ database_sizes_(origin_info.database_sizes_) {}
+ string16 GetOrigin() const { return origin_; }
+ int64 TotalSize() const { return total_size_; }
+ int64 Quota() const { return quota_; }
+ void GetAllDatabaseNames(std::vector<string16>* databases) const {
+ for (std::map<string16, int64>::const_iterator it = database_sizes_.begin();
+ it != database_sizes_.end(); it++) {
+ databases->push_back(it->first);
+ }
+ }
+ int64 GetDatabaseSize(const string16& database_name) const {
+ std::map<string16, int64>::const_iterator it =
+ database_sizes_.find(database_name);
+ if (it != database_sizes_.end())
+ return it->second;
+ return 0;
+ }
+
+ protected:
+ OriginInfo(const string16& origin, int64 total_size, int64 quota)
+ : origin_(origin), total_size_(total_size), quota_(quota) { }
+
+ string16 origin_;
+ int64 total_size_;
+ int64 quota_;
+ std::map<string16, int64> database_sizes_;
+};
// This class manages the main database, and keeps track of per origin quotas.
//
@@ -58,6 +96,7 @@ class DatabaseTracker
const string16& database_name);
void DatabaseClosed(const string16& origin_identifier,
const string16& database_name);
+ void CloseDatabases(const DatabaseConnections& connections);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
@@ -68,31 +107,33 @@ class DatabaseTracker
FilePath GetFullDBFilePath(const string16& origin_identifier,
const string16& database_name) const;
+ 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);
+
private:
+ // Need this here to allow RefCountedThreadSafe to call ~DatabaseTracker().
friend class base::RefCountedThreadSafe<DatabaseTracker>;
- ~DatabaseTracker();
-
- class CachedOriginInfo {
+ class CachedOriginInfo : public OriginInfo {
public:
- CachedOriginInfo() : total_size_(0) { }
- int64 TotalSize() const { return total_size_; }
- int64 GetCachedDatabaseSize(const string16& database_name) {
- return cached_database_sizes_[database_name];
- }
- void SetCachedDatabaseSize(const string16& database_name, int64 new_size) {
- int64 old_size = cached_database_sizes_[database_name];
- cached_database_sizes_[database_name] = new_size;
+ CachedOriginInfo() : OriginInfo(EmptyString16(), 0, 0) {}
+ void SetOrigin(const string16& origin) { origin_ = origin; }
+ void SetQuota(int64 new_quota) { quota_ = new_quota; }
+ void SetDatabaseSize(const string16& database_name, int64 new_size) {
+ int64 old_size = database_sizes_[database_name];
+ database_sizes_[database_name] = new_size;
if (new_size != old_size)
total_size_ += new_size - old_size;
}
-
- private:
- int64 total_size_;
- std::map<string16, int64> cached_database_sizes_;
};
+ ~DatabaseTracker();
+
bool LazyInit();
+ bool UpgradeToCurrentVersion();
void InsertOrUpdateDatabaseDetails(const string16& origin_identifier,
const string16& database_name,
const string16& database_details,
@@ -100,23 +141,24 @@ class DatabaseTracker
void ClearAllCachedOriginInfo();
CachedOriginInfo* GetCachedOriginInfo(const string16& origin_identifier);
- int64 GetCachedDatabaseFileSize(const string16& origin_identifier,
- const string16& database_name);
- int64 UpdateCachedDatabaseFileSize(const string16& origin_identifier,
- const string16& database_name);
+
int64 GetDBFileSize(const string16& origin_identifier,
const string16& database_name) const;
- int64 GetOriginUsage(const string16& origin_identifer);
- int64 GetOriginQuota(const string16& origin_identifier) const;
+
int64 GetOriginSpaceAvailable(const string16& origin_identifier);
+ int64 UpdateCachedDatabaseFileSize(const string16& origin_identifier,
+ const string16& database_name);
+
bool initialized_;
const FilePath db_dir_;
scoped_ptr<sql::Connection> db_;
scoped_ptr<DatabasesTable> databases_table_;
+ scoped_ptr<QuotaTable> quota_table_;
scoped_ptr<sql::MetaTable> meta_table_;
ObserverList<Observer> observers_;
std::map<string16, CachedOriginInfo> origins_info_map_;
+ DatabaseConnections database_connections_;
FRIEND_TEST(DatabaseTrackerTest, TestIt);
};
diff --git a/webkit/database/database_tracker_unittest.cc b/webkit/database/database_tracker_unittest.cc
index cf9fb5d..8b44a451 100644
--- a/webkit/database/database_tracker_unittest.cc
+++ b/webkit/database/database_tracker_unittest.cc
@@ -70,9 +70,6 @@ TEST(DatabaseTrackerTest, TestIt) {
EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<DatabaseTracker> tracker(new DatabaseTracker(temp_dir.path()));
- // Get the default quota for all origins.
- const int64 kDefaultQuota = tracker->GetOriginQuota(EmptyString16());
-
// Add two observers.
TestObserver observer1;
TestObserver observer2;
@@ -89,24 +86,43 @@ TEST(DatabaseTrackerTest, TestIt) {
const string16 kDB3 = ASCIIToUTF16("db3");
const string16 kDescription = ASCIIToUTF16("database_description");
+ // Get the quota for kOrigin1 and kOrigin2
+ DatabaseTracker::CachedOriginInfo* origin1_info =
+ tracker->GetCachedOriginInfo(kOrigin1);
+ DatabaseTracker::CachedOriginInfo* origin2_info =
+ tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_TRUE(origin2_info);
+ int64 origin1_quota = origin1_info->Quota();
+ int64 origin2_quota = origin2_info->Quota();
+ EXPECT_EQ(origin1_quota, tracker->GetOriginSpaceAvailable(kOrigin1));
+ EXPECT_EQ(origin2_quota, tracker->GetOriginSpaceAvailable(kOrigin2));
+
+ // Set a new quota for kOrigin1
+ origin1_quota *= 2;
+ tracker->SetOriginQuota(kOrigin1, origin1_quota);
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(origin1_quota, origin1_info->Quota());
+
tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
&database_size, &space_available);
EXPECT_EQ(0, database_size);
- EXPECT_EQ(kDefaultQuota, space_available);
+ EXPECT_EQ(origin1_quota, space_available);
tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
&database_size, &space_available);
EXPECT_EQ(0, database_size);
- EXPECT_EQ(kDefaultQuota, space_available);
+ EXPECT_EQ(origin2_quota, space_available);
tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0,
&database_size, &space_available);
EXPECT_EQ(0, database_size);
- EXPECT_EQ(kDefaultQuota, space_available);
+ EXPECT_EQ(origin1_quota, space_available);
// Tell the tracker that a database has changed.
// Even though nothing has changed, the observers should be notified.
tracker->DatabaseModified(kOrigin1, kDB1);
- CheckNotificationReceived(&observer1, kOrigin1, kDB1, 0, kDefaultQuota);
- CheckNotificationReceived(&observer2, kOrigin1, kDB1, 0, kDefaultQuota);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB1, 0, origin1_quota);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB1, 0, origin1_quota);
// Write some data to each file and check that the listeners are
// called with the appropriate values.
@@ -121,28 +137,38 @@ TEST(DatabaseTrackerTest, TestIt) {
EXPECT_EQ(4, file_util::WriteFile(
tracker->GetFullDBFilePath(kOrigin1, kDB3), "aaaa", 4));
tracker->DatabaseModified(kOrigin1, kDB1);
- CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1, kDefaultQuota - 1);
- CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1, kDefaultQuota - 1);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1, origin1_quota - 1);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1, origin1_quota - 1);
tracker->DatabaseModified(kOrigin2, kDB2);
- CheckNotificationReceived(&observer1, kOrigin2, kDB2, 2, kDefaultQuota - 2);
- CheckNotificationReceived(&observer2, kOrigin2, kDB2, 2, kDefaultQuota - 2);
+ CheckNotificationReceived(&observer1, kOrigin2, kDB2, 2, origin2_quota - 2);
+ CheckNotificationReceived(&observer2, kOrigin2, kDB2, 2, origin2_quota - 2);
tracker->DatabaseModified(kOrigin1, kDB3);
- CheckNotificationReceived(&observer1, kOrigin1, kDB3, 4, kDefaultQuota - 5);
- CheckNotificationReceived(&observer2, kOrigin1, kDB3, 4, kDefaultQuota - 5);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB3, 4, origin1_quota - 5);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB3, 4, origin1_quota - 5);
+
+ // Make sure the available space for kOrigin1 and kOrigin2 changed accordingly
+ EXPECT_EQ(origin1_quota - 5, tracker->GetOriginSpaceAvailable(kOrigin1));
+ EXPECT_EQ(origin2_quota - 2, tracker->GetOriginSpaceAvailable(kOrigin2));
+
+ // Close all databases
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+ tracker->DatabaseClosed(kOrigin2, kDB2);
+ tracker->DatabaseClosed(kOrigin1, kDB3);
// Open an existing database and check the reported size
tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
&database_size, &space_available);
EXPECT_EQ(1, database_size);
- EXPECT_EQ(kDefaultQuota - 5, space_available);
+ EXPECT_EQ(origin1_quota - 5, space_available);
// Make sure that the observers are notified even if
// the size of the database hasn't changed.
EXPECT_EQ(1, file_util::WriteFile(
tracker->GetFullDBFilePath(kOrigin1, kDB1), "b", 1));
tracker->DatabaseModified(kOrigin1, kDB1);
- CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1, kDefaultQuota - 5);
- CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1, kDefaultQuota - 5);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1, origin1_quota - 5);
+ CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1, origin1_quota - 5);
+ tracker->DatabaseClosed(kOrigin1, kDB1);
// Remove an observer; this should clear all caches.
tracker->RemoveObserver(&observer2);
@@ -156,8 +182,9 @@ TEST(DatabaseTrackerTest, TestIt) {
EXPECT_EQ(6, file_util::WriteFile(
tracker->GetFullDBFilePath(kOrigin1, kDB3), "dddddd", 6));
tracker->DatabaseModified(kOrigin1, kDB1);
- CheckNotificationReceived(&observer1, kOrigin1, kDB1, 5, kDefaultQuota - 11);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB1, 5, origin1_quota - 11);
EXPECT_FALSE(observer2.DidReceiveNewNotification());
+ EXPECT_EQ(origin1_quota - 11, tracker->GetOriginSpaceAvailable(kOrigin1));
// Close the tracker database and clear all caches.
// Then make sure that DatabaseOpened() still returns the correct result.
@@ -165,16 +192,70 @@ TEST(DatabaseTrackerTest, TestIt) {
tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
&database_size, &space_available);
EXPECT_EQ(5, database_size);
- EXPECT_EQ(kDefaultQuota - 11, space_available);
+ EXPECT_EQ(origin1_quota - 11, space_available);
// Close the tracker database and clear all caches. Then make sure that
// DatabaseModified() still calls the observers with correct values.
tracker->CloseTrackerDatabaseAndClearCaches();
tracker->DatabaseModified(kOrigin1, kDB3);
- CheckNotificationReceived(&observer1, kOrigin1, kDB3, 6, kDefaultQuota - 11);
+ CheckNotificationReceived(&observer1, kOrigin1, kDB3, 6, origin1_quota - 11);
+ tracker->DatabaseClosed(kOrigin1, kDB1);
- // Clean up.
+ // Remove all observers.
tracker->RemoveObserver(&observer1);
+
+ // 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));
+ 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));
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(origin1_quota - 5, tracker->GetOriginSpaceAvailable(kOrigin1));
+ EXPECT_EQ(5, origin1_info->GetDatabaseSize(kDB1));
+ EXPECT_EQ(0, origin1_info->GetDatabaseSize(kDB3));
+
+ // Get all data for all origins
+ std::vector<OriginInfo> origins_info;
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ EXPECT_EQ(size_t(2), origins_info.size());
+ EXPECT_EQ(kOrigin1, origins_info[0].GetOrigin());
+ EXPECT_EQ(5, origins_info[0].TotalSize());
+ EXPECT_EQ(origin1_quota, origins_info[0].Quota());
+ EXPECT_EQ(5, origins_info[0].GetDatabaseSize(kDB1));
+ EXPECT_EQ(0, origins_info[0].GetDatabaseSize(kDB3));
+
+ EXPECT_EQ(kOrigin2, origins_info[1].GetOrigin());
+ EXPECT_EQ(2, origins_info[1].TotalSize());
+ EXPECT_EQ(origin2_quota, origins_info[1].Quota());
+
+ // Trying to delete an origin with databases in use should fail
+ tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
+ &database_size, &space_available);
+ EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1));
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(5, origin1_info->GetDatabaseSize(kDB1));
+ tracker->DatabaseClosed(kOrigin1, kDB1);
+
+ // Delete an origin that doesn't have any database in use
+ EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1));
+ origins_info.clear();
+ EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
+ EXPECT_EQ(size_t(1), origins_info.size());
+ EXPECT_EQ(kOrigin2, origins_info[0].GetOrigin());
+
+ origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
+ EXPECT_TRUE(origin1_info);
+ EXPECT_EQ(origin1_quota, origin1_info->Quota());
+ EXPECT_EQ(0, origin1_info->TotalSize());
+ EXPECT_EQ(origin1_quota, tracker->GetOriginSpaceAvailable(kOrigin1));
}
} // namespace webkit_database
diff --git a/webkit/database/databases_table.cc b/webkit/database/databases_table.cc
index 132e7ad..a987522 100644
--- a/webkit/database/databases_table.cc
+++ b/webkit/database/databases_table.cc
@@ -112,12 +112,24 @@ bool DatabasesTable::DeleteDatabaseDetails(const string16& origin_identifier,
return false;
}
+bool DatabasesTable::GetAllOrigins(std::vector<string16>* origins) {
+ sql::Statement statement(db_->GetCachedStatement(
+ SQL_FROM_HERE, "SELECT DISTINCT origin FROM Databases ORDER BY origin"));
+ if (statement.is_valid()) {
+ while (statement.Step())
+ origins->push_back(UTF8ToUTF16(statement.ColumnString(0)));
+ return statement.Succeeded();
+ }
+
+ return false;
+}
+
bool DatabasesTable::GetAllDatabaseDetailsForOrigin(
const string16& origin_identifier,
std::vector<DatabaseDetails>* details_vector) {
sql::Statement statement(db_->GetCachedStatement(
SQL_FROM_HERE, "SELECT name, description, estimated_size "
- "FROM Databases WHERE origin = ? ORDER BY origin, name"));
+ "FROM Databases WHERE origin = ? ORDER BY name"));
if (statement.is_valid() &&
statement.BindString(0, UTF16ToUTF8(origin_identifier))) {
while (statement.Step()) {
@@ -134,4 +146,15 @@ bool DatabasesTable::GetAllDatabaseDetailsForOrigin(
return false;
}
+bool DatabasesTable::DeleteOrigin(const string16& origin_identifier) {
+ sql::Statement delete_statement(db_->GetCachedStatement(
+ SQL_FROM_HERE, "DELETE FROM Databases WHERE origin = ?"));
+ if (delete_statement.is_valid() &&
+ delete_statement.BindString(0, UTF16ToUTF8(origin_identifier))) {
+ return (delete_statement.Run() && db_->GetLastChangeCount());
+ }
+
+ return false;
+}
+
} // namespace webkit_database
diff --git a/webkit/database/databases_table.h b/webkit/database/databases_table.h
index 481489f..4443c76 100644
--- a/webkit/database/databases_table.h
+++ b/webkit/database/databases_table.h
@@ -38,8 +38,10 @@ class DatabasesTable {
bool UpdateDatabaseDetails(const DatabaseDetails& details);
bool DeleteDatabaseDetails(const string16& origin_identifier,
const string16& database_name);
+ bool GetAllOrigins(std::vector<string16>* origins);
bool GetAllDatabaseDetailsForOrigin(const string16& origin_identifier,
std::vector<DatabaseDetails>* details);
+ bool DeleteOrigin(const string16& origin_identifier);
private:
sql::Connection* db_;
};
diff --git a/webkit/database/databases_table_unittest.cc b/webkit/database/databases_table_unittest.cc
index fb46d08..2d72d60 100644
--- a/webkit/database/databases_table_unittest.cc
+++ b/webkit/database/databases_table_unittest.cc
@@ -4,12 +4,11 @@
#include "app/sql/connection.h"
#include "app/sql/statement.h"
-#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/database/databases_table.h"
-namespace webkit_database {
+namespace {
class TestErrorDelegate : public sql::ErrorDelegate {
public:
@@ -20,6 +19,10 @@ class TestErrorDelegate : public sql::ErrorDelegate {
}
};
+} // namespace
+
+namespace webkit_database {
+
static void CheckDetailsAreEqual(const DatabaseDetails& d1,
const DatabaseDetails& d2) {
EXPECT_EQ(d1.origin_identifier, d2.origin_identifier);
@@ -36,7 +39,6 @@ static bool DatabasesTableIsEmpty(sql::Connection* db) {
TEST(DatabasesTableTest, TestIt) {
// Initialize the 'Databases' table.
- ScopedTempDir temp_dir;
sql::Connection db;
// Set an error delegate that will make all operations return false on error.
@@ -44,8 +46,7 @@ TEST(DatabasesTableTest, TestIt) {
db.set_error_delegate(error_delegate);
// Initialize the temp dir and the 'Databases' table.
- EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
- EXPECT_TRUE(db.Open(temp_dir.path().Append(FILE_PATH_LITERAL("tracker.db"))));
+ EXPECT_TRUE(db.OpenInMemory());
DatabasesTable databases_table(&db);
EXPECT_TRUE(databases_table.Init());
@@ -116,6 +117,23 @@ TEST(DatabasesTableTest, TestIt) {
CheckDetailsAreEqual(details_in1, details_out_origin1[0]);
CheckDetailsAreEqual(details_in2, details_out_origin1[1]);
+ // Get the list of all origins: should be "origin1" and "origin2".
+ std::vector<string16> origins_out;
+ EXPECT_TRUE(databases_table.GetAllOrigins(&origins_out));
+ EXPECT_EQ(size_t(2), origins_out.size());
+ EXPECT_EQ(details_in1.origin_identifier, origins_out[0]);
+ EXPECT_EQ(details_in3.origin_identifier, origins_out[1]);
+
+ // Delete an origin and check that it's no longer in the table.
+ origins_out.clear();
+ EXPECT_TRUE(databases_table.DeleteOrigin(details_in3.origin_identifier));
+ EXPECT_TRUE(databases_table.GetAllOrigins(&origins_out));
+ EXPECT_EQ(size_t(1), origins_out.size());
+ EXPECT_EQ(details_in1.origin_identifier, origins_out[0]);
+
+ // Deleting an origin that doesn't have any record in this table should fail.
+ EXPECT_FALSE(databases_table.DeleteOrigin(ASCIIToUTF16("unknown_origin")));
+
// Delete the details for 'db1' and check that they're no longer there.
EXPECT_TRUE(databases_table.DeleteDatabaseDetails(
details_in1.origin_identifier, details_in1.database_name));
diff --git a/webkit/database/quota_table.cc b/webkit/database/quota_table.cc
new file mode 100644
index 0000000..2b6507e
--- /dev/null
+++ b/webkit/database/quota_table.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2009 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.
+
+#include "webkit/database/quota_table.h"
+
+#include "app/sql/connection.h"
+#include "app/sql/statement.h"
+#include "base/string_util.h"
+
+namespace webkit_database {
+
+bool QuotaTable::Init() {
+ // 'Quota' schema:
+ // origin The origin.
+ // quota The quota for this origin.
+ return db_->DoesTableExist("Quota") ||
+ db_->Execute(
+ "CREATE TABLE Quota ("
+ "origin TEXT NOT NULL PRIMARY KEY, "
+ "quota INTEGER NOT NULL)");
+}
+
+int64 QuotaTable::GetOriginQuota(const string16& origin_identifier) {
+ sql::Statement statement(db_->GetCachedStatement(
+ SQL_FROM_HERE, "SELECT quota FROM Quota WHERE origin = ?"));
+ if (statement.is_valid() &&
+ statement.BindString(0, UTF16ToUTF8(origin_identifier)) &&
+ statement.Step()) {
+ return statement.ColumnInt64(0);
+ }
+
+ return -1;
+}
+
+bool QuotaTable::SetOriginQuota(const string16& origin_identifier,
+ int64 quota) {
+ DCHECK(quota >= 0);
+
+ // Insert or update the quota for this origin.
+ sql::Statement replace_statement(db_->GetCachedStatement(
+ SQL_FROM_HERE, "REPLACE INTO Quota VALUES (?, ?)"));
+ if (replace_statement.is_valid() &&
+ replace_statement.BindString(0, UTF16ToUTF8(origin_identifier)) &&
+ replace_statement.BindInt64(1, quota)) {
+ return replace_statement.Run();
+ }
+
+ return false;
+}
+
+bool QuotaTable::ClearOriginQuota(const string16& origin_identifier) {
+ sql::Statement statement(db_->GetCachedStatement(
+ SQL_FROM_HERE, "DELETE FROM Quota WHERE origin = ?"));
+ if (statement.is_valid() &&
+ statement.BindString(0, UTF16ToUTF8(origin_identifier))) {
+ return (statement.Run() && db_->GetLastChangeCount());
+ }
+
+ return false;
+}
+
+} // namespace webkit_database
diff --git a/webkit/database/quota_table.h b/webkit/database/quota_table.h
new file mode 100644
index 0000000..8f6e4e8
--- /dev/null
+++ b/webkit/database/quota_table.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 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.
+
+#ifndef WEBKIT_DATABASE_QUOTA_TABLE_H_
+#define WEBKIT_DATABASE_QUOTA_TABLE_H_
+
+#include <vector>
+
+#include "base/string16.h"
+
+namespace sql {
+class Connection;
+}
+
+namespace webkit_database {
+
+class QuotaTable {
+ public:
+ explicit QuotaTable(sql::Connection* db) : db_(db) { }
+
+ bool Init();
+ int64 GetOriginQuota(const string16& origin_identifier);
+ bool SetOriginQuota(const string16& origin_identifier, int64 quota);
+ bool ClearOriginQuota(const string16& origin_identifier);
+
+ private:
+ sql::Connection* db_;
+};
+
+} // namespace webkit_database
+
+#endif // WEBKIT_DATABASE_QUOTA_TABLE_H_
diff --git a/webkit/database/quota_table_unittest.cc b/webkit/database/quota_table_unittest.cc
new file mode 100644
index 0000000..fbb59bf
--- /dev/null
+++ b/webkit/database/quota_table_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2009 The Chromium Authos. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "app/sql/connection.h"
+#include "app/sql/statement.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/database/quota_table.h"
+
+namespace {
+
+class TestErrorDelegate : public sql::ErrorDelegate {
+ public:
+ virtual ~TestErrorDelegate() { }
+ virtual int OnError(
+ int error, sql::Connection* connection, sql::Statement* stmt) {
+ return error;
+ }
+};
+
+} // namespace
+
+namespace webkit_database {
+
+static bool QuotaTableIsEmpty(sql::Connection* db) {
+ sql::Statement statement(db->GetCachedStatement(
+ SQL_FROM_HERE, "SELECT COUNT(*) FROM Quota"));
+ return (statement.is_valid() && statement.Step() && !statement.ColumnInt(0));
+}
+
+TEST(QuotaTableTest, TestIt) {
+ // Initialize the 'Quota' table.
+ sql::Connection db;
+
+ // Set an error delegate that will make all operations return false on error.
+ scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate());
+ db.set_error_delegate(error_delegate);
+
+ // Initialize the temp dir and the 'Databases' table.
+ EXPECT_TRUE(db.OpenInMemory());
+ QuotaTable quota_table(&db);
+ EXPECT_TRUE(quota_table.Init());
+
+ // The 'Quota' table should be empty.
+ EXPECT_TRUE(QuotaTableIsEmpty(&db));
+
+ // Set and check the quota for a new origin.
+ string16 origin = ASCIIToUTF16("origin");
+ EXPECT_TRUE(quota_table.SetOriginQuota(origin, 1000));
+ EXPECT_EQ(1000, quota_table.GetOriginQuota(origin));
+
+ // Reset and check the quota for the same origin.
+ EXPECT_TRUE(quota_table.SetOriginQuota(origin, 2000));
+ EXPECT_EQ(2000, quota_table.GetOriginQuota(origin));
+
+ // Clear the quota for an origin
+ EXPECT_TRUE(quota_table.ClearOriginQuota(origin));
+ EXPECT_TRUE(quota_table.GetOriginQuota(origin) < 0);
+
+ // Check that there's no quota set for unknown origins.
+ EXPECT_TRUE(quota_table.GetOriginQuota(ASCIIToUTF16("unknown_origin")) < 0);
+}
+
+} // namespace webkit_database