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_enumerator.h" 14 #include "base/message_loop/message_loop_proxy.h" 15 #include "base/platform_file.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.h" 27 #include "webkit/browser/quota/special_storage_policy.h" 28 #include "webkit/common/database/database_identifier.h" 29 30 namespace webkit_database { 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 quota::SpecialStoragePolicy* special_storage_policy, 86 quota::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 quota::QuotaClient::kDatabase, 125 webkit_database::GetOriginFromIdentifier(origin_identifier), 126 quota::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 quota::QuotaClient::kDatabase, 160 webkit_database::GetOriginFromIdentifier(origin_identifier), 161 quota::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( 293 origin_identifier, database_name); 294 if (id < 0) 295 return base::FilePath(); 296 297 base::FilePath file_name = base::FilePath::FromWStringHack( 298 UTF8ToWide(base::Int64ToString(id))); 299 return db_dir_.Append(base::FilePath::FromWStringHack( 300 UTF16ToWide(GetOriginDirectory(origin_identifier)))).Append(file_name); 301 } 302 303 bool DatabaseTracker::GetOriginInfo(const std::string& origin_identifier, 304 OriginInfo* info) { 305 DCHECK(info); 306 CachedOriginInfo* cached_info = GetCachedOriginInfo(origin_identifier); 307 if (!cached_info) 308 return false; 309 *info = OriginInfo(*cached_info); 310 return true; 311 } 312 313 bool DatabaseTracker::GetAllOriginIdentifiers( 314 std::vector<std::string>* origin_identifiers) { 315 DCHECK(origin_identifiers); 316 DCHECK(origin_identifiers->empty()); 317 if (!LazyInit()) 318 return false; 319 return databases_table_->GetAllOriginIdentifiers(origin_identifiers); 320 } 321 322 bool DatabaseTracker::GetAllOriginsInfo( 323 std::vector<OriginInfo>* origins_info) { 324 DCHECK(origins_info); 325 DCHECK(origins_info->empty()); 326 327 std::vector<std::string> origins; 328 if (!GetAllOriginIdentifiers(&origins)) 329 return false; 330 331 for (std::vector<std::string>::const_iterator it = origins.begin(); 332 it != origins.end(); it++) { 333 CachedOriginInfo* origin_info = GetCachedOriginInfo(*it); 334 if (!origin_info) { 335 // Restore 'origins_info' to its initial state. 336 origins_info->clear(); 337 return false; 338 } 339 origins_info->push_back(OriginInfo(*origin_info)); 340 } 341 342 return true; 343 } 344 345 bool DatabaseTracker::DeleteClosedDatabase( 346 const std::string& origin_identifier, 347 const base::string16& database_name) { 348 if (!LazyInit()) 349 return false; 350 351 // Check if the database is opened by any renderer. 352 if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) 353 return false; 354 355 int64 db_file_size = quota_manager_proxy_.get() 356 ? GetDBFileSize(origin_identifier, database_name) 357 : 0; 358 359 // Try to delete the file on the hard drive. 360 base::FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); 361 if (!sql::Connection::Delete(db_file)) 362 return false; 363 364 if (quota_manager_proxy_.get() && db_file_size) 365 quota_manager_proxy_->NotifyStorageModified( 366 quota::QuotaClient::kDatabase, 367 webkit_database::GetOriginFromIdentifier(origin_identifier), 368 quota::kStorageTypeTemporary, 369 -db_file_size); 370 371 // Clean up the main database and invalidate the cached record. 372 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); 373 origins_info_map_.erase(origin_identifier); 374 375 std::vector<DatabaseDetails> details; 376 if (databases_table_->GetAllDatabaseDetailsForOriginIdentifier( 377 origin_identifier, &details) && details.empty()) { 378 // Try to delete the origin in case this was the last database. 379 DeleteOrigin(origin_identifier, false); 380 } 381 return true; 382 } 383 384 bool DatabaseTracker::DeleteOrigin(const std::string& origin_identifier, 385 bool force) { 386 if (!LazyInit()) 387 return false; 388 389 // Check if any database in this origin is opened by any renderer. 390 if (database_connections_.IsOriginUsed(origin_identifier) && !force) 391 return false; 392 393 int64 deleted_size = 0; 394 if (quota_manager_proxy_.get()) { 395 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); 396 if (origin_info) 397 deleted_size = origin_info->TotalSize(); 398 } 399 400 origins_info_map_.erase(origin_identifier); 401 base::FilePath origin_dir = db_dir_.AppendASCII(origin_identifier); 402 403 // Create a temporary directory to move possibly still existing databases to, 404 // as we can't delete the origin directory on windows if it contains opened 405 // files. 406 base::FilePath new_origin_dir; 407 file_util::CreateTemporaryDirInDir(db_dir_, 408 kTemporaryDirectoryPrefix, 409 &new_origin_dir); 410 base::FileEnumerator databases( 411 origin_dir, 412 false, 413 base::FileEnumerator::FILES); 414 for (base::FilePath database = databases.Next(); !database.empty(); 415 database = databases.Next()) { 416 base::FilePath new_file = new_origin_dir.Append(database.BaseName()); 417 base::Move(database, new_file); 418 } 419 base::DeleteFile(origin_dir, true); 420 base::DeleteFile(new_origin_dir, true); // might fail on windows. 421 422 databases_table_->DeleteOriginIdentifier(origin_identifier); 423 424 if (quota_manager_proxy_.get() && deleted_size) { 425 quota_manager_proxy_->NotifyStorageModified( 426 quota::QuotaClient::kDatabase, 427 webkit_database::GetOriginFromIdentifier(origin_identifier), 428 quota::kStorageTypeTemporary, 429 -deleted_size); 430 } 431 432 return true; 433 } 434 435 bool DatabaseTracker::IsDatabaseScheduledForDeletion( 436 const std::string& origin_identifier, 437 const base::string16& database_name) { 438 DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier); 439 if (it == dbs_to_be_deleted_.end()) 440 return false; 441 442 std::set<base::string16>& databases = it->second; 443 return (databases.find(database_name) != databases.end()); 444 } 445 446 bool DatabaseTracker::LazyInit() { 447 if (!is_initialized_ && !shutting_down_) { 448 DCHECK(!db_->is_open()); 449 DCHECK(!databases_table_.get()); 450 DCHECK(!meta_table_.get()); 451 452 // If there are left-over directories from failed deletion attempts, clean 453 // them up. 454 if (base::DirectoryExists(db_dir_)) { 455 base::FileEnumerator directories( 456 db_dir_, 457 false, 458 base::FileEnumerator::DIRECTORIES, 459 kTemporaryDirectoryPattern); 460 for (base::FilePath directory = directories.Next(); !directory.empty(); 461 directory = directories.Next()) { 462 base::DeleteFile(directory, true); 463 } 464 } 465 466 // If the tracker database exists, but it's corrupt or doesn't 467 // have a meta table, delete the database directory. 468 const base::FilePath kTrackerDatabaseFullPath = 469 db_dir_.Append(base::FilePath(kTrackerDatabaseFileName)); 470 if (base::DirectoryExists(db_dir_) && 471 base::PathExists(kTrackerDatabaseFullPath) && 472 (!db_->Open(kTrackerDatabaseFullPath) || 473 !sql::MetaTable::DoesTableExist(db_.get()))) { 474 db_->Close(); 475 if (!base::DeleteFile(db_dir_, true)) 476 return false; 477 } 478 479 db_->set_histogram_tag("DatabaseTracker"); 480 481 databases_table_.reset(new DatabasesTable(db_.get())); 482 meta_table_.reset(new sql::MetaTable()); 483 484 is_initialized_ = 485 file_util::CreateDirectory(db_dir_) && 486 (db_->is_open() || 487 (is_incognito_ ? db_->OpenInMemory() : 488 db_->Open(kTrackerDatabaseFullPath))) && 489 UpgradeToCurrentVersion(); 490 if (!is_initialized_) { 491 databases_table_.reset(NULL); 492 meta_table_.reset(NULL); 493 db_->Close(); 494 } 495 } 496 return is_initialized_; 497 } 498 499 bool DatabaseTracker::UpgradeToCurrentVersion() { 500 sql::Transaction transaction(db_.get()); 501 if (!transaction.Begin() || 502 !meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) || 503 (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) || 504 !databases_table_->Init()) 505 return false; 506 507 if (meta_table_->GetVersionNumber() < kCurrentVersion) 508 meta_table_->SetVersionNumber(kCurrentVersion); 509 510 return transaction.Commit(); 511 } 512 513 void DatabaseTracker::InsertOrUpdateDatabaseDetails( 514 const std::string& origin_identifier, 515 const base::string16& database_name, 516 const base::string16& database_description, 517 int64 estimated_size) { 518 DatabaseDetails details; 519 if (!databases_table_->GetDatabaseDetails( 520 origin_identifier, database_name, &details)) { 521 details.origin_identifier = origin_identifier; 522 details.database_name = database_name; 523 details.description = database_description; 524 details.estimated_size = estimated_size; 525 databases_table_->InsertDatabaseDetails(details); 526 } else if ((details.description != database_description) || 527 (details.estimated_size != estimated_size)) { 528 details.description = database_description; 529 details.estimated_size = estimated_size; 530 databases_table_->UpdateDatabaseDetails(details); 531 } 532 } 533 534 void DatabaseTracker::ClearAllCachedOriginInfo() { 535 origins_info_map_.clear(); 536 } 537 538 DatabaseTracker::CachedOriginInfo* DatabaseTracker::MaybeGetCachedOriginInfo( 539 const std::string& origin_identifier, bool create_if_needed) { 540 if (!LazyInit()) 541 return NULL; 542 543 // Populate the cache with data for this origin if needed. 544 if (origins_info_map_.find(origin_identifier) == origins_info_map_.end()) { 545 if (!create_if_needed) 546 return NULL; 547 548 std::vector<DatabaseDetails> details; 549 if (!databases_table_->GetAllDatabaseDetailsForOriginIdentifier( 550 origin_identifier, &details)) { 551 return NULL; 552 } 553 554 CachedOriginInfo& origin_info = origins_info_map_[origin_identifier]; 555 origin_info.SetOriginIdentifier(origin_identifier); 556 for (std::vector<DatabaseDetails>::const_iterator it = details.begin(); 557 it != details.end(); it++) { 558 int64 db_file_size; 559 if (database_connections_.IsDatabaseOpened( 560 origin_identifier, it->database_name)) { 561 db_file_size = database_connections_.GetOpenDatabaseSize( 562 origin_identifier, it->database_name); 563 } else { 564 db_file_size = GetDBFileSize(origin_identifier, it->database_name); 565 } 566 origin_info.SetDatabaseSize(it->database_name, db_file_size); 567 origin_info.SetDatabaseDescription(it->database_name, it->description); 568 } 569 } 570 571 return &origins_info_map_[origin_identifier]; 572 } 573 574 int64 DatabaseTracker::GetDBFileSize(const std::string& origin_identifier, 575 const base::string16& database_name) { 576 base::FilePath db_file_name = GetFullDBFilePath(origin_identifier, 577 database_name); 578 int64 db_file_size = 0; 579 if (!file_util::GetFileSize(db_file_name, &db_file_size)) 580 db_file_size = 0; 581 return db_file_size; 582 } 583 584 int64 DatabaseTracker::SeedOpenDatabaseInfo( 585 const std::string& origin_id, const base::string16& name, 586 const base::string16& description) { 587 DCHECK(database_connections_.IsDatabaseOpened(origin_id, name)); 588 int64 size = GetDBFileSize(origin_id, name); 589 database_connections_.SetOpenDatabaseSize(origin_id, name, size); 590 CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); 591 if (info) { 592 info->SetDatabaseSize(name, size); 593 info->SetDatabaseDescription(name, description); 594 } 595 return size; 596 } 597 598 int64 DatabaseTracker::UpdateOpenDatabaseInfoAndNotify( 599 const std::string& origin_id, const base::string16& name, 600 const base::string16* opt_description) { 601 DCHECK(database_connections_.IsDatabaseOpened(origin_id, name)); 602 int64 new_size = GetDBFileSize(origin_id, name); 603 int64 old_size = database_connections_.GetOpenDatabaseSize(origin_id, name); 604 CachedOriginInfo* info = MaybeGetCachedOriginInfo(origin_id, false); 605 if (info && opt_description) 606 info->SetDatabaseDescription(name, *opt_description); 607 if (old_size != new_size) { 608 database_connections_.SetOpenDatabaseSize(origin_id, name, new_size); 609 if (info) 610 info->SetDatabaseSize(name, new_size); 611 if (quota_manager_proxy_.get()) 612 quota_manager_proxy_->NotifyStorageModified( 613 quota::QuotaClient::kDatabase, 614 webkit_database::GetOriginFromIdentifier(origin_id), 615 quota::kStorageTypeTemporary, 616 new_size - old_size); 617 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged( 618 origin_id, name, new_size)); 619 } 620 return new_size; 621 } 622 623 void DatabaseTracker::ScheduleDatabaseForDeletion( 624 const std::string& origin_identifier, 625 const base::string16& database_name) { 626 DCHECK(database_connections_.IsDatabaseOpened(origin_identifier, 627 database_name)); 628 dbs_to_be_deleted_[origin_identifier].insert(database_name); 629 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseScheduledForDeletion( 630 origin_identifier, database_name)); 631 } 632 633 void DatabaseTracker::ScheduleDatabasesForDeletion( 634 const DatabaseSet& databases, 635 const net::CompletionCallback& callback) { 636 DCHECK(!databases.empty()); 637 638 if (!callback.is_null()) 639 deletion_callbacks_.push_back(std::make_pair(callback, databases)); 640 for (DatabaseSet::const_iterator ori = databases.begin(); 641 ori != databases.end(); ++ori) { 642 for (std::set<base::string16>::const_iterator db = ori->second.begin(); 643 db != ori->second.end(); ++db) 644 ScheduleDatabaseForDeletion(ori->first, *db); 645 } 646 } 647 648 int DatabaseTracker::DeleteDatabase(const std::string& origin_identifier, 649 const base::string16& database_name, 650 const net::CompletionCallback& callback) { 651 if (!LazyInit()) 652 return net::ERR_FAILED; 653 654 if (database_connections_.IsDatabaseOpened(origin_identifier, 655 database_name)) { 656 if (!callback.is_null()) { 657 DatabaseSet set; 658 set[origin_identifier].insert(database_name); 659 deletion_callbacks_.push_back(std::make_pair(callback, set)); 660 } 661 ScheduleDatabaseForDeletion(origin_identifier, database_name); 662 return net::ERR_IO_PENDING; 663 } 664 DeleteClosedDatabase(origin_identifier, database_name); 665 return net::OK; 666 } 667 668 int DatabaseTracker::DeleteDataModifiedSince( 669 const base::Time& cutoff, 670 const net::CompletionCallback& callback) { 671 if (!LazyInit()) 672 return net::ERR_FAILED; 673 674 DatabaseSet to_be_deleted; 675 676 std::vector<std::string> origins_identifiers; 677 if (!databases_table_->GetAllOriginIdentifiers(&origins_identifiers)) 678 return net::ERR_FAILED; 679 int rv = net::OK; 680 for (std::vector<std::string>::const_iterator ori = 681 origins_identifiers.begin(); 682 ori != origins_identifiers.end(); ++ori) { 683 if (special_storage_policy_.get() && 684 special_storage_policy_->IsStorageProtected( 685 webkit_database::GetOriginFromIdentifier(*ori))) { 686 continue; 687 } 688 689 std::vector<DatabaseDetails> details; 690 if (!databases_table_-> 691 GetAllDatabaseDetailsForOriginIdentifier(*ori, &details)) 692 rv = net::ERR_FAILED; 693 for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); 694 db != details.end(); ++db) { 695 base::FilePath db_file = GetFullDBFilePath(*ori, db->database_name); 696 base::PlatformFileInfo file_info; 697 file_util::GetFileInfo(db_file, &file_info); 698 if (file_info.last_modified < cutoff) 699 continue; 700 701 // Check if the database is opened by any renderer. 702 if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) 703 to_be_deleted[*ori].insert(db->database_name); 704 else 705 DeleteClosedDatabase(*ori, db->database_name); 706 } 707 } 708 709 if (rv != net::OK) 710 return rv; 711 712 if (!to_be_deleted.empty()) { 713 ScheduleDatabasesForDeletion(to_be_deleted, callback); 714 return net::ERR_IO_PENDING; 715 } 716 return net::OK; 717 } 718 719 int DatabaseTracker::DeleteDataForOrigin( 720 const std::string& origin, const net::CompletionCallback& callback) { 721 if (!LazyInit()) 722 return net::ERR_FAILED; 723 724 DatabaseSet to_be_deleted; 725 726 std::vector<DatabaseDetails> details; 727 if (!databases_table_-> 728 GetAllDatabaseDetailsForOriginIdentifier(origin, &details)) 729 return net::ERR_FAILED; 730 for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); 731 db != details.end(); ++db) { 732 // Check if the database is opened by any renderer. 733 if (database_connections_.IsDatabaseOpened(origin, db->database_name)) 734 to_be_deleted[origin].insert(db->database_name); 735 else 736 DeleteClosedDatabase(origin, db->database_name); 737 } 738 739 if (!to_be_deleted.empty()) { 740 ScheduleDatabasesForDeletion(to_be_deleted, callback); 741 return net::ERR_IO_PENDING; 742 } 743 return net::OK; 744 } 745 746 void DatabaseTracker::GetIncognitoFileHandle( 747 const base::string16& vfs_file_name, 748 base::PlatformFile* file_handle) const { 749 DCHECK(is_incognito_); 750 FileHandlesMap::const_iterator it = 751 incognito_file_handles_.find(vfs_file_name); 752 if (it != incognito_file_handles_.end()) 753 *file_handle = it->second; 754 else 755 *file_handle = base::kInvalidPlatformFileValue; 756 } 757 758 void DatabaseTracker::SaveIncognitoFileHandle( 759 const base::string16& vfs_file_name, 760 const base::PlatformFile& file_handle) { 761 DCHECK(is_incognito_); 762 DCHECK(incognito_file_handles_.find(vfs_file_name) == 763 incognito_file_handles_.end()); 764 if (file_handle != base::kInvalidPlatformFileValue) 765 incognito_file_handles_[vfs_file_name] = file_handle; 766 } 767 768 bool DatabaseTracker::CloseIncognitoFileHandle( 769 const base::string16& vfs_file_name) { 770 DCHECK(is_incognito_); 771 DCHECK(incognito_file_handles_.find(vfs_file_name) != 772 incognito_file_handles_.end()); 773 774 bool handle_closed = false; 775 FileHandlesMap::iterator it = incognito_file_handles_.find(vfs_file_name); 776 if (it != incognito_file_handles_.end()) { 777 handle_closed = base::ClosePlatformFile(it->second); 778 if (handle_closed) 779 incognito_file_handles_.erase(it); 780 } 781 return handle_closed; 782 } 783 784 bool DatabaseTracker::HasSavedIncognitoFileHandle( 785 const base::string16& vfs_file_name) const { 786 return (incognito_file_handles_.find(vfs_file_name) != 787 incognito_file_handles_.end()); 788 } 789 790 void DatabaseTracker::DeleteIncognitoDBDirectory() { 791 shutting_down_ = true; 792 is_initialized_ = false; 793 794 for (FileHandlesMap::iterator it = incognito_file_handles_.begin(); 795 it != incognito_file_handles_.end(); it++) 796 base::ClosePlatformFile(it->second); 797 798 base::FilePath incognito_db_dir = 799 profile_path_.Append(kIncognitoDatabaseDirectoryName); 800 if (base::DirectoryExists(incognito_db_dir)) 801 base::DeleteFile(incognito_db_dir, true); 802 } 803 804 void DatabaseTracker::ClearSessionOnlyOrigins() { 805 shutting_down_ = true; 806 807 bool has_session_only_databases = 808 special_storage_policy_.get() && 809 special_storage_policy_->HasSessionOnlyOrigins(); 810 811 // Clearing only session-only databases, and there are none. 812 if (!has_session_only_databases) 813 return; 814 815 if (!LazyInit()) 816 return; 817 818 std::vector<std::string> origin_identifiers; 819 GetAllOriginIdentifiers(&origin_identifiers); 820 821 for (std::vector<std::string>::iterator origin = 822 origin_identifiers.begin(); 823 origin != origin_identifiers.end(); ++origin) { 824 GURL origin_url = webkit_database::GetOriginFromIdentifier(*origin); 825 if (!special_storage_policy_->IsStorageSessionOnly(origin_url)) 826 continue; 827 if (special_storage_policy_->IsStorageProtected(origin_url)) 828 continue; 829 webkit_database::OriginInfo origin_info; 830 std::vector<base::string16> databases; 831 GetOriginInfo(*origin, &origin_info); 832 origin_info.GetAllDatabaseNames(&databases); 833 834 for (std::vector<base::string16>::iterator database = databases.begin(); 835 database != databases.end(); ++database) { 836 base::PlatformFile file_handle = base::CreatePlatformFile( 837 GetFullDBFilePath(*origin, *database), 838 base::PLATFORM_FILE_OPEN_ALWAYS | 839 base::PLATFORM_FILE_SHARE_DELETE | 840 base::PLATFORM_FILE_DELETE_ON_CLOSE | 841 base::PLATFORM_FILE_READ, 842 NULL, NULL); 843 base::ClosePlatformFile(file_handle); 844 } 845 DeleteOrigin(*origin, true); 846 } 847 } 848 849 850 void DatabaseTracker::Shutdown() { 851 DCHECK(db_tracker_thread_.get()); 852 DCHECK(db_tracker_thread_->BelongsToCurrentThread()); 853 if (shutting_down_) { 854 NOTREACHED(); 855 return; 856 } 857 if (is_incognito_) 858 DeleteIncognitoDBDirectory(); 859 else if (!force_keep_session_state_) 860 ClearSessionOnlyOrigins(); 861 } 862 863 void DatabaseTracker::SetForceKeepSessionState() { 864 DCHECK(db_tracker_thread_.get()); 865 if (!db_tracker_thread_->BelongsToCurrentThread()) { 866 db_tracker_thread_->PostTask( 867 FROM_HERE, 868 base::Bind(&DatabaseTracker::SetForceKeepSessionState, this)); 869 return; 870 } 871 force_keep_session_state_ = true; 872 } 873 874 } // namespace webkit_database 875