blob: 68899deed6fc088e2529d6c759cd5c7971e0536b [file] [log] [blame]
// Copyright (c) 2012 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 <algorithm>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.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"
#include "sql/connection.h"
#include "sql/diagnostic_error_delegate.h"
#include "sql/meta_table.h"
#include "sql/transaction.h"
#include "third_party/sqlite/sqlite3.h"
#include "webkit/database/database_quota_client.h"
#include "webkit/database/database_util.h"
#include "webkit/database/databases_table.h"
#include "webkit/quota/quota_manager.h"
#include "webkit/quota/special_storage_policy.h"
namespace {
class HistogramUniquifier {
public:
static const char* name() { return "Sqlite.DatabaseTracker.Error"; }
};
sql::ErrorDelegate* GetErrorHandlerForTrackerDb() {
return new sql::DiagnosticErrorDelegate<HistogramUniquifier>();
}
} // anon namespace
namespace webkit_database {
const FilePath::CharType kDatabaseDirectoryName[] =
FILE_PATH_LITERAL("databases");
const FilePath::CharType kIncognitoDatabaseDirectoryName[] =
FILE_PATH_LITERAL("databases-incognito");
const FilePath::CharType kTrackerDatabaseFileName[] =
FILE_PATH_LITERAL("Databases.db");
static const int kCurrentVersion = 2;
static const int kCompatibleVersion = 1;
const FilePath::CharType kTemporaryDirectoryPrefix[] =
FILE_PATH_LITERAL("DeleteMe");
const FilePath::CharType kTemporaryDirectoryPattern[] =
FILE_PATH_LITERAL("DeleteMe*");
OriginInfo::OriginInfo()
: total_size_(0) {}
OriginInfo::OriginInfo(const OriginInfo& origin_info)
: origin_(origin_info.origin_),
total_size_(origin_info.total_size_),
database_info_(origin_info.database_info_) {}
OriginInfo::~OriginInfo() {}
void OriginInfo::GetAllDatabaseNames(std::vector<string16>* databases) const {
for (DatabaseInfoMap::const_iterator it = database_info_.begin();
it != database_info_.end(); it++) {
databases->push_back(it->first);
}
}
int64 OriginInfo::GetDatabaseSize(const string16& database_name) const {
DatabaseInfoMap::const_iterator it = database_info_.find(database_name);
if (it != database_info_.end())
return it->second.first;
return 0;
}
string16 OriginInfo::GetDatabaseDescription(
const string16& database_name) const {
DatabaseInfoMap::const_iterator it = database_info_.find(database_name);
if (it != database_info_.end())
return it->second.second;
return string16();
}
OriginInfo::OriginInfo(const string16& origin, int64 total_size)
: origin_(origin), total_size_(total_size) {}
DatabaseTracker::DatabaseTracker(
const FilePath& profile_path,
bool is_incognito,
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_(false),
save_session_state_(false),
shutting_down_(false),
profile_path_(profile_path),
db_dir_(is_incognito_ ?
profile_path_.Append(kIncognitoDatabaseDirectoryName) :
profile_path_.Append(kDatabaseDirectoryName)),
db_(new sql::Connection()),
databases_table_(NULL),
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(
new DatabaseQuotaClient(db_tracker_thread, this));
}
}
DatabaseTracker::~DatabaseTracker() {
DCHECK(dbs_to_be_deleted_.empty());
DCHECK(deletion_callbacks_.empty());
}
void DatabaseTracker::DatabaseOpened(const string16& origin_identifier,
const string16& database_name,
const string16& database_description,
int64 estimated_size,
int64* database_size) {
if (shutting_down_ || !LazyInit()) {
*database_size = 0;
return;
}
if (quota_manager_proxy_)
quota_manager_proxy_->NotifyStorageAccessed(
quota::QuotaClient::kDatabase,
DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
quota::kStorageTypeTemporary);
InsertOrUpdateDatabaseDetails(origin_identifier, database_name,
database_description, estimated_size);
if (database_connections_.AddConnection(origin_identifier, database_name)) {
*database_size = SeedOpenDatabaseInfo(origin_identifier,
database_name,
database_description);
return;
}
*database_size = UpdateOpenDatabaseInfoAndNotify(origin_identifier,
database_name,
&database_description);
}
void DatabaseTracker::DatabaseModified(const string16& origin_identifier,
const string16& database_name) {
if (!LazyInit())
return;
UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name);
}
void DatabaseTracker::DatabaseClosed(const string16& origin_identifier,
const string16& database_name) {
if (database_connections_.IsEmpty()) {
DCHECK(!is_initialized_);
return;
}
// We call NotifiyStorageAccessed when a db is opened and also when
// closed because we don't call it for read while open.
if (quota_manager_proxy_)
quota_manager_proxy_->NotifyStorageAccessed(
quota::QuotaClient::kDatabase,
DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
quota::kStorageTypeTemporary);
UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name);
if (database_connections_.RemoveConnection(origin_identifier, database_name))
DeleteDatabaseIfNeeded(origin_identifier, database_name);
}
void DatabaseTracker::HandleSqliteError(
const string16& origin_identifier,
const string16& database_name,
int error) {
// We only handle errors that indicate corruption and we
// do so with a heavy hand, we delete it. Any renderers/workers
// with this database open will receive a message to close it
// immediately, once all have closed, the files will be deleted.
// In the interim, all attempts to open a new connection to that
// database will fail.
// Note: the client-side filters out all but these two errors as
// a small optimization, see WebDatabaseObserverImpl::HandleSqliteError.
if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) {
DeleteDatabase(origin_identifier, database_name,
net::CompletionCallback());
}
}
void DatabaseTracker::CloseDatabases(const DatabaseConnections& connections) {
if (database_connections_.IsEmpty()) {
DCHECK(!is_initialized_ || connections.IsEmpty());
return;
}
// When being closed by this route, there's a chance that
// the tracker missed some DatabseModified calls. This method is used
// when a renderer crashes to cleanup its open resources.
// We need to examine what we have in connections for the
// size of each open databases and notify any differences between the
// actual file sizes now.
std::vector<std::pair<string16, string16> > open_dbs;
connections.ListConnections(&open_dbs);
for (std::vector<std::pair<string16, string16> >::iterator it =
open_dbs.begin(); it != open_dbs.end(); ++it)
UpdateOpenDatabaseSizeAndNotify(it->first, it->second);
std::vector<std::pair<string16, string16> > closed_dbs;
database_connections_.RemoveConnections(connections, &closed_dbs);
for (std::vector<std::pair<string16, string16> >::iterator it =
closed_dbs.begin(); it != closed_dbs.end(); ++it) {
DeleteDatabaseIfNeeded(it->first, it->second);
}
}
void DatabaseTracker::DeleteDatabaseIfNeeded(const string16& origin_identifier,
const string16& database_name) {
DCHECK(!database_connections_.IsDatabaseOpened(origin_identifier,
database_name));
if (IsDatabaseScheduledForDeletion(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);
PendingDeletionCallbacks::iterator callback = deletion_callbacks_.begin();
while (callback != deletion_callbacks_.end()) {
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);
callback = deletion_callbacks_.erase(callback);
continue;
}
}
}
++callback;
}
}
}
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();
if (!is_incognito_) {
meta_table_.reset(NULL);
databases_table_.reset(NULL);
db_->Close();
is_initialized_ = false;
}
}
string16 DatabaseTracker::GetOriginDirectory(
const string16& origin_identifier) {
if (!is_incognito_)
return origin_identifier;
OriginDirectoriesMap::const_iterator it =
incognito_origin_directories_.find(origin_identifier);
if (it != incognito_origin_directories_.end())
return it->second;
string16 origin_directory =
base::IntToString16(incognito_origin_directories_generator_++);
incognito_origin_directories_[origin_identifier] = origin_directory;
return origin_directory;
}
FilePath DatabaseTracker::GetFullDBFilePath(
const string16& origin_identifier,
const string16& database_name) {
DCHECK(!origin_identifier.empty());
if (!LazyInit())
return FilePath();
int64 id = databases_table_->GetDatabaseID(
origin_identifier, database_name);
if (id < 0)
return FilePath();
FilePath file_name = FilePath::FromWStringHack(
UTF8ToWide(base::Int64ToString(id)));
return db_dir_.Append(FilePath::FromWStringHack(
UTF16ToWide(GetOriginDirectory(origin_identifier)))).Append(file_name);
}
bool DatabaseTracker::GetOriginInfo(const string16& origin_identifier,
OriginInfo* info) {
DCHECK(info);
CachedOriginInfo* cached_info = GetCachedOriginInfo(origin_identifier);
if (!cached_info)
return false;
*info = OriginInfo(*cached_info);
return true;
}
bool DatabaseTracker::GetAllOriginIdentifiers(
std::vector<string16>* origin_identifiers) {
DCHECK(origin_identifiers);
DCHECK(origin_identifiers->empty());
if (!LazyInit())
return false;
return databases_table_->GetAllOrigins(origin_identifiers);
}
bool DatabaseTracker::GetAllOriginsInfo(std::vector<OriginInfo>* origins_info) {
DCHECK(origins_info);
DCHECK(origins_info->empty());
std::vector<string16> origins;
if (!GetAllOriginIdentifiers(&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;
}
bool DatabaseTracker::DeleteClosedDatabase(const string16& origin_identifier,
const string16& database_name) {
if (!LazyInit())
return false;
// Check if the database is opened by any renderer.
if (database_connections_.IsDatabaseOpened(origin_identifier, database_name))
return false;
int64 db_file_size = quota_manager_proxy_ ?
GetDBFileSize(origin_identifier, database_name) : 0;
// 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;
// Also delete any orphaned journal file.
DCHECK(db_file.Extension().empty());
file_util::Delete(db_file.InsertBeforeExtensionASCII(
DatabaseUtil::kJournalFileSuffix), false);
if (quota_manager_proxy_ && db_file_size)
quota_manager_proxy_->NotifyStorageModified(
quota::QuotaClient::kDatabase,
DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
quota::kStorageTypeTemporary,
-db_file_size);
// Clean up the main database and invalidate the cached record.
databases_table_->DeleteDatabaseDetails(origin_identifier, database_name);
origins_info_map_.erase(origin_identifier);
std::vector<DatabaseDetails> details;
if (databases_table_->GetAllDatabaseDetailsForOrigin(
origin_identifier, &details) && details.empty()) {
// Try to delete the origin in case this was the last database.
DeleteOrigin(origin_identifier, false);
}
return true;
}
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) && !force)
return false;
int64 deleted_size = 0;
if (quota_manager_proxy_) {
CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier);
if (origin_info)
deleted_size = origin_info->TotalSize();
}
origins_info_map_.erase(origin_identifier);
FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack(
UTF16ToWide(origin_identifier)));
// 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);
if (quota_manager_proxy_ && deleted_size) {
quota_manager_proxy_->NotifyStorageModified(
quota::QuotaClient::kDatabase,
DatabaseUtil::GetOriginFromIdentifier(origin_identifier),
quota::kStorageTypeTemporary,
-deleted_size);
}
return true;
}
bool DatabaseTracker::IsDatabaseScheduledForDeletion(
const string16& origin_identifier,
const string16& database_name) {
DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier);
if (it == dbs_to_be_deleted_.end())
return false;
std::set<string16>& databases = it->second;
return (databases.find(database_name) != databases.end());
}
bool DatabaseTracker::LazyInit() {
if (!is_initialized_ && !shutting_down_) {
DCHECK(!db_->is_open());
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 =
db_dir_.Append(FilePath(kTrackerDatabaseFileName));
if (file_util::DirectoryExists(db_dir_) &&
file_util::PathExists(kTrackerDatabaseFullPath) &&
(!db_->Open(kTrackerDatabaseFullPath) ||
!sql::MetaTable::DoesTableExist(db_.get()))) {
db_->Close();
if (!file_util::Delete(db_dir_, true))
return false;
}
db_->set_error_delegate(GetErrorHandlerForTrackerDb());
databases_table_.reset(new DatabasesTable(db_.get()));
meta_table_.reset(new sql::MetaTable());
is_initialized_ =
file_util::CreateDirectory(db_dir_) &&
(db_->is_open() ||
(is_incognito_ ? db_->OpenInMemory() :
db_->Open(kTrackerDatabaseFullPath))) &&
UpgradeToCurrentVersion();
if (!is_initialized_) {
databases_table_.reset(NULL);
meta_table_.reset(NULL);
db_->Close();
}
}
return is_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())
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,
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);
}
}
void DatabaseTracker::ClearAllCachedOriginInfo() {
origins_info_map_.clear();
}
DatabaseTracker::CachedOriginInfo* DatabaseTracker::MaybeGetCachedOriginInfo(
const string16& origin_identifier, bool create_if_needed) {
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()) {
if (!create_if_needed)
return NULL;
std::vector<DatabaseDetails> details;
if (!databases_table_->GetAllDatabaseDetailsForOrigin(
origin_identifier, &details)) {
return NULL;
}
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;
if (database_connections_.IsDatabaseOpened(
origin_identifier, it->database_name)) {
db_file_size = database_connections_.GetOpenDatabaseSize(
origin_identifier, it->database_name);
} else {
db_file_size = GetDBFileSize(origin_identifier, it->database_name);
}
origin_info.SetDatabaseSize(it->database_name, db_file_size);
origin_info.SetDatabaseDescription(it->database_name, it->description);
}
}
return &origins_info_map_[origin_identifier];
}
int64 DatabaseTracker::GetDBFileSize(const string16& origin_identifier,
const string16& database_name) {
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::SeedOpenDatabaseInfo(
const string16& origin_id, const string16& name,
const string16& description) {
DCHECK(database_connections_.IsDatabaseOpened(origin_id, name));
int64 size = GetDBFileSize(origin_id, name);
database_connections_.SetOpenDatabaseSize(origin_id, name, size);
CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false);
if (info) {
info->SetDatabaseSize(name, size);
info->SetDatabaseDescription(name, description);
}
return size;
}
int64 DatabaseTracker::UpdateOpenDatabaseInfoAndNotify(
const string16& origin_id, const string16& name,
const string16* opt_description) {
DCHECK(database_connections_.IsDatabaseOpened(origin_id, name));
int64 new_size = GetDBFileSize(origin_id, name);
int64 old_size = database_connections_.GetOpenDatabaseSize(origin_id, name);
CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false);
if (info && opt_description)
info->SetDatabaseDescription(name, *opt_description);
if (old_size != new_size) {
database_connections_.SetOpenDatabaseSize(origin_id, name, new_size);
if (info)
info->SetDatabaseSize(name, new_size);
if (quota_manager_proxy_)
quota_manager_proxy_->NotifyStorageModified(
quota::QuotaClient::kDatabase,
DatabaseUtil::GetOriginFromIdentifier(origin_id),
quota::kStorageTypeTemporary,
new_size - old_size);
FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged(
origin_id, name, new_size));
}
return new_size;
}
void DatabaseTracker::ScheduleDatabaseForDeletion(
const string16& origin_identifier,
const string16& database_name) {
DCHECK(database_connections_.IsDatabaseOpened(origin_identifier,
database_name));
dbs_to_be_deleted_[origin_identifier].insert(database_name);
FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseScheduledForDeletion(
origin_identifier, database_name));
}
void DatabaseTracker::ScheduleDatabasesForDeletion(
const DatabaseSet& databases,
const net::CompletionCallback& callback) {
DCHECK(!databases.empty());
if (!callback.is_null())
deletion_callbacks_.push_back(std::make_pair(callback, databases));
for (DatabaseSet::const_iterator ori = databases.begin();
ori != databases.end(); ++ori) {
for (std::set<string16>::const_iterator db = ori->second.begin();
db != ori->second.end(); ++db)
ScheduleDatabaseForDeletion(ori->first, *db);
}
}
int DatabaseTracker::DeleteDatabase(const string16& origin_identifier,
const string16& database_name,
const net::CompletionCallback& callback) {
if (!LazyInit())
return net::ERR_FAILED;
if (database_connections_.IsDatabaseOpened(origin_identifier,
database_name)) {
if (!callback.is_null()) {
DatabaseSet set;
set[origin_identifier].insert(database_name);
deletion_callbacks_.push_back(std::make_pair(callback, set));
}
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,
const net::CompletionCallback& callback) {
if (!LazyInit())
return net::ERR_FAILED;
DatabaseSet to_be_deleted;
std::vector<string16> origins_identifiers;
if (!databases_table_->GetAllOrigins(&origins_identifiers))
return net::ERR_FAILED;
int rv = net::OK;
for (std::vector<string16>::const_iterator ori = origins_identifiers.begin();
ori != origins_identifiers.end(); ++ori) {
if (special_storage_policy_.get() &&
special_storage_policy_->IsStorageProtected(
DatabaseUtil::GetOriginFromIdentifier(*ori))) {
continue;
}
std::vector<DatabaseDetails> details;
if (!databases_table_->GetAllDatabaseDetailsForOrigin(*ori, &details))
rv = net::ERR_FAILED;
for (std::vector<DatabaseDetails>::const_iterator db = details.begin();
db != details.end(); ++db) {
FilePath db_file = GetFullDBFilePath(*ori, db->database_name);
base::PlatformFileInfo file_info;
file_util::GetFileInfo(db_file, &file_info);
if (file_info.last_modified < cutoff)
continue;
// Check if the database is opened by any renderer.
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::OK)
return rv;
if (!to_be_deleted.empty()) {
ScheduleDatabasesForDeletion(to_be_deleted, callback);
return net::ERR_IO_PENDING;
}
return net::OK;
}
int DatabaseTracker::DeleteDataForOrigin(
const string16& origin, const net::CompletionCallback& callback) {
if (!LazyInit())
return net::ERR_FAILED;
DatabaseSet to_be_deleted;
std::vector<DatabaseDetails> details;
if (!databases_table_->GetAllDatabaseDetailsForOrigin(origin, &details))
return net::ERR_FAILED;
for (std::vector<DatabaseDetails>::const_iterator db = details.begin();
db != details.end(); ++db) {
// Check if the database is opened by any renderer.
if (database_connections_.IsDatabaseOpened(origin, db->database_name))
to_be_deleted[origin].insert(db->database_name);
else
DeleteClosedDatabase(origin, db->database_name);
}
if (!to_be_deleted.empty()) {
ScheduleDatabasesForDeletion(to_be_deleted, callback);
return net::ERR_IO_PENDING;
}
return net::OK;
}
void DatabaseTracker::GetIncognitoFileHandle(
const string16& vfs_file_name, base::PlatformFile* file_handle) const {
DCHECK(is_incognito_);
FileHandlesMap::const_iterator it =
incognito_file_handles_.find(vfs_file_name);
if (it != incognito_file_handles_.end())
*file_handle = it->second;
else
*file_handle = base::kInvalidPlatformFileValue;
}
void DatabaseTracker::SaveIncognitoFileHandle(
const string16& vfs_file_name, const base::PlatformFile& file_handle) {
DCHECK(is_incognito_);
DCHECK(incognito_file_handles_.find(vfs_file_name) ==
incognito_file_handles_.end());
if (file_handle != base::kInvalidPlatformFileValue)
incognito_file_handles_[vfs_file_name] = file_handle;
}
bool DatabaseTracker::CloseIncognitoFileHandle(const string16& vfs_file_name) {
DCHECK(is_incognito_);
DCHECK(incognito_file_handles_.find(vfs_file_name) !=
incognito_file_handles_.end());
bool handle_closed = false;
FileHandlesMap::iterator it = incognito_file_handles_.find(vfs_file_name);
if (it != incognito_file_handles_.end()) {
handle_closed = base::ClosePlatformFile(it->second);
if (handle_closed)
incognito_file_handles_.erase(it);
}
return handle_closed;
}
bool DatabaseTracker::HasSavedIncognitoFileHandle(
const string16& vfs_file_name) const {
return (incognito_file_handles_.find(vfs_file_name) !=
incognito_file_handles_.end());
}
void DatabaseTracker::DeleteIncognitoDBDirectory() {
shutting_down_ = true;
is_initialized_ = false;
for (FileHandlesMap::iterator it = incognito_file_handles_.begin();
it != incognito_file_handles_.end(); it++)
base::ClosePlatformFile(it->second);
FilePath incognito_db_dir =
profile_path_.Append(kIncognitoDatabaseDirectoryName);
if (file_util::DirectoryExists(incognito_db_dir))
file_util::Delete(incognito_db_dir, true);
}
void DatabaseTracker::ClearLocalState(bool clear_all_databases) {
shutting_down_ = true;
bool has_session_only_databases =
special_storage_policy_.get() &&
special_storage_policy_->HasSessionOnlyOrigins();
// Clearing only session-only databases, and there are none.
if (!clear_all_databases && !has_session_only_databases)
return;
if (!LazyInit())
return;
std::vector<string16> origin_identifiers;
GetAllOriginIdentifiers(&origin_identifiers);
for (std::vector<string16>::iterator origin = origin_identifiers.begin();
origin != origin_identifiers.end(); ++origin) {
GURL origin_url =
webkit_database::DatabaseUtil::GetOriginFromIdentifier(*origin);
if (!clear_all_databases &&
!special_storage_policy_->IsStorageSessionOnly(origin_url)) {
continue;
}
if (special_storage_policy_.get() &&
special_storage_policy_->IsStorageProtected(origin_url)) {
continue;
}
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 (!save_session_state_)
ClearLocalState(clear_local_state_on_exit_);
}
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,
base::Bind(&DatabaseTracker::SetClearLocalStateOnExit, this,
clear_local_state_on_exit));
return;
}
if (shutting_down_) {
NOTREACHED();
return;
}
clear_local_state_on_exit_ = clear_local_state_on_exit;
}
void DatabaseTracker::SaveSessionState() {
DCHECK(db_tracker_thread_.get());
if (!db_tracker_thread_->BelongsToCurrentThread()) {
db_tracker_thread_->PostTask(
FROM_HERE,
base::Bind(&DatabaseTracker::SaveSessionState, this));
return;
}
save_session_state_ = true;
}
} // namespace webkit_database