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 |