blob: efece6d6644adc4bfe0b8ffc8e1071a574b2efa3 [file] [log] [blame]
// 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_tracker.h"
#include <vector>
#include "app/sql/connection.h"
#include "app/sql/meta_table.h"
#include "app/sql/statement.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"
namespace webkit_database {
const FilePath::CharType kDatabaseDirectoryName[] =
FILE_PATH_LITERAL("databases");
const FilePath::CharType kTrackerDatabaseFileName[] =
FILE_PATH_LITERAL("Databases.db");
const int kCurrentVersion = 1;
const int kCompatibleVersion = 1;
const int64 kDefaultQuota = 5 * 1024 * 1024;
const int64 kDefaultExtensionQuota = 50 * 1024 * 1024;
const char* kExtensionOriginIdentifierPrefix = "chrome-extension_";
DatabaseTracker::DatabaseTracker(const FilePath& profile_path)
: initialized_(false),
db_dir_(profile_path.Append(FilePath(kDatabaseDirectoryName))),
db_(new sql::Connection()),
databases_table_(NULL),
meta_table_(NULL) {
}
DatabaseTracker::~DatabaseTracker() {
DCHECK(observers_.size() == 0);
}
void DatabaseTracker::DatabaseOpened(const string16& origin_identifier,
const string16& database_name,
const string16& database_description,
int64 estimated_size,
int64* database_size,
int64* space_available) {
if (!LazyInit()) {
*database_size = 0;
*space_available = 0;
return;
}
InsertOrUpdateDatabaseDetails(origin_identifier, database_name,
database_description, estimated_size);
*database_size = GetCachedDatabaseFileSize(origin_identifier, database_name);
*space_available = GetOriginSpaceAvailable(origin_identifier);
}
void DatabaseTracker::DatabaseModified(const string16& origin_identifier,
const string16& database_name) {
if (!LazyInit())
return;
int64 updated_db_size =
UpdateCachedDatabaseFileSize(origin_identifier, database_name);
int64 space_available = GetOriginSpaceAvailable(origin_identifier);
FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged(
origin_identifier, database_name, updated_db_size, space_available));
}
void DatabaseTracker::DatabaseClosed(const string16& origin_identifier,
const string16& database_name) {
// TODO(dumi): figure out how to use this information at a later time
}
void DatabaseTracker::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void DatabaseTracker::RemoveObserver(Observer* observer) {
// When we remove a listener, we do not know which cached information
// is still needed and which information can be discarded. So we just
// clear all caches and re-populate them as needed.
observers_.RemoveObserver(observer);
ClearAllCachedOriginInfo();
}
void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() {
ClearAllCachedOriginInfo();
meta_table_.reset(NULL);
databases_table_.reset(NULL);
db_->Close();
initialized_ = false;
}
FilePath DatabaseTracker::GetFullDBFilePath(
const string16& origin_identifier,
const string16& database_name) const {
DCHECK(!origin_identifier.empty());
DCHECK(!database_name.empty());
int64 id = databases_table_->GetDatabaseID(
origin_identifier, database_name);
if (id < 0)
return FilePath();
FilePath file_name = FilePath::FromWStringHack(Int64ToWString(id));
return db_dir_.Append(FilePath::FromWStringHack(
UTF16ToWide(origin_identifier))).Append(file_name);
}
bool DatabaseTracker::LazyInit() {
if (!initialized_) {
DCHECK(!db_->is_open());
DCHECK(!databases_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
const FilePath kTrackerDatabaseFullPath =
db_dir_.Append(FilePath(kTrackerDatabaseFileName));
if (file_util::DirectoryExists(db_dir_) &&
file_util::PathExists(kTrackerDatabaseFullPath) &&
(!db_->Open(kTrackerDatabaseFullPath) ||
!db_->DoesTableExist("meta"))) {
db_->Close();
if (!file_util::Delete(db_dir_, true))
return false;
}
databases_table_.reset(new DatabasesTable(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();
if (!initialized_) {
databases_table_.reset(NULL);
meta_table_.reset(NULL);
db_->Close();
}
}
return initialized_;
}
void DatabaseTracker::InsertOrUpdateDatabaseDetails(
const string16& origin_identifier,
const string16& database_name,
const string16& database_description,
int64 estimated_size) {
DatabaseDetails details;
if (!databases_table_->GetDatabaseDetails(
origin_identifier, database_name, &details)) {
details.origin_identifier = origin_identifier;
details.database_name = database_name;
details.description = database_description;
details.estimated_size = estimated_size;
databases_table_->InsertDatabaseDetails(details);
} else if ((details.description != database_description) ||
(details.estimated_size != estimated_size)) {
details.description = database_description;
details.estimated_size = estimated_size;
databases_table_->UpdateDatabaseDetails(details);
}
}
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) {
// 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;
if (!databases_table_->GetAllDatabaseDetailsForOrigin(
origin_identifier, &details)) {
return NULL;
}
CachedOriginInfo& origin_info = origins_info_map_[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);
}
}
return &origins_info_map_[origin_identifier];
}
int64 DatabaseTracker::GetCachedDatabaseFileSize(
const string16& origin_identifier,
const string16& database_name) {
CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
if (!origin_info)
return 0;
return origin_info->GetCachedDatabaseSize(database_name);
}
int64 DatabaseTracker::UpdateCachedDatabaseFileSize(
const string16& origin_identifier,
const string16& database_name) {
int64 new_size = GetDBFileSize(origin_identifier, database_name);
CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
if (origin_info)
origin_info->SetCachedDatabaseSize(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