diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-17 07:46:00 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-17 07:46:00 +0000 |
commit | 42b1020f82ad53a0d0c8591e977360e8fee3415c (patch) | |
tree | 0ba1fa306810790507fb065cb4c43c215de5812e /webkit/quota | |
parent | a604854af9ff6f729ada6d8e5bc6f53147b0f3e4 (diff) | |
download | chromium_src-42b1020f82ad53a0d0c8591e977360e8fee3415c.zip chromium_src-42b1020f82ad53a0d0c8591e977360e8fee3415c.tar.gz chromium_src-42b1020f82ad53a0d0c8591e977360e8fee3415c.tar.bz2 |
Implement quota manager database.
BUG=61676
TEST=QuotaDatabaseTest.*
Review URL: http://codereview.chromium.org/6627003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78521 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/quota')
-rw-r--r-- | webkit/quota/quota_database.cc | 540 | ||||
-rw-r--r-- | webkit/quota/quota_database.h | 93 | ||||
-rw-r--r-- | webkit/quota/quota_database_unittest.cc | 161 | ||||
-rw-r--r-- | webkit/quota/webkit_quota.gypi | 4 |
4 files changed, 798 insertions, 0 deletions
diff --git a/webkit/quota/quota_database.cc b/webkit/quota/quota_database.cc new file mode 100644 index 0000000..a6dd0e6 --- /dev/null +++ b/webkit/quota/quota_database.cc @@ -0,0 +1,540 @@ +// Copyright (c) 2011 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/quota/quota_database.h" + +#include "app/sql/connection.h" +#include "app/sql/diagnostic_error_delegate.h" +#include "app/sql/meta_table.h" +#include "app/sql/statement.h" +#include "app/sql/transaction.h" +#include "base/auto_reset.h" +#include "base/file_util.h" +#include "googleurl/src/gurl.h" + +namespace { + +const int64 kUninitializedId = -1; +const int64 kUninitializedQuota = -1; + +// Definitions for database schema. + +const int kCurrentVersion = 1; +const int kCompatibleVersion = 1; + +const char kOriginsTable[] = "Origins"; +const char kStorageInfoTable[] = "StorageInfo"; +const char kGlobalQuotaKeyPrefix[] = "GlobalQuota-"; + +const struct { + const char* table_name; + const char* columns; +} kTables[] = { + { kOriginsTable, + "(origin_url TEXT NOT NULL)" }, + { kStorageInfoTable, + "(origin_rowid INTEGER," + " type INTEGER NOT NULL," + " quota INTEGER," + " used_count INTEGER," + " last_access_time INTEGER)" }, +}; + +const struct { + const char* index_name; + const char* table_name; + const char* columns; + bool unique; +} kIndexes[] = { + { "OriginsIndex", + kOriginsTable, + "(origin_url)", + true }, + { "StorageInfoIndex", + kStorageInfoTable, + "(origin_rowid)", + false }, +}; + +const int kTableCount = ARRAYSIZE_UNSAFE(kTables); +const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes); + +class HistogramUniquifier { + public: + static const char* name() { return "Sqlite.Quota.Error"; } +}; + +sql::ErrorDelegate* GetErrorHandlerForQuotaManagerDb() { + return new sql::DiagnosticErrorDelegate<HistogramUniquifier>(); +} + +bool PrepareUniqueStatement( + sql::Connection* db, const char* sql, sql::Statement* statement) { + DCHECK(db && sql && statement); + statement->Assign(db->GetUniqueStatement(sql)); + if (!statement->is_valid()) { + NOTREACHED() << db->GetErrorMessage(); + return false; + } + return true; +} + +bool PrepareCachedStatement( + sql::Connection* db, const sql::StatementID& id, + const char* sql, sql::Statement* statement) { + DCHECK(db && sql && statement); + statement->Assign(db->GetCachedStatement(id, sql)); + if (!statement->is_valid()) { + NOTREACHED() << db->GetErrorMessage(); + return false; + } + return true; +} + +std::string GetGlobalQuotaKey(quota::StorageType type) { + if (type == quota::kStorageTypeTemporary) + return std::string(kGlobalQuotaKeyPrefix) + "temporary"; + else if (type == quota::kStorageTypePersistent) + return std::string(kGlobalQuotaKeyPrefix) + "persistent"; + NOTREACHED() << "Unknown storage type " << type; + return std::string(); +} + +} // anonymous namespace + +namespace quota { + +struct QuotaDatabase::StorageInfoRecord { + StorageInfoRecord() + : rowid(kUninitializedId), + origin_rowid(kUninitializedId), + type(kStorageTypeUnknown), + quota(kUninitializedQuota), + used_count(0) {} + int64 rowid; + int64 origin_rowid; + StorageType type; + int64 quota; + int used_count; + base::Time last_access_time; +}; + +QuotaDatabase::QuotaDatabase(const FilePath& path) + : db_file_path_(path), + is_recreating_(false), + is_disabled_(false) { +} + +QuotaDatabase::~QuotaDatabase() { +} + +void QuotaDatabase::CloseConnection() { + meta_table_.reset(); + db_.reset(); +} + +bool QuotaDatabase::GetOriginQuota( + const GURL& origin, StorageType type, int64* quota) { + DCHECK(quota); + StorageInfoRecord record; + if (!FindStorageInfo(origin, type, &record)) + return false; + if (record.quota == kUninitializedQuota) + return false; + *quota = record.quota; + return true; +} + +bool QuotaDatabase::SetOriginQuota( + const GURL& origin, StorageType type, int64 quota) { + DCHECK(quota >= 0); + if (!LazyOpen(true)) + return false; + + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + + int64 origin_rowid; + if (!FindOrigin(origin, &origin_rowid)) { + if (!InsertOrigin(origin, &origin_rowid)) + return false; + } + + StorageInfoRecord record; + if (!FindStorageInfo(origin_rowid, type, &record)) { + record.origin_rowid = origin_rowid; + record.type = type; + record.quota = quota; + if (!InsertStorageInfo(record)) + return false; + return transaction.Commit(); + } + + const char* kSql = + "UPDATE StorageInfo" + " SET quota = ?" + " WHERE rowid = ?"; + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, quota); + statement.BindInt64(1, record.rowid); + if (!statement.Run()) + return false; + return transaction.Commit(); +} + +bool QuotaDatabase::SetOriginLastAccessTime( + const GURL& origin, StorageType type, base::Time last_access_time) { + if (!LazyOpen(true)) + return false; + + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + + int64 origin_rowid; + if (!FindOrigin(origin, &origin_rowid)) { + if (!InsertOrigin(origin, &origin_rowid)) + return false; + } + StorageInfoRecord record; + if (!FindStorageInfo(origin_rowid, type, &record)) { + record.origin_rowid = origin_rowid; + record.type = type; + record.used_count = 0; + record.last_access_time = last_access_time; + if (!InsertStorageInfo(record)) + return false; + return transaction.Commit(); + } + + const char* kSql = + "UPDATE StorageInfo" + " SET used_count = ?, last_access_time = ?" + " WHERE rowid = ?"; + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt(0, record.used_count + 1); + statement.BindInt64(1, last_access_time.ToInternalValue()); + statement.BindInt64(2, record.rowid); + if (!statement.Run()) + return false; + + return transaction.Commit(); +} + +bool QuotaDatabase::DeleteStorageInfo(const GURL& origin, StorageType type) { + if (!LazyOpen(false)) + return false; + + int64 origin_rowid; + if (!FindOrigin(origin, &origin_rowid)) + return false; + + StorageInfoRecord record; + if (!FindStorageInfo(origin_rowid, type, &record)) + return false; + + const char* kSql = + "DELETE FROM StorageInfo" + " WHERE rowid = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record.rowid); + return statement.Run(); +} + +bool QuotaDatabase::GetGlobalQuota(StorageType type, int64* quota) { + if (!LazyOpen(false)) + return false; + return meta_table_->GetValue(GetGlobalQuotaKey(type).c_str(), quota); +} + +bool QuotaDatabase::SetGlobalQuota(StorageType type, int64 quota) { + if (!LazyOpen(true)) + return false; + return meta_table_->SetValue(GetGlobalQuotaKey(type).c_str(), quota); +} + +bool QuotaDatabase::GetLRUOrigins( + StorageType type, std::vector<GURL>* origins, + int max_used_count, int num_origins_limit) { + DCHECK(origins); + DCHECK(num_origins_limit > 0); + if (!LazyOpen(false)) + return false; + + const char* kSqlBase = + "SELECT o.origin_url FROM Origins o, StorageInfo s" + " WHERE o.rowid = s.origin_rowid AND" + " s.type = ?"; + + // TODO(kinuko): depending on how we call this method, consider creating + // an index to avoid frequent full table scan. + const char* kSqlSuffix = + " ORDER BY s.last_access_time ASC " + " LIMIT ?"; + + std::string sql(kSqlBase); + sql::StatementID id = SQL_FROM_HERE; + if (max_used_count >= 0) { + sql += " AND s.used_count <= ?"; + id = SQL_FROM_HERE; + } + sql += kSqlSuffix; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), id, sql.c_str(), &statement)) + return false; + + int column = 0; + statement.BindInt(column++, static_cast<int>(type)); + if (max_used_count >= 0) + statement.BindInt(column++, max_used_count); + statement.BindInt(column++, num_origins_limit); + + origins->clear(); + while (statement.Step()) + origins->push_back(GURL(statement.ColumnString(0))); + + DCHECK(origins->size() <= static_cast<size_t>(num_origins_limit)); + + return statement.Succeeded(); +} + +bool QuotaDatabase::FindOrigin(const GURL& origin_url, int64* origin_rowid) { + DCHECK(origin_rowid); + if (!LazyOpen(false)) + return false; + + const char* kSql = "SELECT rowid FROM Origins WHERE origin_url = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindString(0, origin_url.spec()); + if (!statement.Step() || !statement.Succeeded()) + return false; + + *origin_rowid = statement.ColumnInt64(0); + return true; +} + +bool QuotaDatabase::InsertOrigin(const GURL& origin_url, int64 *origin_rowid) { + DCHECK(origin_rowid); + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO Origins (origin_url) VALUES(?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + + statement.BindString(0, origin_url.spec()); + if (!statement.Run()) + return false; + + *origin_rowid = db_->GetLastInsertRowId(); + return true; +} + +bool QuotaDatabase::FindStorageInfo(int64 origin_rowid, StorageType type, + StorageInfoRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT rowid, origin_rowid, quota, used_count, last_access_time" + " FROM StorageInfo" + " WHERE origin_rowid = ? AND type = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, origin_rowid); + statement.BindInt(1, static_cast<int>(type)); + if (!statement.Step() || !statement.Succeeded()) + return false; + + record->rowid = statement.ColumnInt64(0); + record->origin_rowid = statement.ColumnInt64(1); + record->quota = statement.ColumnInt64(2); + record->used_count = statement.ColumnInt(3); + record->last_access_time = base::Time::FromInternalValue( + statement.ColumnInt64(4)); + + return true; +} + +bool QuotaDatabase::FindStorageInfo(const GURL& origin, StorageType type, + StorageInfoRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + int64 origin_rowid; + if (!FindOrigin(origin, &origin_rowid)) + return false; + + return FindStorageInfo(origin_rowid, type, record); +} + +bool QuotaDatabase::InsertStorageInfo(const StorageInfoRecord& record) { + if (!LazyOpen(true)) + return false; + + DCHECK(record.type == kStorageTypeTemporary || + record.type == kStorageTypePersistent); + + const char* kSql = + "INSERT INTO StorageInfo" + " (origin_rowid, type, quota, used_count, last_access_time)" + " VALUES(?, ?, ?, ?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(db_.get(), SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record.origin_rowid); + statement.BindInt(1, static_cast<int>(record.type)); + statement.BindInt64(2, record.quota); + statement.BindInt(3, record.used_count); + statement.BindInt64(4, record.last_access_time.ToInternalValue()); + return statement.Run(); +} + +bool QuotaDatabase::LazyOpen(bool create_if_needed) { + if (db_.get()) + return true; + + // If we tried and failed once, don't try again in the same session + // to avoid creating an incoherent mess on disk. + if (is_disabled_) + return false; + + if (!create_if_needed && !file_util::PathExists(db_file_path_)) + return false; + + db_.reset(new sql::Connection); + meta_table_.reset(new sql::MetaTable); + + bool opened = false; + if (!file_util::CreateDirectory(db_file_path_.DirName())) { + LOG(ERROR) << "Failed to create quota database directory."; + } else { + opened = db_->Open(db_file_path_); + if (opened) + db_->Preload(); + } + + if (!opened || !EnsureDatabaseVersion()) { + LOG(ERROR) << "Failed to open the quota database."; + is_disabled_ = true; + db_.reset(); + meta_table_.reset(); + return false; + } + + return true; +} + +bool QuotaDatabase::EnsureDatabaseVersion() { + if (!sql::MetaTable::DoesTableExist(db_.get())) + return CreateSchema(); + + if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) + return false; + + if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) { + LOG(WARNING) << "Quota database is too new."; + return false; + } + + if (meta_table_->GetVersionNumber() < kCurrentVersion) { + // TODO(kinuko): Support schema upgrade. + return ResetSchema(); + } + +#ifndef NDEBUG + DCHECK(sql::MetaTable::DoesTableExist(db_.get())); + for (int i = 0; i < kTableCount; ++i) { + DCHECK(db_->DoesTableExist(kTables[i].table_name)); + } +#endif + + return true; +} + +bool QuotaDatabase::CreateSchema() { + // TODO(kinuko): Factor out the common code to create databases. + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + + if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) + return false; + + for (int i = 0; i < kTableCount; ++i) { + std::string sql("CREATE TABLE "); + sql += kTables[i].table_name; + sql += kTables[i].columns; + if (!db_->Execute(sql.c_str())) + return false; + } + + for (int i = 0; i < kIndexCount; ++i) { + std::string sql; + if (kIndexes[i].unique) + sql += "CREATE UNIQUE INDEX "; + else + sql += "CREATE INDEX "; + sql += kIndexes[i].index_name; + sql += " ON "; + sql += kIndexes[i].table_name; + sql += kIndexes[i].columns; + if (!db_->Execute(sql.c_str())) + return false; + } + + return transaction.Commit(); +} + +bool QuotaDatabase::ResetSchema() { + DCHECK(!db_file_path_.empty()); + DCHECK(file_util::PathExists(db_file_path_)); + VLOG(1) << "Deleting existing quota data and starting over."; + + db_.reset(); + meta_table_.reset(); + + if (!file_util::Delete(db_file_path_, true)) + return false; + + // Make sure the steps above actually deleted things. + if (file_util::PathExists(db_file_path_)) + return false; + + // So we can't go recursive. + if (is_recreating_) + return false; + + AutoReset<bool> auto_reset(&is_recreating_, true); + return LazyOpen(true); +} + +} // quota namespace diff --git a/webkit/quota/quota_database.h b/webkit/quota/quota_database.h new file mode 100644 index 0000000..6aa1603 --- /dev/null +++ b/webkit/quota/quota_database.h @@ -0,0 +1,93 @@ +// Copyright (c) 2011 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_QUOTA_QUOTA_DATABASE_H_ +#define WEBKIT_QUOTA_QUOTA_DATABASE_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/scoped_ptr.h" +#include "base/time.h" + +namespace sql { +class Connection; +class MetaTable; +class Statement; +} + +class GURL; + +namespace quota { + +// TODO(kinuko): put this in a separated header file when we have more modules. +enum StorageType { + kStorageTypeUnknown, + kStorageTypeTemporary, + kStorageTypePersistent, +}; + +// All the methods of this class must run on the DB thread. +class QuotaDatabase { + public: + explicit QuotaDatabase(const FilePath& path); + ~QuotaDatabase(); + + void CloseConnection(); + + bool GetOriginQuota(const GURL& origin, StorageType type, int64* quota); + bool SetOriginQuota(const GURL& origin, StorageType type, int64 quota); + + bool SetOriginLastAccessTime(const GURL& origin, StorageType type, + base::Time last_access_time); + + bool DeleteStorageInfo(const GURL& origin, StorageType type); + + bool GetGlobalQuota(StorageType type, int64* quota); + bool SetGlobalQuota(StorageType type, int64 quota); + + // Return least recently used origins whose used_count is <= + // |max_used_count| up to |num_origins_limit|. If |max_used_count| is -1, + // it just returns LRU storages regardless of the used_count value. + // |num_origins_limit| must be > 0. + bool GetLRUOrigins(StorageType type, std::vector<GURL>* origins, + int max_used_count, int num_origins_limit); + + private: + struct StorageInfoRecord; + + bool FindOrigin(const GURL& origin_url, int64* origin_rowid); + bool InsertOrigin(const GURL& origin_url, int64* origin_rowid); + + bool FindStorageInfo(int64 origin_rowid, StorageType type, + StorageInfoRecord* record); + bool FindStorageInfo(const GURL& origin, StorageType type, + StorageInfoRecord* record); + bool InsertStorageInfo(const StorageInfoRecord& record); + + bool LazyOpen(bool create_if_needed); + bool EnsureDatabaseVersion(); + bool CreateSchema(); + bool ResetSchema(); + + FilePath db_file_path_; + + scoped_ptr<sql::Connection> db_; + scoped_ptr<sql::MetaTable> meta_table_; + bool is_recreating_; + bool is_disabled_; + + FRIEND_TEST_ALL_PREFIXES(QuotaDatabaseTest, LazyOpen); + FRIEND_TEST_ALL_PREFIXES(QuotaDatabaseTest, OriginQuota); + FRIEND_TEST_ALL_PREFIXES(QuotaDatabaseTest, GlobalQuota); + FRIEND_TEST_ALL_PREFIXES(QuotaDatabaseTest, OriginLastAccessTimeLRU); + + DISALLOW_COPY_AND_ASSIGN(QuotaDatabase); +}; + +} // namespace quota + +#endif // WEBKIT_QUOTA_QUOTA_DATABASE_H_ diff --git a/webkit/quota/quota_database_unittest.cc b/webkit/quota/quota_database_unittest.cc new file mode 100644 index 0000000..836ee16 --- /dev/null +++ b/webkit/quota/quota_database_unittest.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h" + +#include "app/sql/connection.h" +#include "base/file_util.h" +#include "base/scoped_temp_dir.h" +#include "googleurl/src/gurl.h" +#include "webkit/quota/quota_database.h" + +namespace { + +const base::Time kZeroTime; + +class TestErrorDelegate : public sql::ErrorDelegate { + public: + virtual ~TestErrorDelegate() { } + virtual int OnError( + int error, sql::Connection* connection, sql::Statement* stmt) { + return error; + } +}; + +} // namespace + +namespace quota { + +TEST(QuotaDatabaseTest, LazyOpen) { + ScopedTempDir data_dir; + ASSERT_TRUE(data_dir.CreateUniqueTempDir()); + + const FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db"); + QuotaDatabase db(kDbFile); + EXPECT_FALSE(db.LazyOpen(false)); + ASSERT_TRUE(db.LazyOpen(true)); + EXPECT_TRUE(file_util::PathExists(kDbFile)); +} + +TEST(QuotaDatabaseTest, OriginQuota) { + ScopedTempDir data_dir; + ASSERT_TRUE(data_dir.CreateUniqueTempDir()); + + const FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db"); + QuotaDatabase db(kDbFile); + ASSERT_TRUE(db.LazyOpen(true)); + + const GURL kOrigin("http://foo.com:8080/"); + const int kQuota1 = 13579; + const int kQuota2 = kQuota1 + 1024; + + int64 quota = -1; + EXPECT_FALSE(db.GetOriginQuota(kOrigin, kStorageTypeTemporary, "a)); + EXPECT_FALSE(db.GetOriginQuota(kOrigin, kStorageTypePersistent, "a)); + + // Insert quota for temporary. + EXPECT_TRUE(db.SetOriginQuota(kOrigin, kStorageTypeTemporary, kQuota1)); + EXPECT_TRUE(db.GetOriginQuota(kOrigin, kStorageTypeTemporary, "a)); + EXPECT_EQ(kQuota1, quota); + + // Update quota for temporary. + EXPECT_TRUE(db.SetOriginQuota(kOrigin, kStorageTypeTemporary, kQuota2)); + EXPECT_TRUE(db.GetOriginQuota(kOrigin, kStorageTypeTemporary, "a)); + EXPECT_EQ(kQuota2, quota); + + // Quota for persistent must not be updated. + EXPECT_FALSE(db.GetOriginQuota(kOrigin, kStorageTypePersistent, "a)); + + // Delete temporary storage info. + EXPECT_TRUE(db.DeleteStorageInfo(kOrigin, kStorageTypeTemporary)); + EXPECT_FALSE(db.GetOriginQuota(kOrigin, kStorageTypeTemporary, "a)); +} + +TEST(QuotaDatabaseTest, GlobalQuota) { + ScopedTempDir data_dir; + ASSERT_TRUE(data_dir.CreateUniqueTempDir()); + + const FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db"); + QuotaDatabase db(kDbFile); + ASSERT_TRUE(db.LazyOpen(true)); + + const int kQuota1 = 9999; + const int kQuota2 = 86420; + + int64 quota = -1; + EXPECT_FALSE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); + EXPECT_FALSE(db.GetGlobalQuota(kStorageTypePersistent, "a)); + + EXPECT_TRUE(db.SetGlobalQuota(kStorageTypeTemporary, kQuota1)); + EXPECT_TRUE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); + EXPECT_EQ(kQuota1, quota); + + EXPECT_TRUE(db.SetGlobalQuota(kStorageTypeTemporary, kQuota1 + 1024)); + EXPECT_TRUE(db.GetGlobalQuota(kStorageTypeTemporary, "a)); + EXPECT_EQ(kQuota1 + 1024, quota); + + EXPECT_FALSE(db.GetGlobalQuota(kStorageTypePersistent, "a)); + + EXPECT_TRUE(db.SetGlobalQuota(kStorageTypePersistent, kQuota2)); + EXPECT_TRUE(db.GetGlobalQuota(kStorageTypePersistent, "a)); + EXPECT_EQ(kQuota2, quota); +} + +TEST(QuotaDatabaseTest, OriginLastAccessTimeLRU) { + ScopedTempDir data_dir; + ASSERT_TRUE(data_dir.CreateUniqueTempDir()); + + const FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db"); + QuotaDatabase db(kDbFile); + ASSERT_TRUE(db.LazyOpen(true)); + + std::vector<GURL> origins; + EXPECT_TRUE(db.GetLRUOrigins(kStorageTypeTemporary, &origins, -1, 10)); + EXPECT_EQ(0U, origins.size()); + + const GURL kOrigin1("http://a/"); + const GURL kOrigin2("http://b/"); + const GURL kOrigin3("http://c/"); + const GURL kOrigin4("http://p/"); + + // Adding three temporary storages, and + EXPECT_TRUE(db.SetOriginLastAccessTime( + kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(10))); + EXPECT_TRUE(db.SetOriginLastAccessTime( + kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(20))); + EXPECT_TRUE(db.SetOriginLastAccessTime( + kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(30))); + + // one persistent. + EXPECT_TRUE(db.SetOriginLastAccessTime( + kOrigin4, kStorageTypePersistent, base::Time::FromInternalValue(40))); + + EXPECT_TRUE(db.GetLRUOrigins(kStorageTypeTemporary, &origins, 0, 10)); + + ASSERT_EQ(3U, origins.size()); + EXPECT_EQ(kOrigin1.spec(), origins[0].spec()); + EXPECT_EQ(kOrigin2.spec(), origins[1].spec()); + EXPECT_EQ(kOrigin3.spec(), origins[2].spec()); + + EXPECT_TRUE(db.SetOriginLastAccessTime( + kOrigin1, kStorageTypeTemporary, base::Time::Now())); + + EXPECT_TRUE(db.GetLRUOrigins(kStorageTypeTemporary, &origins, 0, 10)); + + // Now kOrigin1 has used_count=1, so it should not be in the returned list. + ASSERT_EQ(2U, origins.size()); + EXPECT_EQ(kOrigin2.spec(), origins[0].spec()); + EXPECT_EQ(kOrigin3.spec(), origins[1].spec()); + + // Query again without used_count condition. + EXPECT_TRUE(db.GetLRUOrigins(kStorageTypeTemporary, &origins, -1, 10)); + + // Now kOrigin1 must be returned as the newest one. + ASSERT_EQ(3U, origins.size()); + EXPECT_EQ(kOrigin2.spec(), origins[0].spec()); + EXPECT_EQ(kOrigin3.spec(), origins[1].spec()); + EXPECT_EQ(kOrigin1.spec(), origins[2].spec()); +} + +} // namespace quota diff --git a/webkit/quota/webkit_quota.gypi b/webkit/quota/webkit_quota.gypi index b738d43..491c494 100644 --- a/webkit/quota/webkit_quota.gypi +++ b/webkit/quota/webkit_quota.gypi @@ -9,9 +9,13 @@ 'type': '<(library)', 'msvs_guid': '36A9AAD2-10DD-42CF-BF51-3CC79D26FCB4', 'dependencies': [ + '<(DEPTH)/app/app.gyp:app_base', '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/net/net.gyp:net', ], 'sources': [ + 'quota_database.cc', + 'quota_database.h', 'special_storage_policy.cc', 'special_storage_policy.h', ], |