| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/browser/database/database_tracker.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/basictypes.h" | |
| 11 #include "base/bind.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/files/file.h" | |
| 14 #include "base/files/file_enumerator.h" | |
| 15 #include "base/message_loop/message_loop_proxy.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/utf_string_conversions.h" | |
| 18 #include "net/base/net_errors.h" | |
| 19 #include "sql/connection.h" | |
| 20 #include "sql/meta_table.h" | |
| 21 #include "sql/transaction.h" | |
| 22 #include "third_party/sqlite/sqlite3.h" | |
| 23 #include "webkit/browser/database/database_quota_client.h" | |
| 24 #include "webkit/browser/database/database_util.h" | |
| 25 #include "webkit/browser/database/databases_table.h" | |
| 26 #include "webkit/browser/quota/quota_manager_proxy.h" | |
| 27 #include "webkit/browser/quota/special_storage_policy.h" | |
| 28 #include "webkit/common/database/database_identifier.h" | |
| 29 | |
| 30 namespace storage { | |
| 31 | |
| 32 const base::FilePath::CharType kDatabaseDirectoryName[] = | |
| 33 FILE_PATH_LITERAL("databases"); | |
| 34 const base::FilePath::CharType kIncognitoDatabaseDirectoryName[] = | |
| 35 FILE_PATH_LITERAL("databases-incognito"); | |
| 36 const base::FilePath::CharType kTrackerDatabaseFileName[] = | |
| 37 FILE_PATH_LITERAL("Databases.db"); | |
| 38 static const int kCurrentVersion = 2; | |
| 39 static const int kCompatibleVersion = 1; | |
| 40 | |
| 41 const base::FilePath::CharType kTemporaryDirectoryPrefix[] = | |
| 42 FILE_PATH_LITERAL("DeleteMe"); | |
| 43 const base::FilePath::CharType kTemporaryDirectoryPattern[] = | |
| 44 FILE_PATH_LITERAL("DeleteMe*"); | |
| 45 | |
| 46 OriginInfo::OriginInfo() | |
| 47 : total_size_(0) {} | |
| 48 | |
| 49 OriginInfo::OriginInfo(const OriginInfo& origin_info) | |
| 50 : origin_identifier_(origin_info.origin_identifier_), | |
| 51 total_size_(origin_info.total_size_), | |
| 52 database_info_(origin_info.database_info_) {} | |
| 53 | |
| 54 OriginInfo::~OriginInfo() {} | |
| 55 | |
| 56 void OriginInfo::GetAllDatabaseNames( | |
| 57 std::vector<base::string16>* databases) const { | |
| 58 for (DatabaseInfoMap::const_iterator it = database_info_.begin(); | |
| 59 it != database_info_.end(); it++) { | |
| 60 databases->push_back(it->first); | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 int64 OriginInfo::GetDatabaseSize(const base::string16& database_name) const { | |
| 65 DatabaseInfoMap::const_iterator it = database_info_.find(database_name); | |
| 66 if (it != database_info_.end()) | |
| 67 return it->second.first; | |
| 68 return 0; | |
| 69 } | |
| 70 | |
| 71 base::string16 OriginInfo::GetDatabaseDescription( | |
| 72 const base::string16& database_name) const { | |
| 73 DatabaseInfoMap::const_iterator it = database_info_.find(database_name); | |
| 74 if (it != database_info_.end()) | |
| 75 return it->second.second; | |
| 76 return base::string16(); | |
| 77 } | |
| 78 | |
| 79 OriginInfo::OriginInfo(const std::string& origin_identifier, int64 total_size) | |
| 80 : origin_identifier_(origin_identifier), total_size_(total_size) {} | |
| 81 | |
| 82 DatabaseTracker::DatabaseTracker( | |
| 83 const base::FilePath& profile_path, | |
| 84 bool is_incognito, | |
| 85 storage::SpecialStoragePolicy* special_storage_policy, | |
| 86 storage::QuotaManagerProxy* quota_manager_proxy, | |
| 87 base::MessageLoopProxy* db_tracker_thread) | |
| 88 : is_initialized_(false), | |
| 89 is_incognito_(is_incognito), | |
| 90 force_keep_session_state_(false), | |
| 91 shutting_down_(false), | |
| 92 profile_path_(profile_path), | |
| 93 db_dir_(is_incognito_ | |
| 94 ? profile_path_.Append(kIncognitoDatabaseDirectoryName) | |
| 95 : profile_path_.Append(kDatabaseDirectoryName)), | |
| 96 db_(new sql::Connection()), | |
| 97 special_storage_policy_(special_storage_policy), | |
| 98 quota_manager_proxy_(quota_manager_proxy), | |
| 99 db_tracker_thread_(db_tracker_thread), | |
| 100 incognito_origin_directories_generator_(0) { | |
| 101 if (quota_manager_proxy) { | |
| 102 quota_manager_proxy->RegisterClient( | |
| 103 new DatabaseQuotaClient(db_tracker_thread, this)); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 DatabaseTracker::~DatabaseTracker() { | |
| 108 DCHECK(dbs_to_be_deleted_.empty()); | |
| 109 DCHECK(deletion_callbacks_.empty()); | |
| 110 } | |
| 111 | |
| 112 void DatabaseTracker::DatabaseOpened(const std::string& origin_identifier, | |
| 113 const base::string16& database_name, | |
| 114 const base::string16& database_description, | |
| 115 int64 estimated_size, | |
| 116 int64* database_size) { | |
| 117 if (shutting_down_ || !LazyInit()) { | |
| 118 *database_size = 0; | |
| 119 return; | |
| 120 } | |
| 121 | |
| 122 if (quota_manager_proxy_.get()) | |
| 123 quota_manager_proxy_->NotifyStorageAccessed( | |
| 124 storage::QuotaClient::kDatabase, | |
| 125 storage::GetOriginFromIdentifier(origin_identifier), | |
| 126 storage::kStorageTypeTemporary); | |
| 127 | |
| 128 InsertOrUpdateDatabaseDetails(origin_identifier, database_name, | |
| 129 database_description, estimated_size); | |
| 130 if (database_connections_.AddConnection(origin_identifier, database_name)) { | |
| 131 *database_size = SeedOpenDatabaseInfo(origin_identifier, | |
| 132 database_name, | |
| 133 database_description); | |
| 134 return; | |
| 135 } | |
| 136 *database_size = UpdateOpenDatabaseInfoAndNotify(origin_identifier, | |
| 137 database_name, | |
| 138 &database_description); | |
| 139 } | |
| 140 | |
| 141 void DatabaseTracker::DatabaseModified(const std::string& origin_identifier, | |
| 142 const base::string16& database_name) { | |
| 143 if (!LazyInit()) | |
| 144 return; | |
| 145 UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name); | |
| 146 } | |
| 147 | |
| 148 void DatabaseTracker::DatabaseClosed(const std::string& origin_identifier, | |
| 149 const base::string16& database_name) { | |
| 150 if (database_connections_.IsEmpty()) { | |
| 151 DCHECK(!is_initialized_); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 // We call NotifiyStorageAccessed when a db is opened and also when | |
| 156 // closed because we don't call it for read while open. | |
| 157 if (quota_manager_proxy_.get()) | |
| 158 quota_manager_proxy_->NotifyStorageAccessed( | |
| 159 storage::QuotaClient::kDatabase, | |
| 160 storage::GetOriginFromIdentifier(origin_identifier), | |
| 161 storage::kStorageTypeTemporary); | |
| 162 | |
| 163 UpdateOpenDatabaseSizeAndNotify(origin_identifier, database_name); | |
| 164 if (database_connections_.RemoveConnection(origin_identifier, database_name)) | |
| 165 DeleteDatabaseIfNeeded(origin_identifier, database_name); | |
| 166 } | |
| 167 | |
| 168 void DatabaseTracker::HandleSqliteError( | |
| 169 const std::string& origin_identifier, | |
| 170 const base::string16& database_name, | |
| 171 int error) { | |
| 172 // We only handle errors that indicate corruption and we | |
| 173 // do so with a heavy hand, we delete it. Any renderers/workers | |
| 174 // with this database open will receive a message to close it | |
| 175 // immediately, once all have closed, the files will be deleted. | |
| 176 // In the interim, all attempts to open a new connection to that | |
| 177 // database will fail. | |
| 178 // Note: the client-side filters out all but these two errors as | |
| 179 // a small optimization, see WebDatabaseObserverImpl::HandleSqliteError. | |
| 180 if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) { | |
| 181 DeleteDatabase(origin_identifier, database_name, | |
| 182 net::CompletionCallback()); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 void DatabaseTracker::CloseDatabases(const DatabaseConnections& connections) { | |
| 187 if (database_connections_.IsEmpty()) { | |
| 188 DCHECK(!is_initialized_ || connections.IsEmpty()); | |
| 189 return; | |
| 190 } | |
| 191 | |
| 192 // When being closed by this route, there's a chance that | |
| 193 // the tracker missed some DatabseModified calls. This method is used | |
| 194 // when a renderer crashes to cleanup its open resources. | |
| 195 // We need to examine what we have in connections for the | |
| 196 // size of each open databases and notify any differences between the | |
| 197 // actual file sizes now. | |
| 198 std::vector<std::pair<std::string, base::string16> > open_dbs; | |
| 199 connections.ListConnections(&open_dbs); | |
| 200 for (std::vector<std::pair<std::string, base::string16> >::iterator it = | |
| 201 open_dbs.begin(); it != open_dbs.end(); ++it) | |
| 202 UpdateOpenDatabaseSizeAndNotify(it->first, it->second); | |
| 203 | |
| 204 std::vector<std::pair<std::string, base::string16> > closed_dbs; | |
| 205 database_connections_.RemoveConnections(connections, &closed_dbs); | |
| 206 for (std::vector<std::pair<std::string, base::string16> >::iterator it = | |
| 207 closed_dbs.begin(); it != closed_dbs.end(); ++it) { | |
| 208 DeleteDatabaseIfNeeded(it->first, it->second); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 void DatabaseTracker::DeleteDatabaseIfNeeded( | |
| 213 const std::string& origin_identifier, | |
| 214 const base::string16& database_name) { | |
| 215 DCHECK(!database_connections_.IsDatabaseOpened(origin_identifier, | |
| 216 database_name)); | |
| 217 if (IsDatabaseScheduledForDeletion(origin_identifier, database_name)) { | |
| 218 DeleteClosedDatabase(origin_identifier, database_name); | |
| 219 dbs_to_be_deleted_[origin_identifier].erase(database_name); | |
| 220 if (dbs_to_be_deleted_[origin_identifier].empty()) | |
| 221 dbs_to_be_deleted_.erase(origin_identifier); | |
| 222 | |
| 223 PendingDeletionCallbacks::iterator callback = deletion_callbacks_.begin(); | |
| 224 while (callback != deletion_callbacks_.end()) { | |
| 225 DatabaseSet::iterator found_origin = | |
| 226 callback->second.find(origin_identifier); | |
| 227 if (found_origin != callback->second.end()) { | |
| 228 std::set<base::string16>& databases = found_origin->second; | |
| 229 databases.erase(database_name); | |
| 230 if (databases.empty()) { | |
| 231 callback->second.erase(found_origin); | |
| 232 if (callback->second.empty()) { | |
| 233 net::CompletionCallback cb = callback->first; | |
| 234 cb.Run(net::OK); | |
| 235 callback = deletion_callbacks_.erase(callback); | |
| 236 continue; | |
| 237 } | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 ++callback; | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 void DatabaseTracker::AddObserver(Observer* observer) { | |
| 247 observers_.AddObserver(observer); | |
| 248 } | |
| 249 | |
| 250 void DatabaseTracker::RemoveObserver(Observer* observer) { | |
| 251 // When we remove a listener, we do not know which cached information | |
| 252 // is still needed and which information can be discarded. So we just | |
| 253 // clear all caches and re-populate them as needed. | |
| 254 observers_.RemoveObserver(observer); | |
| 255 ClearAllCachedOriginInfo(); | |
| 256 } | |
| 257 | |
| 258 void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() { | |
| 259 ClearAllCachedOriginInfo(); | |
| 260 | |
| 261 if (!is_incognito_) { | |
| 262 meta_table_.reset(NULL); | |
| 263 databases_table_.reset(NULL); | |
| 264 db_->Close(); | |
| 265 is_initialized_ = false; | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 base::string16 DatabaseTracker::GetOriginDirectory( | |
| 270 const std::string& origin_identifier) { | |
| 271 if (!is_incognito_) | |
| 272 return base::UTF8ToUTF16(origin_identifier); | |
| 273 | |
| 274 OriginDirectoriesMap::const_iterator it = | |
| 275 incognito_origin_directories_.find(origin_identifier); | |
| 276 if (it != incognito_origin_directories_.end()) | |
| 277 return it->second; | |
| 278 | |
| 279 base::string16 origin_directory = | |
| 280 base::IntToString16(incognito_origin_directories_generator_++); | |
| 281 incognito_origin_directories_[origin_identifier] = origin_directory; | |
| 282 return origin_directory; | |
| 283 } | |
| 284 | |
| 285 base::FilePath DatabaseTracker::GetFullDBFilePath( | |
| 286 const std::string& origin_identifier, | |
| 287 const base::string16& database_name) { | |
| 288 DCHECK(!origin_identifier.empty()); | |
| 289 if (!LazyInit()) | |
| 290 return base::FilePath(); | |
| 291 | |
| 292 int64 id = databases_table_->GetDatabaseID(origin_identifier, database_name); | |
| 293 if (id < 0) | |
| 294 return base::FilePath(); | |
| 295 | |
| 296 return db_dir_.Append(base::FilePath::FromUTF16Unsafe( | |
| 297 GetOriginDirectory(origin_identifier))).AppendASCII( | |
| 298 base::Int64ToString(id)); | |
| 299 } | |
| 300 | |
| 301 bool DatabaseTracker::GetOriginInfo(const std::string& origin_identifier, | |
| 302 OriginInfo* info) { | |
| 303 DCHECK(info); | |
| 304 CachedOriginInfo* cached_info = GetCachedOriginInfo(origin_identifier); | |
| 305 if (!cached_info) | |
| 306 return false; | |
| 307 *info = OriginInfo(*cached_info); | |
| 308 return true; | |
| 309 } | |
| 310 | |
| 311 bool DatabaseTracker::GetAllOriginIdentifiers( | |
| 312 std::vector<std::string>* origin_identifiers) { | |
| 313 DCHECK(origin_identifiers); | |
| 314 DCHECK(origin_identifiers->empty()); | |
| 315 if (!LazyInit()) | |
| 316 return false; | |
| 317 return databases_table_->GetAllOriginIdentifiers(origin_identifiers); | |
| 318 } | |
| 319 | |
| 320 bool DatabaseTracker::GetAllOriginsInfo( | |
| 321 std::vector<OriginInfo>* origins_info) { | |
| 322 DCHECK(origins_info); | |
| 323 DCHECK(origins_info->empty()); | |
| 324 | |
| 325 std::vector<std::string> origins; | |
| 326 if (!GetAllOriginIdentifiers(&origins)) | |
| 327 return false; | |
| 328 | |
| 329 for (std::vector<std::string>::const_iterator it = origins.begin(); | |
| 330 it != origins.end(); it++) { | |
| 331 CachedOriginInfo* origin_info = GetCachedOriginInfo(*it); | |
| 332 if (!origin_info) { | |
| 333 // Restore 'origins_info' to its initial state. | |
| 334 origins_info->clear(); | |
| 335 return false; | |
| 336 } | |
| 337 origins_info->push_back(OriginInfo(*origin_info)); | |
| 338 } | |
| 339 | |
| 340 return true; | |
| 341 } | |
| 342 | |
| 343 bool DatabaseTracker::DeleteClosedDatabase( | |
| 344 const std::string& origin_identifier, | |
| 345 const base::string16& database_name) { | |
| 346 if (!LazyInit()) | |
| 347 return false; | |
| 348 | |
| 349 // Check if the database is opened by any renderer. | |
| 350 if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) | |
| 351 return false; | |
| 352 | |
| 353 int64 db_file_size = quota_manager_proxy_.get() | |
| 354 ? GetDBFileSize(origin_identifier, database_name) | |
| 355 : 0; | |
| 356 | |
| 357 // Try to delete the file on the hard drive. | |
| 358 base::FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); | |
| 359 if (!sql::Connection::Delete(db_file)) | |
| 360 return false; | |
| 361 | |
| 362 if (quota_manager_proxy_.get() && db_file_size) | |
| 363 quota_manager_proxy_->NotifyStorageModified( | |
| 364 storage::QuotaClient::kDatabase, | |
| 365 storage::GetOriginFromIdentifier(origin_identifier), | |
| 366 storage::kStorageTypeTemporary, | |
| 367 -db_file_size); | |
| 368 | |
| 369 // Clean up the main database and invalidate the cached record. | |
| 370 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); | |
| 371 origins_info_map_.erase(origin_identifier); | |
| 372 | |
| 373 std::vector<DatabaseDetails> details; | |
| 374 if (databases_table_->GetAllDatabaseDetailsForOriginIdentifier( | |
| 375 origin_identifier, &details) && details.empty()) { | |
| 376 // Try to delete the origin in case this was the last database. | |
| 377 DeleteOrigin(origin_identifier, false); | |
| 378 } | |
| 379 return true; | |
| 380 } | |
| 381 | |
| 382 bool DatabaseTracker::DeleteOrigin(const std::string& origin_identifier, | |
| 383 bool force) { | |
| 384 if (!LazyInit()) | |
| 385 return false; | |
| 386 | |
| 387 // Check if any database in this origin is opened by any renderer. | |
| 388 if (database_connections_.IsOriginUsed(origin_identifier) && !force) | |
| 389 return false; | |
| 390 | |
| 391 int64 deleted_size = 0; | |
| 392 if (quota_manager_proxy_.get()) { | |
| 393 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | |
| 394 if (origin_info) | |
| 395 deleted_size = origin_info->TotalSize(); | |
| 396 } | |
| 397 | |
| 398 origins_info_map_.erase(origin_identifier); | |
| 399 base::FilePath origin_dir = db_dir_.AppendASCII(origin_identifier); | |
| 400 | |
| 401 // Create a temporary directory to move possibly still existing databases to, | |
| 402 // as we can't delete the origin directory on windows if it contains opened | |
| 403 // files. | |
| 404 base::FilePath new_origin_dir; | |
| 405 base::CreateTemporaryDirInDir(db_dir_, | |
| 406 kTemporaryDirectoryPrefix, | |
| 407 &new_origin_dir); | |
| 408 base::FileEnumerator databases( | |
| 409 origin_dir, | |
| 410 false, | |
| 411 base::FileEnumerator::FILES); | |
| 412 for (base::FilePath database = databases.Next(); !database.empty(); | |
| 413 database = databases.Next()) { | |
| 414 base::FilePath new_file = new_origin_dir.Append(database.BaseName()); | |
| 415 base::Move(database, new_file); | |
| 416 } | |
| 417 base::DeleteFile(origin_dir, true); | |
| 418 base::DeleteFile(new_origin_dir, true); // might fail on windows. | |
| 419 | |
| 420 databases_table_->DeleteOriginIdentifier(origin_identifier); | |
| 421 | |
| 422 if (quota_manager_proxy_.get() && deleted_size) { | |
| 423 quota_manager_proxy_->NotifyStorageModified( | |
| 424 storage::QuotaClient::kDatabase, | |
| 425 storage::GetOriginFromIdentifier(origin_identifier), | |
| 426 storage::kStorageTypeTemporary, | |
| 427 -deleted_size); | |
| 428 } | |
| 429 | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 bool DatabaseTracker::IsDatabaseScheduledForDeletion( | |
| 434 const std::string& origin_identifier, | |
| 435 const base::string16& database_name) { | |
| 436 DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier); | |
| 437 if (it == dbs_to_be_deleted_.end()) | |
| 438 return false; | |
| 439 | |
| 440 std::set<base::string16>& databases = it->second; | |
| 441 return (databases.find(database_name) != databases.end()); | |
| 442 } | |
| 443 | |
| 444 bool DatabaseTracker::LazyInit() { | |
| 445 if (!is_initialized_ && !shutting_down_) { | |
| 446 DCHECK(!db_->is_open()); | |
| 447 DCHECK(!databases_table_.get()); | |
| 448 DCHECK(!meta_table_.get()); | |
| 449 | |
| 450 // If there are left-over directories from failed deletion attempts, clean | |
| 451 // them up. | |
| 452 if (base::DirectoryExists(db_dir_)) { | |
| 453 base::FileEnumerator directories( | |
| 454 db_dir_, | |
| 455 false, | |
| 456 base::FileEnumerator::DIRECTORIES, | |
| 457 kTemporaryDirectoryPattern); | |
| 458 for (base::FilePath directory = directories.Next(); !directory.empty(); | |
| 459 directory = directories.Next()) { | |
| 460 base::DeleteFile(directory, true); | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 // If the tracker database exists, but it's corrupt or doesn't | |
| 465 // have a meta table, delete the database directory. | |
| 466 const base::FilePath kTrackerDatabaseFullPath = | |
| 467 db_dir_.Append(base::FilePath(kTrackerDatabaseFileName)); | |
| 468 if (base::DirectoryExists(db_dir_) && | |
| 469 base::PathExists(kTrackerDatabaseFullPath) && | |
| 470 (!db_->Open(kTrackerDatabaseFullPath) || | |
| 471 !sql::MetaTable::DoesTableExist(db_.get()))) { | |
| 472 db_->Close(); | |
| 473 if (!base::DeleteFile(db_dir_, true)) | |
| 474 return false; | |
| 475 } | |
| 476 | |
| 477 db_->set_histogram_tag("DatabaseTracker"); | |
| 478 | |
| 479 databases_table_.reset(new DatabasesTable(db_.get())); | |
| 480 meta_table_.reset(new sql::MetaTable()); | |
| 481 | |
| 482 is_initialized_ = | |
| 483 base::CreateDirectory(db_dir_) && | |
| 484 (db_->is_open() || | |
| 485 (is_incognito_ ? db_->OpenInMemory() : | |
| 486 db_->Open(kTrackerDatabaseFullPath))) && | |
| 487 UpgradeToCurrentVersion(); | |
| 488 if (!is_initialized_) { | |
| 489 databases_table_.reset(NULL); | |
| 490 meta_table_.reset(NULL); | |
| 491 db_->Close(); | |
| 492 } | |
| 493 } | |
| 494 return is_initialized_; | |
| 495 } | |
| 496 | |
| 497 bool DatabaseTracker::UpgradeToCurrentVersion() { | |
| 498 sql::Transaction transaction(db_.get()); | |
| 499 if (!transaction.Begin() || | |
| 500 !meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) || | |
| 501 (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) || | |
| 502 !databases_table_->Init()) | |
| 503 return false; | |
| 504 | |
| 505 if (meta_table_->GetVersionNumber() < kCurrentVersion) | |
| 506 meta_table_->SetVersionNumber(kCurrentVersion); | |
| 507 | |
| 508 return transaction.Commit(); | |
| 509 } | |
| 510 | |
| 511 void DatabaseTracker::InsertOrUpdateDatabaseDetails( | |
| 512 const std::string& origin_identifier, | |
| 513 const base::string16& database_name, | |
| 514 const base::string16& database_description, | |
| 515 int64 estimated_size) { | |
| 516 DatabaseDetails details; | |
| 517 if (!databases_table_->GetDatabaseDetails( | |
| 518 origin_identifier, database_name, &details)) { | |
| 519 details.origin_identifier = origin_identifier; | |
| 520 details.database_name = database_name; | |
| 521 details.description = database_description; | |
| 522 details.estimated_size = estimated_size; | |
| 523 databases_table_->InsertDatabaseDetails(details); | |
| 524 } else if ((details.description != database_description) || | |
| 525 (details.estimated_size != estimated_size)) { | |
| 526 details.description = database_description; | |
| 527 details.estimated_size = estimated_size; | |
| 528 databases_table_->UpdateDatabaseDetails(details); | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 void DatabaseTracker::ClearAllCachedOriginInfo() { | |
| 533 origins_info_map_.clear(); | |
| 534 } | |
| 535 | |
| 536 DatabaseTracker::CachedOriginInfo* DatabaseTracker::MaybeGetCachedOriginInfo( | |
| 537 const std::string& origin_identifier, bool create_if_needed) { | |
| 538 if (!LazyInit()) | |
| 539 return NULL; | |
| 540 | |
| 541 // Populate the cache with data for this origin if needed. | |
| 542 if (origins_info_map_.find(origin_identifier) == origins_info_map_.end()) { | |
| 543 if (!create_if_needed) | |
| 544 return NULL; | |
| 545 | |
| 546 std::vector<DatabaseDetails> details; | |
| 547 if (!databases_table_->GetAllDatabaseDetailsForOriginIdentifier( | |
| 548 origin_identifier, &details)) { | |
| 549 return NULL; | |
| 550 } | |
| 551 | |
| 552 CachedOriginInfo& origin_info = origins_info_map_[origin_identifier]; | |
| 553 origin_info.SetOriginIdentifier(origin_identifier); | |
| 554 for (std::vector<DatabaseDetails>::const_iterator it = details.begin(); | |
| 555 it != details.end(); it++) { | |
| 556 int64 db_file_size; | |
| 557 if (database_connections_.IsDatabaseOpened( | |
| 558 origin_identifier, it->database_name)) { | |
| 559 db_file_size = database_connections_.GetOpenDatabaseSize( | |
| 560 origin_identifier, it->database_name); | |
| 561 } else { | |
| 562 db_file_size = GetDBFileSize(origin_identifier, it->database_name); | |
| 563 } | |
| 564 origin_info.SetDatabaseSize(it->database_name, db_file_size); | |
| 565 origin_info.SetDatabaseDescription(it->database_name, it->description); | |
| 566 } | |
| 567 } | |
| 568 | |
| 569 return &origins_info_map_[origin_identifier]; | |
| 570 } | |
| 571 | |
| 572 int64 DatabaseTracker::GetDBFileSize(const std::string& origin_identifier, | |
| 573 const base::string16& database_name) { | |
| 574 base::FilePath db_file_name = GetFullDBFilePath(origin_identifier, | |
| 575 database_name); | |
| 576 int64 db_file_size = 0; | |
| 577 if (!base::GetFileSize(db_file_name, &db_file_size)) | |
| 578 db_file_size = 0; | |
| 579 return db_file_size; | |
| 580 } | |
| 581 | |
| 582 int64 DatabaseTracker::SeedOpenDatabaseInfo( | |
| 583 const std::string& origin_id, const base::string16& name, | |
| 584 const base::string16& description) { | |
| 585 DCHECK(database_connections_.IsDatabaseOpened(origin_id, name)); | |
| 586 int64 size = GetDBFileSize(origin_id, name); | |
| 587 database_connections_.SetOpenDatabaseSize(origin_id, name, size); | |
| 588 CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); | |
| 589 if (info) { | |
| 590 info->SetDatabaseSize(name, size); | |
| 591 info->SetDatabaseDescription(name, description); | |
| 592 } | |
| 593 return size; | |
| 594 } | |
| 595 | |
| 596 int64 DatabaseTracker::UpdateOpenDatabaseInfoAndNotify( | |
| 597 const std::string& origin_id, const base::string16& name, | |
| 598 const base::string16* opt_description) { | |
| 599 DCHECK(database_connections_.IsDatabaseOpened(origin_id, name)); | |
| 600 int64 new_size = GetDBFileSize(origin_id, name); | |
| 601 int64 old_size = database_connections_.GetOpenDatabaseSize(origin_id, name); | |
| 602 CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); | |
| 603 if (info && opt_description) | |
| 604 info->SetDatabaseDescription(name, *opt_description); | |
| 605 if (old_size != new_size) { | |
| 606 database_connections_.SetOpenDatabaseSize(origin_id, name, new_size); | |
| 607 if (info) | |
| 608 info->SetDatabaseSize(name, new_size); | |
| 609 if (quota_manager_proxy_.get()) | |
| 610 quota_manager_proxy_->NotifyStorageModified( | |
| 611 storage::QuotaClient::kDatabase, | |
| 612 storage::GetOriginFromIdentifier(origin_id), | |
| 613 storage::kStorageTypeTemporary, | |
| 614 new_size - old_size); | |
| 615 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged( | |
| 616 origin_id, name, new_size)); | |
| 617 } | |
| 618 return new_size; | |
| 619 } | |
| 620 | |
| 621 void DatabaseTracker::ScheduleDatabaseForDeletion( | |
| 622 const std::string& origin_identifier, | |
| 623 const base::string16& database_name) { | |
| 624 DCHECK(database_connections_.IsDatabaseOpened(origin_identifier, | |
| 625 database_name)); | |
| 626 dbs_to_be_deleted_[origin_identifier].insert(database_name); | |
| 627 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseScheduledForDeletion( | |
| 628 origin_identifier, database_name)); | |
| 629 } | |
| 630 | |
| 631 void DatabaseTracker::ScheduleDatabasesForDeletion( | |
| 632 const DatabaseSet& databases, | |
| 633 const net::CompletionCallback& callback) { | |
| 634 DCHECK(!databases.empty()); | |
| 635 | |
| 636 if (!callback.is_null()) | |
| 637 deletion_callbacks_.push_back(std::make_pair(callback, databases)); | |
| 638 for (DatabaseSet::const_iterator ori = databases.begin(); | |
| 639 ori != databases.end(); ++ori) { | |
| 640 for (std::set<base::string16>::const_iterator db = ori->second.begin(); | |
| 641 db != ori->second.end(); ++db) | |
| 642 ScheduleDatabaseForDeletion(ori->first, *db); | |
| 643 } | |
| 644 } | |
| 645 | |
| 646 int DatabaseTracker::DeleteDatabase(const std::string& origin_identifier, | |
| 647 const base::string16& database_name, | |
| 648 const net::CompletionCallback& callback) { | |
| 649 if (!LazyInit()) | |
| 650 return net::ERR_FAILED; | |
| 651 | |
| 652 if (database_connections_.IsDatabaseOpened(origin_identifier, | |
| 653 database_name)) { | |
| 654 if (!callback.is_null()) { | |
| 655 DatabaseSet set; | |
| 656 set[origin_identifier].insert(database_name); | |
| 657 deletion_callbacks_.push_back(std::make_pair(callback, set)); | |
| 658 } | |
| 659 ScheduleDatabaseForDeletion(origin_identifier, database_name); | |
| 660 return net::ERR_IO_PENDING; | |
| 661 } | |
| 662 DeleteClosedDatabase(origin_identifier, database_name); | |
| 663 return net::OK; | |
| 664 } | |
| 665 | |
| 666 int DatabaseTracker::DeleteDataModifiedSince( | |
| 667 const base::Time& cutoff, | |
| 668 const net::CompletionCallback& callback) { | |
| 669 if (!LazyInit()) | |
| 670 return net::ERR_FAILED; | |
| 671 | |
| 672 DatabaseSet to_be_deleted; | |
| 673 | |
| 674 std::vector<std::string> origins_identifiers; | |
| 675 if (!databases_table_->GetAllOriginIdentifiers(&origins_identifiers)) | |
| 676 return net::ERR_FAILED; | |
| 677 int rv = net::OK; | |
| 678 for (std::vector<std::string>::const_iterator ori = | |
| 679 origins_identifiers.begin(); | |
| 680 ori != origins_identifiers.end(); ++ori) { | |
| 681 if (special_storage_policy_.get() && | |
| 682 special_storage_policy_->IsStorageProtected( | |
| 683 storage::GetOriginFromIdentifier(*ori))) { | |
| 684 continue; | |
| 685 } | |
| 686 | |
| 687 std::vector<DatabaseDetails> details; | |
| 688 if (!databases_table_-> | |
| 689 GetAllDatabaseDetailsForOriginIdentifier(*ori, &details)) | |
| 690 rv = net::ERR_FAILED; | |
| 691 for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); | |
| 692 db != details.end(); ++db) { | |
| 693 base::FilePath db_file = GetFullDBFilePath(*ori, db->database_name); | |
| 694 base::File::Info file_info; | |
| 695 base::GetFileInfo(db_file, &file_info); | |
| 696 if (file_info.last_modified < cutoff) | |
| 697 continue; | |
| 698 | |
| 699 // Check if the database is opened by any renderer. | |
| 700 if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) | |
| 701 to_be_deleted[*ori].insert(db->database_name); | |
| 702 else | |
| 703 DeleteClosedDatabase(*ori, db->database_name); | |
| 704 } | |
| 705 } | |
| 706 | |
| 707 if (rv != net::OK) | |
| 708 return rv; | |
| 709 | |
| 710 if (!to_be_deleted.empty()) { | |
| 711 ScheduleDatabasesForDeletion(to_be_deleted, callback); | |
| 712 return net::ERR_IO_PENDING; | |
| 713 } | |
| 714 return net::OK; | |
| 715 } | |
| 716 | |
| 717 int DatabaseTracker::DeleteDataForOrigin( | |
| 718 const std::string& origin, const net::CompletionCallback& callback) { | |
| 719 if (!LazyInit()) | |
| 720 return net::ERR_FAILED; | |
| 721 | |
| 722 DatabaseSet to_be_deleted; | |
| 723 | |
| 724 std::vector<DatabaseDetails> details; | |
| 725 if (!databases_table_-> | |
| 726 GetAllDatabaseDetailsForOriginIdentifier(origin, &details)) | |
| 727 return net::ERR_FAILED; | |
| 728 for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); | |
| 729 db != details.end(); ++db) { | |
| 730 // Check if the database is opened by any renderer. | |
| 731 if (database_connections_.IsDatabaseOpened(origin, db->database_name)) | |
| 732 to_be_deleted[origin].insert(db->database_name); | |
| 733 else | |
| 734 DeleteClosedDatabase(origin, db->database_name); | |
| 735 } | |
| 736 | |
| 737 if (!to_be_deleted.empty()) { | |
| 738 ScheduleDatabasesForDeletion(to_be_deleted, callback); | |
| 739 return net::ERR_IO_PENDING; | |
| 740 } | |
| 741 return net::OK; | |
| 742 } | |
| 743 | |
| 744 const base::File* DatabaseTracker::GetIncognitoFile( | |
| 745 const base::string16& vfs_file_name) const { | |
| 746 DCHECK(is_incognito_); | |
| 747 FileHandlesMap::const_iterator it = | |
| 748 incognito_file_handles_.find(vfs_file_name); | |
| 749 if (it != incognito_file_handles_.end()) | |
| 750 return it->second; | |
| 751 | |
| 752 return NULL; | |
| 753 } | |
| 754 | |
| 755 const base::File* DatabaseTracker::SaveIncognitoFile( | |
| 756 const base::string16& vfs_file_name, | |
| 757 base::File file) { | |
| 758 DCHECK(is_incognito_); | |
| 759 if (!file.IsValid()) | |
| 760 return NULL; | |
| 761 | |
| 762 base::File* to_insert = new base::File(file.Pass()); | |
| 763 std::pair<FileHandlesMap::iterator, bool> rv = | |
| 764 incognito_file_handles_.insert(std::make_pair(vfs_file_name, to_insert)); | |
| 765 DCHECK(rv.second); | |
| 766 return rv.first->second; | |
| 767 } | |
| 768 | |
| 769 void DatabaseTracker::CloseIncognitoFileHandle( | |
| 770 const base::string16& vfs_file_name) { | |
| 771 DCHECK(is_incognito_); | |
| 772 DCHECK(incognito_file_handles_.find(vfs_file_name) != | |
| 773 incognito_file_handles_.end()); | |
| 774 | |
| 775 FileHandlesMap::iterator it = incognito_file_handles_.find(vfs_file_name); | |
| 776 if (it != incognito_file_handles_.end()) { | |
| 777 delete it->second; | |
| 778 incognito_file_handles_.erase(it); | |
| 779 } | |
| 780 } | |
| 781 | |
| 782 bool DatabaseTracker::HasSavedIncognitoFileHandle( | |
| 783 const base::string16& vfs_file_name) const { | |
| 784 return (incognito_file_handles_.find(vfs_file_name) != | |
| 785 incognito_file_handles_.end()); | |
| 786 } | |
| 787 | |
| 788 void DatabaseTracker::DeleteIncognitoDBDirectory() { | |
| 789 is_initialized_ = false; | |
| 790 | |
| 791 for (FileHandlesMap::iterator it = incognito_file_handles_.begin(); | |
| 792 it != incognito_file_handles_.end(); it++) { | |
| 793 delete it->second; | |
| 794 } | |
| 795 | |
| 796 base::FilePath incognito_db_dir = | |
| 797 profile_path_.Append(kIncognitoDatabaseDirectoryName); | |
| 798 if (base::DirectoryExists(incognito_db_dir)) | |
| 799 base::DeleteFile(incognito_db_dir, true); | |
| 800 } | |
| 801 | |
| 802 void DatabaseTracker::ClearSessionOnlyOrigins() { | |
| 803 bool has_session_only_databases = | |
| 804 special_storage_policy_.get() && | |
| 805 special_storage_policy_->HasSessionOnlyOrigins(); | |
| 806 | |
| 807 // Clearing only session-only databases, and there are none. | |
| 808 if (!has_session_only_databases) | |
| 809 return; | |
| 810 | |
| 811 if (!LazyInit()) | |
| 812 return; | |
| 813 | |
| 814 std::vector<std::string> origin_identifiers; | |
| 815 GetAllOriginIdentifiers(&origin_identifiers); | |
| 816 | |
| 817 for (std::vector<std::string>::iterator origin = | |
| 818 origin_identifiers.begin(); | |
| 819 origin != origin_identifiers.end(); ++origin) { | |
| 820 GURL origin_url = storage::GetOriginFromIdentifier(*origin); | |
| 821 if (!special_storage_policy_->IsStorageSessionOnly(origin_url)) | |
| 822 continue; | |
| 823 if (special_storage_policy_->IsStorageProtected(origin_url)) | |
| 824 continue; | |
| 825 storage::OriginInfo origin_info; | |
| 826 std::vector<base::string16> databases; | |
| 827 GetOriginInfo(*origin, &origin_info); | |
| 828 origin_info.GetAllDatabaseNames(&databases); | |
| 829 | |
| 830 for (std::vector<base::string16>::iterator database = databases.begin(); | |
| 831 database != databases.end(); ++database) { | |
| 832 base::File file(GetFullDBFilePath(*origin, *database), | |
| 833 base::File::FLAG_OPEN_ALWAYS | | |
| 834 base::File::FLAG_SHARE_DELETE | | |
| 835 base::File::FLAG_DELETE_ON_CLOSE | | |
| 836 base::File::FLAG_READ); | |
| 837 } | |
| 838 DeleteOrigin(*origin, true); | |
| 839 } | |
| 840 } | |
| 841 | |
| 842 | |
| 843 void DatabaseTracker::Shutdown() { | |
| 844 DCHECK(db_tracker_thread_.get()); | |
| 845 DCHECK(db_tracker_thread_->BelongsToCurrentThread()); | |
| 846 if (shutting_down_) { | |
| 847 NOTREACHED(); | |
| 848 return; | |
| 849 } | |
| 850 shutting_down_ = true; | |
| 851 if (is_incognito_) | |
| 852 DeleteIncognitoDBDirectory(); | |
| 853 else if (!force_keep_session_state_) | |
| 854 ClearSessionOnlyOrigins(); | |
| 855 CloseTrackerDatabaseAndClearCaches(); | |
| 856 } | |
| 857 | |
| 858 void DatabaseTracker::SetForceKeepSessionState() { | |
| 859 DCHECK(db_tracker_thread_.get()); | |
| 860 if (!db_tracker_thread_->BelongsToCurrentThread()) { | |
| 861 db_tracker_thread_->PostTask( | |
| 862 FROM_HERE, | |
| 863 base::Bind(&DatabaseTracker::SetForceKeepSessionState, this)); | |
| 864 return; | |
| 865 } | |
| 866 force_keep_session_state_ = true; | |
| 867 } | |
| 868 | |
| 869 } // namespace storage | |
| OLD | NEW |