Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/database/database_tracker.h" | 5 #include "webkit/database/database_tracker.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "app/sql/connection.h" | 10 #include "app/sql/connection.h" |
| 11 #include "app/sql/diagnostic_error_delegate.h" | 11 #include "app/sql/diagnostic_error_delegate.h" |
| 12 #include "app/sql/meta_table.h" | 12 #include "app/sql/meta_table.h" |
| 13 #include "app/sql/statement.h" | 13 #include "app/sql/statement.h" |
| 14 #include "app/sql/transaction.h" | 14 #include "app/sql/transaction.h" |
| 15 #include "base/basictypes.h" | 15 #include "base/basictypes.h" |
| 16 #include "base/file_util.h" | 16 #include "base/file_util.h" |
| 17 #include "base/message_loop_proxy.h" | 17 #include "base/message_loop_proxy.h" |
| 18 #include "base/platform_file.h" | |
| 18 #include "base/string_number_conversions.h" | 19 #include "base/string_number_conversions.h" |
| 19 #include "base/utf_string_conversions.h" | 20 #include "base/utf_string_conversions.h" |
| 20 #include "net/base/net_errors.h" | 21 #include "net/base/net_errors.h" |
| 21 #include "webkit/database/database_quota_client.h" | 22 #include "webkit/database/database_quota_client.h" |
| 22 #include "webkit/database/database_util.h" | 23 #include "webkit/database/database_util.h" |
| 23 #include "webkit/database/databases_table.h" | 24 #include "webkit/database/databases_table.h" |
| 24 #include "webkit/quota/quota_manager.h" | 25 #include "webkit/quota/quota_manager.h" |
| 25 #include "webkit/quota/special_storage_policy.h" | 26 #include "webkit/quota/special_storage_policy.h" |
| 26 | 27 |
| 27 namespace { | 28 namespace { |
| 28 | 29 |
| 29 class HistogramUniquifier { | 30 class HistogramUniquifier { |
| 30 public: | 31 public: |
| 31 static const char* name() { return "Sqlite.DatabaseTracker.Error"; } | 32 static const char* name() { return "Sqlite.DatabaseTracker.Error"; } |
| 32 }; | 33 }; |
| 33 | 34 |
| 34 sql::ErrorDelegate* GetErrorHandlerForTrackerDb() { | 35 sql::ErrorDelegate* GetErrorHandlerForTrackerDb() { |
| 35 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>(); | 36 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>(); |
| 36 } | 37 } |
| 37 | 38 |
| 39 void ClearLocalState(webkit_database::DatabaseTracker* tracker) { | |
|
michaeln
2011/07/01 03:02:34
What guarantees are there that the tracker has not
jochen (gone - plz use gerrit)
2011/07/06 09:24:53
Done.
| |
| 40 std::vector<string16> origin_identifiers; | |
| 41 tracker->GetAllOriginIdentifiers(&origin_identifiers); | |
| 42 | |
| 43 for (std::vector<string16>::iterator origin = origin_identifiers.begin(); | |
| 44 origin != origin_identifiers.end(); ++origin) { | |
| 45 if (tracker->special_storage_policy() && | |
| 46 tracker->special_storage_policy()->IsStorageProtected( | |
| 47 webkit_database::DatabaseUtil::GetOriginFromIdentifier(*origin))) { | |
| 48 continue; | |
| 49 } | |
| 50 webkit_database::OriginInfo origin_info; | |
| 51 std::vector<string16> databases; | |
| 52 tracker->GetOriginInfo(*origin, &origin_info); | |
| 53 origin_info.GetAllDatabaseNames(&databases); | |
| 54 | |
| 55 for (std::vector<string16>::iterator database = databases.begin(); | |
| 56 database != databases.end(); ++database) { | |
| 57 base::PlatformFile file_handle = base::CreatePlatformFile( | |
| 58 tracker->GetFullDBFilePath(*origin, *database), | |
| 59 base::PLATFORM_FILE_OPEN_ALWAYS | | |
| 60 base::PLATFORM_FILE_SHARE_DELETE | | |
| 61 base::PLATFORM_FILE_DELETE_ON_CLOSE | | |
| 62 base::PLATFORM_FILE_READ, | |
| 63 NULL, NULL); | |
| 64 base::ClosePlatformFile(file_handle); | |
| 65 } | |
| 66 tracker->DeleteOrigin(*origin, true); | |
| 67 } | |
| 68 } | |
| 69 | |
| 38 } // anon namespace | 70 } // anon namespace |
| 39 | 71 |
| 40 namespace webkit_database { | 72 namespace webkit_database { |
| 41 | 73 |
| 42 const FilePath::CharType kDatabaseDirectoryName[] = | 74 const FilePath::CharType kDatabaseDirectoryName[] = |
| 43 FILE_PATH_LITERAL("databases"); | 75 FILE_PATH_LITERAL("databases"); |
| 44 const FilePath::CharType kIncognitoDatabaseDirectoryName[] = | 76 const FilePath::CharType kIncognitoDatabaseDirectoryName[] = |
| 45 FILE_PATH_LITERAL("databases-incognito"); | 77 FILE_PATH_LITERAL("databases-incognito"); |
| 46 const FilePath::CharType kTrackerDatabaseFileName[] = | 78 const FilePath::CharType kTrackerDatabaseFileName[] = |
| 47 FILE_PATH_LITERAL("Databases.db"); | 79 FILE_PATH_LITERAL("Databases.db"); |
| 48 static const int kCurrentVersion = 2; | 80 static const int kCurrentVersion = 2; |
| 49 static const int kCompatibleVersion = 1; | 81 static const int kCompatibleVersion = 1; |
| 50 static const char* kExtensionOriginIdentifierPrefix = "chrome-extension_"; | 82 |
| 83 const FilePath::CharType kTemporaryDirectoryPrefix[] = | |
| 84 FILE_PATH_LITERAL("DeleteMe"); | |
| 85 const FilePath::CharType kTemporaryDirectoryPattern[] = | |
| 86 FILE_PATH_LITERAL("DeleteMe*"); | |
| 51 | 87 |
| 52 OriginInfo::OriginInfo() | 88 OriginInfo::OriginInfo() |
| 53 : total_size_(0) {} | 89 : total_size_(0) {} |
| 54 | 90 |
| 55 OriginInfo::OriginInfo(const OriginInfo& origin_info) | 91 OriginInfo::OriginInfo(const OriginInfo& origin_info) |
| 56 : origin_(origin_info.origin_), | 92 : origin_(origin_info.origin_), |
| 57 total_size_(origin_info.total_size_), | 93 total_size_(origin_info.total_size_), |
| 58 database_info_(origin_info.database_info_) {} | 94 database_info_(origin_info.database_info_) {} |
| 59 | 95 |
| 60 OriginInfo::~OriginInfo() {} | 96 OriginInfo::~OriginInfo() {} |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 80 return it->second.second; | 116 return it->second.second; |
| 81 return string16(); | 117 return string16(); |
| 82 } | 118 } |
| 83 | 119 |
| 84 OriginInfo::OriginInfo(const string16& origin, int64 total_size) | 120 OriginInfo::OriginInfo(const string16& origin, int64 total_size) |
| 85 : origin_(origin), total_size_(total_size) {} | 121 : origin_(origin), total_size_(total_size) {} |
| 86 | 122 |
| 87 DatabaseTracker::DatabaseTracker( | 123 DatabaseTracker::DatabaseTracker( |
| 88 const FilePath& profile_path, | 124 const FilePath& profile_path, |
| 89 bool is_incognito, | 125 bool is_incognito, |
| 126 bool clear_local_state_on_exit, | |
| 90 quota::SpecialStoragePolicy* special_storage_policy, | 127 quota::SpecialStoragePolicy* special_storage_policy, |
| 91 quota::QuotaManagerProxy* quota_manager_proxy, | 128 quota::QuotaManagerProxy* quota_manager_proxy, |
| 92 base::MessageLoopProxy* db_tracker_thread) | 129 base::MessageLoopProxy* db_tracker_thread) |
| 93 : is_initialized_(false), | 130 : is_initialized_(false), |
| 94 is_incognito_(is_incognito), | 131 is_incognito_(is_incognito), |
| 132 clear_local_state_on_exit_(clear_local_state_on_exit), | |
| 95 shutting_down_(false), | 133 shutting_down_(false), |
| 96 profile_path_(profile_path), | 134 profile_path_(profile_path), |
| 97 db_dir_(is_incognito_ ? | 135 db_dir_(is_incognito_ ? |
| 98 profile_path_.Append(kIncognitoDatabaseDirectoryName) : | 136 profile_path_.Append(kIncognitoDatabaseDirectoryName) : |
| 99 profile_path_.Append(kDatabaseDirectoryName)), | 137 profile_path_.Append(kDatabaseDirectoryName)), |
| 100 db_(new sql::Connection()), | 138 db_(new sql::Connection()), |
| 101 databases_table_(NULL), | 139 databases_table_(NULL), |
| 102 meta_table_(NULL), | 140 meta_table_(NULL), |
| 103 special_storage_policy_(special_storage_policy), | 141 special_storage_policy_(special_storage_policy), |
| 104 quota_manager_proxy_(quota_manager_proxy), | 142 quota_manager_proxy_(quota_manager_proxy), |
| 143 db_tracker_thread_(db_tracker_thread), | |
| 105 incognito_origin_directories_generator_(0) { | 144 incognito_origin_directories_generator_(0) { |
| 106 if (quota_manager_proxy) { | 145 if (quota_manager_proxy) { |
| 107 quota_manager_proxy->RegisterClient( | 146 quota_manager_proxy->RegisterClient( |
| 108 new DatabaseQuotaClient(db_tracker_thread, this)); | 147 new DatabaseQuotaClient(db_tracker_thread, this)); |
| 109 } | 148 } |
| 110 } | 149 } |
| 111 | 150 |
| 112 DatabaseTracker::~DatabaseTracker() { | 151 DatabaseTracker::~DatabaseTracker() { |
| 113 DCHECK(dbs_to_be_deleted_.empty()); | 152 DCHECK(dbs_to_be_deleted_.empty()); |
| 114 DCHECK(deletion_callbacks_.empty()); | 153 DCHECK(deletion_callbacks_.empty()); |
| 115 } | 154 } |
| 116 | 155 |
| 117 void DatabaseTracker::DatabaseOpened(const string16& origin_identifier, | 156 void DatabaseTracker::DatabaseOpened(const string16& origin_identifier, |
| 118 const string16& database_name, | 157 const string16& database_name, |
| 119 const string16& database_description, | 158 const string16& database_description, |
| 120 int64 estimated_size, | 159 int64 estimated_size, |
| 121 int64* database_size) { | 160 int64* database_size) { |
| 122 if (!LazyInit()) { | 161 if (shutting_down_ || !LazyInit()) { |
| 123 *database_size = 0; | 162 *database_size = 0; |
| 124 return; | 163 return; |
| 125 } | 164 } |
| 126 | 165 |
| 127 if (quota_manager_proxy_) | 166 if (quota_manager_proxy_) |
| 128 quota_manager_proxy_->NotifyStorageAccessed( | 167 quota_manager_proxy_->NotifyStorageAccessed( |
| 129 quota::QuotaClient::kDatabase, | 168 quota::QuotaClient::kDatabase, |
| 130 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), | 169 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), |
| 131 quota::kStorageTypeTemporary); | 170 quota::kStorageTypeTemporary); |
| 132 | 171 |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 356 -db_file_size); | 395 -db_file_size); |
| 357 | 396 |
| 358 // Clean up the main database and invalidate the cached record. | 397 // Clean up the main database and invalidate the cached record. |
| 359 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); | 398 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); |
| 360 origins_info_map_.erase(origin_identifier); | 399 origins_info_map_.erase(origin_identifier); |
| 361 | 400 |
| 362 std::vector<DatabaseDetails> details; | 401 std::vector<DatabaseDetails> details; |
| 363 if (databases_table_->GetAllDatabaseDetailsForOrigin( | 402 if (databases_table_->GetAllDatabaseDetailsForOrigin( |
| 364 origin_identifier, &details) && details.empty()) { | 403 origin_identifier, &details) && details.empty()) { |
| 365 // Try to delete the origin in case this was the last database. | 404 // Try to delete the origin in case this was the last database. |
| 366 DeleteOrigin(origin_identifier); | 405 DeleteOrigin(origin_identifier, false); |
| 367 } | 406 } |
| 368 return true; | 407 return true; |
| 369 } | 408 } |
| 370 | 409 |
| 371 bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { | 410 bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier, |
| 411 bool force) { | |
| 372 if (!LazyInit()) | 412 if (!LazyInit()) |
| 373 return false; | 413 return false; |
| 374 | 414 |
| 375 // Check if any database in this origin is opened by any renderer. | 415 // Check if any database in this origin is opened by any renderer. |
| 376 if (database_connections_.IsOriginUsed(origin_identifier)) | 416 if (database_connections_.IsOriginUsed(origin_identifier) && !force) |
| 377 return false; | 417 return false; |
| 378 | 418 |
| 379 int64 deleted_size = 0; | 419 int64 deleted_size = 0; |
| 380 if (quota_manager_proxy_) { | 420 if (quota_manager_proxy_) { |
| 381 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | 421 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); |
| 382 if (origin_info) | 422 if (origin_info) |
| 383 deleted_size = origin_info->TotalSize(); | 423 deleted_size = origin_info->TotalSize(); |
| 384 } | 424 } |
| 385 | 425 |
| 386 // We need to invalidate the cached record whether file_util::Delete() | 426 // We need to invalidate the cached record whether file_util::Delete() |
| 387 // succeeds or not, because even if it fails, it might still delete some | 427 // succeeds or not, because even if it fails, it might still delete some |
| 388 // DB files on the hard drive. | 428 // DB files on the hard drive. |
| 389 origins_info_map_.erase(origin_identifier); | 429 origins_info_map_.erase(origin_identifier); |
| 390 FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( | 430 FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( |
| 391 UTF16ToWide(origin_identifier))); | 431 UTF16ToWide(origin_identifier))); |
| 392 if (!file_util::Delete(origin_dir, true)) | 432 if (!file_util::Delete(origin_dir, true)) { |
| 393 return false; | 433 if (!force) |
| 434 return false; | |
| 435 | |
| 436 // Rename the directory as its name leaks the origin's name. | |
| 437 FilePath new_origin_dir; | |
| 438 file_util::CreateTemporaryDirInDir(db_dir_, | |
| 439 kTemporaryDirectoryPrefix, | |
| 440 &new_origin_dir); | |
| 441 FilePath temp_dir = db_dir_.Append(FILE_PATH_LITERAL("0")); | |
| 442 file_util::Move(origin_dir, temp_dir); | |
| 443 } | |
| 394 | 444 |
| 395 databases_table_->DeleteOrigin(origin_identifier); | 445 databases_table_->DeleteOrigin(origin_identifier); |
| 396 | 446 |
| 397 if (quota_manager_proxy_ && deleted_size) { | 447 if (quota_manager_proxy_ && deleted_size) { |
| 398 quota_manager_proxy_->NotifyStorageModified( | 448 quota_manager_proxy_->NotifyStorageModified( |
| 399 quota::QuotaClient::kDatabase, | 449 quota::QuotaClient::kDatabase, |
| 400 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), | 450 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), |
| 401 quota::kStorageTypeTemporary, | 451 quota::kStorageTypeTemporary, |
| 402 -deleted_size); | 452 -deleted_size); |
| 403 } | 453 } |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 415 std::set<string16>& databases = it->second; | 465 std::set<string16>& databases = it->second; |
| 416 return (databases.find(database_name) != databases.end()); | 466 return (databases.find(database_name) != databases.end()); |
| 417 } | 467 } |
| 418 | 468 |
| 419 bool DatabaseTracker::LazyInit() { | 469 bool DatabaseTracker::LazyInit() { |
| 420 if (!is_initialized_ && !shutting_down_) { | 470 if (!is_initialized_ && !shutting_down_) { |
| 421 DCHECK(!db_->is_open()); | 471 DCHECK(!db_->is_open()); |
| 422 DCHECK(!databases_table_.get()); | 472 DCHECK(!databases_table_.get()); |
| 423 DCHECK(!meta_table_.get()); | 473 DCHECK(!meta_table_.get()); |
| 424 | 474 |
| 475 // If there are left-over directoryfrom failed deletion attempts, clean | |
|
michaeln
2011/07/01 03:02:34
s/b directories from
jochen (gone - plz use gerrit)
2011/07/06 09:24:53
Done.
| |
| 476 // them up. | |
| 477 if (file_util::DirectoryExists(db_dir_)) { | |
| 478 file_util::FileEnumerator directories( | |
| 479 db_dir_, | |
| 480 false, | |
| 481 file_util::FileEnumerator::DIRECTORIES, | |
| 482 kTemporaryDirectoryPattern); | |
| 483 for (FilePath directory = directories.Next(); !directory.empty(); | |
| 484 directory = directories.Next()) { | |
| 485 file_util::Delete(directory, true); | |
| 486 } | |
| 487 } | |
| 488 | |
| 425 // If the tracker database exists, but it's corrupt or doesn't | 489 // If the tracker database exists, but it's corrupt or doesn't |
| 426 // have a meta table, delete the database directory. | 490 // have a meta table, delete the database directory. |
| 427 const FilePath kTrackerDatabaseFullPath = | 491 const FilePath kTrackerDatabaseFullPath = |
| 428 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); | 492 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); |
| 429 if (file_util::DirectoryExists(db_dir_) && | 493 if (file_util::DirectoryExists(db_dir_) && |
| 430 file_util::PathExists(kTrackerDatabaseFullPath) && | 494 file_util::PathExists(kTrackerDatabaseFullPath) && |
| 431 (!db_->Open(kTrackerDatabaseFullPath) || | 495 (!db_->Open(kTrackerDatabaseFullPath) || |
| 432 !sql::MetaTable::DoesTableExist(db_.get()))) { | 496 !sql::MetaTable::DoesTableExist(db_.get()))) { |
| 433 db_->Close(); | 497 db_->Close(); |
| 434 if (!file_util::Delete(db_dir_, true)) | 498 if (!file_util::Delete(db_dir_, true)) |
| (...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 740 for (FileHandlesMap::iterator it = incognito_file_handles_.begin(); | 804 for (FileHandlesMap::iterator it = incognito_file_handles_.begin(); |
| 741 it != incognito_file_handles_.end(); it++) | 805 it != incognito_file_handles_.end(); it++) |
| 742 base::ClosePlatformFile(it->second); | 806 base::ClosePlatformFile(it->second); |
| 743 | 807 |
| 744 FilePath incognito_db_dir = | 808 FilePath incognito_db_dir = |
| 745 profile_path_.Append(kIncognitoDatabaseDirectoryName); | 809 profile_path_.Append(kIncognitoDatabaseDirectoryName); |
| 746 if (file_util::DirectoryExists(incognito_db_dir)) | 810 if (file_util::DirectoryExists(incognito_db_dir)) |
| 747 file_util::Delete(incognito_db_dir, true); | 811 file_util::Delete(incognito_db_dir, true); |
| 748 } | 812 } |
| 749 | 813 |
| 750 // static | 814 void DatabaseTracker::Shutdown() { |
| 751 void DatabaseTracker::ClearLocalState(const FilePath& profile_path) { | 815 if (shutting_down_ || is_incognito_) { |
| 752 // TODO(michaeln): use SpecialStoragePolicy instead of kExtensionOriginPrefix | 816 NOTREACHED(); |
| 753 FilePath db_dir = profile_path.Append(FilePath(kDatabaseDirectoryName)); | 817 return; |
| 754 FilePath db_tracker = db_dir.Append(FilePath(kTrackerDatabaseFileName)); | 818 } |
| 755 if (file_util::DirectoryExists(db_dir) && | 819 if (clear_local_state_on_exit_ && LazyInit()) { |
|
michaeln
2011/07/01 03:02:34
We shouldn't call LazyInit() on the UI thread. It'
jochen (gone - plz use gerrit)
2011/07/06 09:24:53
Done.
| |
| 756 file_util::PathExists(db_tracker)) { | 820 shutting_down_ = true; |
| 757 scoped_ptr<sql::Connection> db_(new sql::Connection); | 821 |
| 758 if (!db_->Open(db_tracker) || | 822 if (!db_tracker_thread_.get()) { |
| 759 !db_->DoesTableExist("Databases")) { | 823 NOTREACHED(); |
| 760 db_->Close(); | |
| 761 file_util::Delete(db_dir, true); | |
| 762 return; | 824 return; |
| 763 } else { | |
| 764 sql::Statement delete_statement(db_->GetCachedStatement( | |
| 765 SQL_FROM_HERE, "DELETE FROM Databases WHERE origin NOT LIKE ?")); | |
| 766 std::string filter(kExtensionOriginIdentifierPrefix); | |
| 767 filter += "%"; | |
| 768 delete_statement.BindString(0, filter); | |
| 769 if (!delete_statement.Run()) { | |
| 770 db_->Close(); | |
| 771 file_util::Delete(db_dir, true); | |
| 772 return; | |
| 773 } | |
| 774 } | 825 } |
| 775 } | 826 scoped_refptr<DatabaseTracker> tracker(this); |
| 776 file_util::FileEnumerator file_enumerator(db_dir, false, | 827 db_tracker_thread_->PostTask(FROM_HERE, |
| 777 file_util::FileEnumerator::DIRECTORIES); | 828 NewRunnableFunction(ClearLocalState, tracker)); |
| 778 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
| 779 file_path = file_enumerator.Next()) { | |
| 780 if (file_path.BaseName() != FilePath(kTrackerDatabaseFileName)) { | |
| 781 std::string basename = file_path.BaseName().MaybeAsASCII(); | |
| 782 if (!basename.empty() && | |
| 783 !StartsWithASCII(basename, kExtensionOriginIdentifierPrefix, true)) { | |
| 784 file_util::Delete(file_path, true); | |
| 785 } | |
| 786 } | |
| 787 } | 829 } |
| 788 } | 830 } |
| 789 | 831 |
| 832 void DatabaseTracker::SetClearLocalStateOnExit(bool clear_local_state_on_exit) { | |
|
michaeln
2011/07/01 03:02:34
It'd be safer to test and set these data members e
jochen (gone - plz use gerrit)
2011/07/06 09:24:53
Done.
| |
| 833 if (shutting_down_) { | |
| 834 NOTREACHED(); | |
| 835 return; | |
| 836 } | |
| 837 clear_local_state_on_exit_ = clear_local_state_on_exit; | |
| 838 } | |
| 839 | |
| 790 } // namespace webkit_database | 840 } // namespace webkit_database |
| OLD | NEW |