diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/webkit/browser/database/database_tracker.cc | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/webkit/browser/database/database_tracker.cc')
-rw-r--r-- | chromium/webkit/browser/database/database_tracker.cc | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/chromium/webkit/browser/database/database_tracker.cc b/chromium/webkit/browser/database/database_tracker.cc new file mode 100644 index 00000000000..e3b3c6abe83 --- /dev/null +++ b/chromium/webkit/browser/database/database_tracker.cc @@ -0,0 +1,874 @@ +// 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/browser/database/database_tracker.h" + +#include <algorithm> +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/file_enumerator.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/platform_file.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "net/base/net_errors.h" +#include "sql/connection.h" +#include "sql/meta_table.h" +#include "sql/transaction.h" +#include "third_party/sqlite/sqlite3.h" +#include "webkit/browser/database/database_quota_client.h" +#include "webkit/browser/database/database_util.h" +#include "webkit/browser/database/databases_table.h" +#include "webkit/browser/quota/quota_manager.h" +#include "webkit/browser/quota/special_storage_policy.h" +#include "webkit/common/database/database_identifier.h" + +namespace webkit_database { + +const base::FilePath::CharType kDatabaseDirectoryName[] = + FILE_PATH_LITERAL("databases"); +const base::FilePath::CharType kIncognitoDatabaseDirectoryName[] = + FILE_PATH_LITERAL("databases-incognito"); +const base::FilePath::CharType kTrackerDatabaseFileName[] = + FILE_PATH_LITERAL("Databases.db"); +static const int kCurrentVersion = 2; +static const int kCompatibleVersion = 1; + +const base::FilePath::CharType kTemporaryDirectoryPrefix[] = + FILE_PATH_LITERAL("DeleteMe"); +const base::FilePath::CharType kTemporaryDirectoryPattern[] = + FILE_PATH_LITERAL("DeleteMe*"); + +OriginInfo::OriginInfo() + : total_size_(0) {} + +OriginInfo::OriginInfo(const OriginInfo& origin_info) + : origin_identifier_(origin_info.origin_identifier_), + total_size_(origin_info.total_size_), + database_info_(origin_info.database_info_) {} + +OriginInfo::~OriginInfo() {} + +void OriginInfo::GetAllDatabaseNames( + std::vector<base::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 base::string16& database_name) const { + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); + if (it != database_info_.end()) + return it->second.first; + return 0; +} + +base::string16 OriginInfo::GetDatabaseDescription( + const base::string16& database_name) const { + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); + if (it != database_info_.end()) + return it->second.second; + return base::string16(); +} + +OriginInfo::OriginInfo(const std::string& origin_identifier, int64 total_size) + : origin_identifier_(origin_identifier), total_size_(total_size) {} + +DatabaseTracker::DatabaseTracker( + const base::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), + force_keep_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()), + 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 std::string& origin_identifier, + const base::string16& database_name, + const base::string16& database_description, + int64 estimated_size, + int64* database_size) { + if (shutting_down_ || !LazyInit()) { + *database_size = 0; + return; + } + + if (quota_manager_proxy_.get()) + quota_manager_proxy_->NotifyStorageAccessed( + quota::QuotaClient::kDatabase, + webkit_database::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 std::string& origin_identifier, + const base::string16& database_name) { + if (!LazyInit()) + return; + UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name); +} + +void DatabaseTracker::DatabaseClosed(const std::string& origin_identifier, + const base::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_.get()) + quota_manager_proxy_->NotifyStorageAccessed( + quota::QuotaClient::kDatabase, + webkit_database::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 std::string& origin_identifier, + const base::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<std::string, base::string16> > open_dbs; + connections.ListConnections(&open_dbs); + for (std::vector<std::pair<std::string, base::string16> >::iterator it = + open_dbs.begin(); it != open_dbs.end(); ++it) + UpdateOpenDatabaseSizeAndNotify(it->first, it->second); + + std::vector<std::pair<std::string, base::string16> > closed_dbs; + database_connections_.RemoveConnections(connections, &closed_dbs); + for (std::vector<std::pair<std::string, base::string16> >::iterator it = + closed_dbs.begin(); it != closed_dbs.end(); ++it) { + DeleteDatabaseIfNeeded(it->first, it->second); + } +} + +void DatabaseTracker::DeleteDatabaseIfNeeded( + const std::string& origin_identifier, + const base::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<base::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; + } +} + +base::string16 DatabaseTracker::GetOriginDirectory( + const std::string& origin_identifier) { + if (!is_incognito_) + return base::UTF8ToUTF16(origin_identifier); + + OriginDirectoriesMap::const_iterator it = + incognito_origin_directories_.find(origin_identifier); + if (it != incognito_origin_directories_.end()) + return it->second; + + base::string16 origin_directory = + base::IntToString16(incognito_origin_directories_generator_++); + incognito_origin_directories_[origin_identifier] = origin_directory; + return origin_directory; +} + +base::FilePath DatabaseTracker::GetFullDBFilePath( + const std::string& origin_identifier, + const base::string16& database_name) { + DCHECK(!origin_identifier.empty()); + if (!LazyInit()) + return base::FilePath(); + + int64 id = databases_table_->GetDatabaseID( + origin_identifier, database_name); + if (id < 0) + return base::FilePath(); + + base::FilePath file_name = base::FilePath::FromWStringHack( + UTF8ToWide(base::Int64ToString(id))); + return db_dir_.Append(base::FilePath::FromWStringHack( + UTF16ToWide(GetOriginDirectory(origin_identifier)))).Append(file_name); +} + +bool DatabaseTracker::GetOriginInfo(const std::string& 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<std::string>* origin_identifiers) { + DCHECK(origin_identifiers); + DCHECK(origin_identifiers->empty()); + if (!LazyInit()) + return false; + return databases_table_->GetAllOriginIdentifiers(origin_identifiers); +} + +bool DatabaseTracker::GetAllOriginsInfo( + std::vector<OriginInfo>* origins_info) { + DCHECK(origins_info); + DCHECK(origins_info->empty()); + + std::vector<std::string> origins; + if (!GetAllOriginIdentifiers(&origins)) + return false; + + for (std::vector<std::string>::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 std::string& origin_identifier, + const base::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_.get() + ? GetDBFileSize(origin_identifier, database_name) + : 0; + + // Try to delete the file on the hard drive. + base::FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); + if (!sql::Connection::Delete(db_file)) + return false; + + if (quota_manager_proxy_.get() && db_file_size) + quota_manager_proxy_->NotifyStorageModified( + quota::QuotaClient::kDatabase, + webkit_database::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_->GetAllDatabaseDetailsForOriginIdentifier( + 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 std::string& 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_.get()) { + CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); + if (origin_info) + deleted_size = origin_info->TotalSize(); + } + + origins_info_map_.erase(origin_identifier); + base::FilePath origin_dir = db_dir_.AppendASCII(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. + base::FilePath new_origin_dir; + file_util::CreateTemporaryDirInDir(db_dir_, + kTemporaryDirectoryPrefix, + &new_origin_dir); + base::FileEnumerator databases( + origin_dir, + false, + base::FileEnumerator::FILES); + for (base::FilePath database = databases.Next(); !database.empty(); + database = databases.Next()) { + base::FilePath new_file = new_origin_dir.Append(database.BaseName()); + base::Move(database, new_file); + } + base::DeleteFile(origin_dir, true); + base::DeleteFile(new_origin_dir, true); // might fail on windows. + + databases_table_->DeleteOriginIdentifier(origin_identifier); + + if (quota_manager_proxy_.get() && deleted_size) { + quota_manager_proxy_->NotifyStorageModified( + quota::QuotaClient::kDatabase, + webkit_database::GetOriginFromIdentifier(origin_identifier), + quota::kStorageTypeTemporary, + -deleted_size); + } + + return true; +} + +bool DatabaseTracker::IsDatabaseScheduledForDeletion( + const std::string& origin_identifier, + const base::string16& database_name) { + DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier); + if (it == dbs_to_be_deleted_.end()) + return false; + + std::set<base::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 (base::DirectoryExists(db_dir_)) { + base::FileEnumerator directories( + db_dir_, + false, + base::FileEnumerator::DIRECTORIES, + kTemporaryDirectoryPattern); + for (base::FilePath directory = directories.Next(); !directory.empty(); + directory = directories.Next()) { + base::DeleteFile(directory, true); + } + } + + // If the tracker database exists, but it's corrupt or doesn't + // have a meta table, delete the database directory. + const base::FilePath kTrackerDatabaseFullPath = + db_dir_.Append(base::FilePath(kTrackerDatabaseFileName)); + if (base::DirectoryExists(db_dir_) && + base::PathExists(kTrackerDatabaseFullPath) && + (!db_->Open(kTrackerDatabaseFullPath) || + !sql::MetaTable::DoesTableExist(db_.get()))) { + db_->Close(); + if (!base::DeleteFile(db_dir_, true)) + return false; + } + + db_->set_histogram_tag("DatabaseTracker"); + + 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 std::string& origin_identifier, + const base::string16& database_name, + const base::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 std::string& 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_->GetAllDatabaseDetailsForOriginIdentifier( + origin_identifier, &details)) { + return NULL; + } + + CachedOriginInfo& origin_info = origins_info_map_[origin_identifier]; + origin_info.SetOriginIdentifier(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 std::string& origin_identifier, + const base::string16& database_name) { + base::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 std::string& origin_id, const base::string16& name, + const base::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 std::string& origin_id, const base::string16& name, + const base::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_.get()) + quota_manager_proxy_->NotifyStorageModified( + quota::QuotaClient::kDatabase, + webkit_database::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 std::string& origin_identifier, + const base::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<base::string16>::const_iterator db = ori->second.begin(); + db != ori->second.end(); ++db) + ScheduleDatabaseForDeletion(ori->first, *db); + } +} + +int DatabaseTracker::DeleteDatabase(const std::string& origin_identifier, + const base::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<std::string> origins_identifiers; + if (!databases_table_->GetAllOriginIdentifiers(&origins_identifiers)) + return net::ERR_FAILED; + int rv = net::OK; + for (std::vector<std::string>::const_iterator ori = + origins_identifiers.begin(); + ori != origins_identifiers.end(); ++ori) { + if (special_storage_policy_.get() && + special_storage_policy_->IsStorageProtected( + webkit_database::GetOriginFromIdentifier(*ori))) { + continue; + } + + std::vector<DatabaseDetails> details; + if (!databases_table_-> + GetAllDatabaseDetailsForOriginIdentifier(*ori, &details)) + rv = net::ERR_FAILED; + for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); + db != details.end(); ++db) { + base::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 std::string& origin, const net::CompletionCallback& callback) { + if (!LazyInit()) + return net::ERR_FAILED; + + DatabaseSet to_be_deleted; + + std::vector<DatabaseDetails> details; + if (!databases_table_-> + GetAllDatabaseDetailsForOriginIdentifier(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 base::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 base::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 base::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 base::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); + + base::FilePath incognito_db_dir = + profile_path_.Append(kIncognitoDatabaseDirectoryName); + if (base::DirectoryExists(incognito_db_dir)) + base::DeleteFile(incognito_db_dir, true); +} + +void DatabaseTracker::ClearSessionOnlyOrigins() { + 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 (!has_session_only_databases) + return; + + if (!LazyInit()) + return; + + std::vector<std::string> origin_identifiers; + GetAllOriginIdentifiers(&origin_identifiers); + + for (std::vector<std::string>::iterator origin = + origin_identifiers.begin(); + origin != origin_identifiers.end(); ++origin) { + GURL origin_url = webkit_database::GetOriginFromIdentifier(*origin); + if (!special_storage_policy_->IsStorageSessionOnly(origin_url)) + continue; + if (special_storage_policy_->IsStorageProtected(origin_url)) + continue; + webkit_database::OriginInfo origin_info; + std::vector<base::string16> databases; + GetOriginInfo(*origin, &origin_info); + origin_info.GetAllDatabaseNames(&databases); + + for (std::vector<base::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 (!force_keep_session_state_) + ClearSessionOnlyOrigins(); +} + +void DatabaseTracker::SetForceKeepSessionState() { + DCHECK(db_tracker_thread_.get()); + if (!db_tracker_thread_->BelongsToCurrentThread()) { + db_tracker_thread_->PostTask( + FROM_HERE, + base::Bind(&DatabaseTracker::SetForceKeepSessionState, this)); + return; + } + force_keep_session_state_ = true; +} + +} // namespace webkit_database |