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 { |
(...skipping 12 matching lines...) Expand all Loading... |
40 namespace webkit_database { | 41 namespace webkit_database { |
41 | 42 |
42 const FilePath::CharType kDatabaseDirectoryName[] = | 43 const FilePath::CharType kDatabaseDirectoryName[] = |
43 FILE_PATH_LITERAL("databases"); | 44 FILE_PATH_LITERAL("databases"); |
44 const FilePath::CharType kIncognitoDatabaseDirectoryName[] = | 45 const FilePath::CharType kIncognitoDatabaseDirectoryName[] = |
45 FILE_PATH_LITERAL("databases-incognito"); | 46 FILE_PATH_LITERAL("databases-incognito"); |
46 const FilePath::CharType kTrackerDatabaseFileName[] = | 47 const FilePath::CharType kTrackerDatabaseFileName[] = |
47 FILE_PATH_LITERAL("Databases.db"); | 48 FILE_PATH_LITERAL("Databases.db"); |
48 static const int kCurrentVersion = 2; | 49 static const int kCurrentVersion = 2; |
49 static const int kCompatibleVersion = 1; | 50 static const int kCompatibleVersion = 1; |
50 static const char* kExtensionOriginIdentifierPrefix = "chrome-extension_"; | 51 |
| 52 const FilePath::CharType kTemporaryDirectoryPrefix[] = |
| 53 FILE_PATH_LITERAL("DeleteMe"); |
| 54 const FilePath::CharType kTemporaryDirectoryPattern[] = |
| 55 FILE_PATH_LITERAL("DeleteMe*"); |
51 | 56 |
52 OriginInfo::OriginInfo() | 57 OriginInfo::OriginInfo() |
53 : total_size_(0) {} | 58 : total_size_(0) {} |
54 | 59 |
55 OriginInfo::OriginInfo(const OriginInfo& origin_info) | 60 OriginInfo::OriginInfo(const OriginInfo& origin_info) |
56 : origin_(origin_info.origin_), | 61 : origin_(origin_info.origin_), |
57 total_size_(origin_info.total_size_), | 62 total_size_(origin_info.total_size_), |
58 database_info_(origin_info.database_info_) {} | 63 database_info_(origin_info.database_info_) {} |
59 | 64 |
60 OriginInfo::~OriginInfo() {} | 65 OriginInfo::~OriginInfo() {} |
(...skipping 19 matching lines...) Expand all Loading... |
80 return it->second.second; | 85 return it->second.second; |
81 return string16(); | 86 return string16(); |
82 } | 87 } |
83 | 88 |
84 OriginInfo::OriginInfo(const string16& origin, int64 total_size) | 89 OriginInfo::OriginInfo(const string16& origin, int64 total_size) |
85 : origin_(origin), total_size_(total_size) {} | 90 : origin_(origin), total_size_(total_size) {} |
86 | 91 |
87 DatabaseTracker::DatabaseTracker( | 92 DatabaseTracker::DatabaseTracker( |
88 const FilePath& profile_path, | 93 const FilePath& profile_path, |
89 bool is_incognito, | 94 bool is_incognito, |
| 95 bool clear_local_state_on_exit, |
90 quota::SpecialStoragePolicy* special_storage_policy, | 96 quota::SpecialStoragePolicy* special_storage_policy, |
91 quota::QuotaManagerProxy* quota_manager_proxy, | 97 quota::QuotaManagerProxy* quota_manager_proxy, |
92 base::MessageLoopProxy* db_tracker_thread) | 98 base::MessageLoopProxy* db_tracker_thread) |
93 : is_initialized_(false), | 99 : is_initialized_(false), |
94 is_incognito_(is_incognito), | 100 is_incognito_(is_incognito), |
| 101 clear_local_state_on_exit_(clear_local_state_on_exit), |
95 shutting_down_(false), | 102 shutting_down_(false), |
96 profile_path_(profile_path), | 103 profile_path_(profile_path), |
97 db_dir_(is_incognito_ ? | 104 db_dir_(is_incognito_ ? |
98 profile_path_.Append(kIncognitoDatabaseDirectoryName) : | 105 profile_path_.Append(kIncognitoDatabaseDirectoryName) : |
99 profile_path_.Append(kDatabaseDirectoryName)), | 106 profile_path_.Append(kDatabaseDirectoryName)), |
100 db_(new sql::Connection()), | 107 db_(new sql::Connection()), |
101 databases_table_(NULL), | 108 databases_table_(NULL), |
102 meta_table_(NULL), | 109 meta_table_(NULL), |
103 special_storage_policy_(special_storage_policy), | 110 special_storage_policy_(special_storage_policy), |
104 quota_manager_proxy_(quota_manager_proxy), | 111 quota_manager_proxy_(quota_manager_proxy), |
| 112 db_tracker_thread_(db_tracker_thread), |
105 incognito_origin_directories_generator_(0) { | 113 incognito_origin_directories_generator_(0) { |
106 if (quota_manager_proxy) { | 114 if (quota_manager_proxy) { |
107 quota_manager_proxy->RegisterClient( | 115 quota_manager_proxy->RegisterClient( |
108 new DatabaseQuotaClient(db_tracker_thread, this)); | 116 new DatabaseQuotaClient(db_tracker_thread, this)); |
109 } | 117 } |
110 } | 118 } |
111 | 119 |
112 DatabaseTracker::~DatabaseTracker() { | 120 DatabaseTracker::~DatabaseTracker() { |
113 DCHECK(dbs_to_be_deleted_.empty()); | 121 DCHECK(dbs_to_be_deleted_.empty()); |
114 DCHECK(deletion_callbacks_.empty()); | 122 DCHECK(deletion_callbacks_.empty()); |
115 } | 123 } |
116 | 124 |
117 void DatabaseTracker::DatabaseOpened(const string16& origin_identifier, | 125 void DatabaseTracker::DatabaseOpened(const string16& origin_identifier, |
118 const string16& database_name, | 126 const string16& database_name, |
119 const string16& database_description, | 127 const string16& database_description, |
120 int64 estimated_size, | 128 int64 estimated_size, |
121 int64* database_size) { | 129 int64* database_size) { |
122 if (!LazyInit()) { | 130 if (shutting_down_ || !LazyInit()) { |
123 *database_size = 0; | 131 *database_size = 0; |
124 return; | 132 return; |
125 } | 133 } |
126 | 134 |
127 if (quota_manager_proxy_) | 135 if (quota_manager_proxy_) |
128 quota_manager_proxy_->NotifyStorageAccessed( | 136 quota_manager_proxy_->NotifyStorageAccessed( |
129 quota::QuotaClient::kDatabase, | 137 quota::QuotaClient::kDatabase, |
130 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), | 138 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), |
131 quota::kStorageTypeTemporary); | 139 quota::kStorageTypeTemporary); |
132 | 140 |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 -db_file_size); | 364 -db_file_size); |
357 | 365 |
358 // Clean up the main database and invalidate the cached record. | 366 // Clean up the main database and invalidate the cached record. |
359 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); | 367 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); |
360 origins_info_map_.erase(origin_identifier); | 368 origins_info_map_.erase(origin_identifier); |
361 | 369 |
362 std::vector<DatabaseDetails> details; | 370 std::vector<DatabaseDetails> details; |
363 if (databases_table_->GetAllDatabaseDetailsForOrigin( | 371 if (databases_table_->GetAllDatabaseDetailsForOrigin( |
364 origin_identifier, &details) && details.empty()) { | 372 origin_identifier, &details) && details.empty()) { |
365 // Try to delete the origin in case this was the last database. | 373 // Try to delete the origin in case this was the last database. |
366 DeleteOrigin(origin_identifier); | 374 DeleteOrigin(origin_identifier, false); |
367 } | 375 } |
368 return true; | 376 return true; |
369 } | 377 } |
370 | 378 |
371 bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { | 379 bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier, |
| 380 bool force) { |
372 if (!LazyInit()) | 381 if (!LazyInit()) |
373 return false; | 382 return false; |
374 | 383 |
375 // Check if any database in this origin is opened by any renderer. | 384 // Check if any database in this origin is opened by any renderer. |
376 if (database_connections_.IsOriginUsed(origin_identifier)) | 385 if (database_connections_.IsOriginUsed(origin_identifier) && !force) |
377 return false; | 386 return false; |
378 | 387 |
379 int64 deleted_size = 0; | 388 int64 deleted_size = 0; |
380 if (quota_manager_proxy_) { | 389 if (quota_manager_proxy_) { |
381 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | 390 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); |
382 if (origin_info) | 391 if (origin_info) |
383 deleted_size = origin_info->TotalSize(); | 392 deleted_size = origin_info->TotalSize(); |
384 } | 393 } |
385 | 394 |
386 // 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 | |
388 // DB files on the hard drive. | |
389 origins_info_map_.erase(origin_identifier); | 395 origins_info_map_.erase(origin_identifier); |
390 FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( | 396 FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( |
391 UTF16ToWide(origin_identifier))); | 397 UTF16ToWide(origin_identifier))); |
392 if (!file_util::Delete(origin_dir, true)) | 398 |
393 return false; | 399 // Create a temporary directory to move possibly still existing databases to, |
| 400 // as we can't delete the origin directory on windows if it contains opened |
| 401 // files. |
| 402 FilePath new_origin_dir; |
| 403 file_util::CreateTemporaryDirInDir(db_dir_, |
| 404 kTemporaryDirectoryPrefix, |
| 405 &new_origin_dir); |
| 406 file_util::FileEnumerator databases( |
| 407 origin_dir, |
| 408 false, |
| 409 file_util::FileEnumerator::FILES); |
| 410 for (FilePath database = databases.Next(); !database.empty(); |
| 411 database = databases.Next()) { |
| 412 FilePath new_file = new_origin_dir.Append(database.BaseName()); |
| 413 file_util::Move(database, new_file); |
| 414 } |
| 415 file_util::Delete(origin_dir, true); |
| 416 file_util::Delete(new_origin_dir, true); // might fail on windows. |
394 | 417 |
395 databases_table_->DeleteOrigin(origin_identifier); | 418 databases_table_->DeleteOrigin(origin_identifier); |
396 | 419 |
397 if (quota_manager_proxy_ && deleted_size) { | 420 if (quota_manager_proxy_ && deleted_size) { |
398 quota_manager_proxy_->NotifyStorageModified( | 421 quota_manager_proxy_->NotifyStorageModified( |
399 quota::QuotaClient::kDatabase, | 422 quota::QuotaClient::kDatabase, |
400 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), | 423 DatabaseUtil::GetOriginFromIdentifier(origin_identifier), |
401 quota::kStorageTypeTemporary, | 424 quota::kStorageTypeTemporary, |
402 -deleted_size); | 425 -deleted_size); |
403 } | 426 } |
(...skipping 11 matching lines...) Expand all Loading... |
415 std::set<string16>& databases = it->second; | 438 std::set<string16>& databases = it->second; |
416 return (databases.find(database_name) != databases.end()); | 439 return (databases.find(database_name) != databases.end()); |
417 } | 440 } |
418 | 441 |
419 bool DatabaseTracker::LazyInit() { | 442 bool DatabaseTracker::LazyInit() { |
420 if (!is_initialized_ && !shutting_down_) { | 443 if (!is_initialized_ && !shutting_down_) { |
421 DCHECK(!db_->is_open()); | 444 DCHECK(!db_->is_open()); |
422 DCHECK(!databases_table_.get()); | 445 DCHECK(!databases_table_.get()); |
423 DCHECK(!meta_table_.get()); | 446 DCHECK(!meta_table_.get()); |
424 | 447 |
| 448 // If there are left-over directories from failed deletion attempts, clean |
| 449 // them up. |
| 450 if (file_util::DirectoryExists(db_dir_)) { |
| 451 file_util::FileEnumerator directories( |
| 452 db_dir_, |
| 453 false, |
| 454 file_util::FileEnumerator::DIRECTORIES, |
| 455 kTemporaryDirectoryPattern); |
| 456 for (FilePath directory = directories.Next(); !directory.empty(); |
| 457 directory = directories.Next()) { |
| 458 file_util::Delete(directory, true); |
| 459 } |
| 460 } |
| 461 |
425 // If the tracker database exists, but it's corrupt or doesn't | 462 // If the tracker database exists, but it's corrupt or doesn't |
426 // have a meta table, delete the database directory. | 463 // have a meta table, delete the database directory. |
427 const FilePath kTrackerDatabaseFullPath = | 464 const FilePath kTrackerDatabaseFullPath = |
428 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); | 465 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); |
429 if (file_util::DirectoryExists(db_dir_) && | 466 if (file_util::DirectoryExists(db_dir_) && |
430 file_util::PathExists(kTrackerDatabaseFullPath) && | 467 file_util::PathExists(kTrackerDatabaseFullPath) && |
431 (!db_->Open(kTrackerDatabaseFullPath) || | 468 (!db_->Open(kTrackerDatabaseFullPath) || |
432 !sql::MetaTable::DoesTableExist(db_.get()))) { | 469 !sql::MetaTable::DoesTableExist(db_.get()))) { |
433 db_->Close(); | 470 db_->Close(); |
434 if (!file_util::Delete(db_dir_, true)) | 471 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(); | 777 for (FileHandlesMap::iterator it = incognito_file_handles_.begin(); |
741 it != incognito_file_handles_.end(); it++) | 778 it != incognito_file_handles_.end(); it++) |
742 base::ClosePlatformFile(it->second); | 779 base::ClosePlatformFile(it->second); |
743 | 780 |
744 FilePath incognito_db_dir = | 781 FilePath incognito_db_dir = |
745 profile_path_.Append(kIncognitoDatabaseDirectoryName); | 782 profile_path_.Append(kIncognitoDatabaseDirectoryName); |
746 if (file_util::DirectoryExists(incognito_db_dir)) | 783 if (file_util::DirectoryExists(incognito_db_dir)) |
747 file_util::Delete(incognito_db_dir, true); | 784 file_util::Delete(incognito_db_dir, true); |
748 } | 785 } |
749 | 786 |
750 // static | 787 void DatabaseTracker::ClearLocalState() { |
751 void DatabaseTracker::ClearLocalState(const FilePath& profile_path) { | 788 shutting_down_ = true; |
752 // TODO(michaeln): use SpecialStoragePolicy instead of kExtensionOriginPrefix | 789 |
753 FilePath db_dir = profile_path.Append(FilePath(kDatabaseDirectoryName)); | 790 std::vector<string16> origin_identifiers; |
754 FilePath db_tracker = db_dir.Append(FilePath(kTrackerDatabaseFileName)); | 791 GetAllOriginIdentifiers(&origin_identifiers); |
755 if (file_util::DirectoryExists(db_dir) && | 792 |
756 file_util::PathExists(db_tracker)) { | 793 for (std::vector<string16>::iterator origin = origin_identifiers.begin(); |
757 scoped_ptr<sql::Connection> db_(new sql::Connection); | 794 origin != origin_identifiers.end(); ++origin) { |
758 if (!db_->Open(db_tracker) || | 795 if (special_storage_policy_.get() && |
759 !db_->DoesTableExist("Databases")) { | 796 special_storage_policy_->IsStorageProtected( |
760 db_->Close(); | 797 webkit_database::DatabaseUtil::GetOriginFromIdentifier(*origin))) { |
761 file_util::Delete(db_dir, true); | 798 continue; |
762 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 } | 799 } |
775 } | 800 webkit_database::OriginInfo origin_info; |
776 file_util::FileEnumerator file_enumerator(db_dir, false, | 801 std::vector<string16> databases; |
777 file_util::FileEnumerator::DIRECTORIES); | 802 GetOriginInfo(*origin, &origin_info); |
778 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | 803 origin_info.GetAllDatabaseNames(&databases); |
779 file_path = file_enumerator.Next()) { | 804 |
780 if (file_path.BaseName() != FilePath(kTrackerDatabaseFileName)) { | 805 for (std::vector<string16>::iterator database = databases.begin(); |
781 std::string basename = file_path.BaseName().MaybeAsASCII(); | 806 database != databases.end(); ++database) { |
782 if (!basename.empty() && | 807 base::PlatformFile file_handle = base::CreatePlatformFile( |
783 !StartsWithASCII(basename, kExtensionOriginIdentifierPrefix, true)) { | 808 GetFullDBFilePath(*origin, *database), |
784 file_util::Delete(file_path, true); | 809 base::PLATFORM_FILE_OPEN_ALWAYS | |
785 } | 810 base::PLATFORM_FILE_SHARE_DELETE | |
| 811 base::PLATFORM_FILE_DELETE_ON_CLOSE | |
| 812 base::PLATFORM_FILE_READ, |
| 813 NULL, NULL); |
| 814 base::ClosePlatformFile(file_handle); |
786 } | 815 } |
| 816 DeleteOrigin(*origin, true); |
787 } | 817 } |
788 } | 818 } |
789 | 819 |
| 820 |
| 821 void DatabaseTracker::Shutdown() { |
| 822 DCHECK(db_tracker_thread_.get()); |
| 823 DCHECK(db_tracker_thread_->BelongsToCurrentThread()); |
| 824 if (shutting_down_) { |
| 825 NOTREACHED(); |
| 826 return; |
| 827 } |
| 828 if (is_incognito_) |
| 829 DeleteIncognitoDBDirectory(); |
| 830 else if (clear_local_state_on_exit_ && LazyInit()) |
| 831 ClearLocalState(); |
| 832 } |
| 833 |
| 834 void DatabaseTracker::SetClearLocalStateOnExit(bool clear_local_state_on_exit) { |
| 835 DCHECK(db_tracker_thread_.get()); |
| 836 if (!db_tracker_thread_->BelongsToCurrentThread()) { |
| 837 db_tracker_thread_->PostTask(FROM_HERE, |
| 838 NewRunnableMethod(this, |
| 839 &DatabaseTracker::SetClearLocalStateOnExit, |
| 840 clear_local_state_on_exit)); |
| 841 return; |
| 842 } |
| 843 if (shutting_down_) { |
| 844 NOTREACHED(); |
| 845 return; |
| 846 } |
| 847 clear_local_state_on_exit_ = clear_local_state_on_exit; |
| 848 } |
| 849 |
790 } // namespace webkit_database | 850 } // namespace webkit_database |
OLD | NEW |