OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 <vector> | 7 #include <vector> |
8 | 8 |
9 #include "app/sql/connection.h" | 9 #include "app/sql/connection.h" |
10 #include "app/sql/meta_table.h" | 10 #include "app/sql/meta_table.h" |
11 #include "app/sql/statement.h" | 11 #include "app/sql/statement.h" |
| 12 #include "app/sql/transaction.h" |
12 #include "base/basictypes.h" | 13 #include "base/basictypes.h" |
13 #include "base/file_path.h" | 14 #include "base/file_path.h" |
14 #include "base/file_util.h" | 15 #include "base/file_util.h" |
15 #include "base/string_util.h" | 16 #include "base/string_util.h" |
16 #include "webkit/database/databases_table.h" | 17 #include "webkit/database/databases_table.h" |
| 18 #include "webkit/database/quota_table.h" |
17 | 19 |
18 namespace webkit_database { | 20 namespace webkit_database { |
19 | 21 |
20 const FilePath::CharType kDatabaseDirectoryName[] = | 22 const FilePath::CharType kDatabaseDirectoryName[] = |
21 FILE_PATH_LITERAL("databases"); | 23 FILE_PATH_LITERAL("databases"); |
22 const FilePath::CharType kTrackerDatabaseFileName[] = | 24 const FilePath::CharType kTrackerDatabaseFileName[] = |
23 FILE_PATH_LITERAL("Databases.db"); | 25 FILE_PATH_LITERAL("Databases.db"); |
24 const int kCurrentVersion = 1; | 26 const int kCurrentVersion = 2; |
25 const int kCompatibleVersion = 1; | 27 const int kCompatibleVersion = 1; |
26 const int64 kDefaultQuota = 5 * 1024 * 1024; | 28 const int64 kDefaultQuota = 5 * 1024 * 1024; |
27 const int64 kDefaultExtensionQuota = 50 * 1024 * 1024; | 29 const int64 kDefaultExtensionQuota = 50 * 1024 * 1024; |
28 const char* kExtensionOriginIdentifierPrefix = "chrome-extension_"; | 30 const char* kExtensionOriginIdentifierPrefix = "chrome-extension_"; |
29 | 31 |
30 DatabaseTracker::DatabaseTracker(const FilePath& profile_path) | 32 DatabaseTracker::DatabaseTracker(const FilePath& profile_path) |
31 : initialized_(false), | 33 : initialized_(false), |
32 db_dir_(profile_path.Append(FilePath(kDatabaseDirectoryName))), | 34 db_dir_(profile_path.Append(FilePath(kDatabaseDirectoryName))), |
33 db_(new sql::Connection()), | 35 db_(new sql::Connection()), |
34 databases_table_(NULL), | 36 databases_table_(NULL), |
(...skipping 11 matching lines...) Expand all Loading... |
46 int64* database_size, | 48 int64* database_size, |
47 int64* space_available) { | 49 int64* space_available) { |
48 if (!LazyInit()) { | 50 if (!LazyInit()) { |
49 *database_size = 0; | 51 *database_size = 0; |
50 *space_available = 0; | 52 *space_available = 0; |
51 return; | 53 return; |
52 } | 54 } |
53 | 55 |
54 InsertOrUpdateDatabaseDetails(origin_identifier, database_name, | 56 InsertOrUpdateDatabaseDetails(origin_identifier, database_name, |
55 database_description, estimated_size); | 57 database_description, estimated_size); |
| 58 database_connections_.AddConnection(origin_identifier, database_name); |
56 | 59 |
57 *database_size = GetCachedDatabaseFileSize(origin_identifier, database_name); | 60 CachedOriginInfo* info = GetCachedOriginInfo(origin_identifier); |
| 61 *database_size = (info ? info->GetDatabaseSize(database_name) : 0); |
58 *space_available = GetOriginSpaceAvailable(origin_identifier); | 62 *space_available = GetOriginSpaceAvailable(origin_identifier); |
59 } | 63 } |
60 | 64 |
61 void DatabaseTracker::DatabaseModified(const string16& origin_identifier, | 65 void DatabaseTracker::DatabaseModified(const string16& origin_identifier, |
62 const string16& database_name) { | 66 const string16& database_name) { |
63 if (!LazyInit()) | 67 if (!LazyInit()) |
64 return; | 68 return; |
65 | 69 |
66 int64 updated_db_size = | 70 int64 updated_db_size = |
67 UpdateCachedDatabaseFileSize(origin_identifier, database_name); | 71 UpdateCachedDatabaseFileSize(origin_identifier, database_name); |
68 int64 space_available = GetOriginSpaceAvailable(origin_identifier); | 72 int64 space_available = GetOriginSpaceAvailable(origin_identifier); |
69 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged( | 73 FOR_EACH_OBSERVER(Observer, observers_, OnDatabaseSizeChanged( |
70 origin_identifier, database_name, updated_db_size, space_available)); | 74 origin_identifier, database_name, updated_db_size, space_available)); |
71 } | 75 } |
72 | 76 |
73 void DatabaseTracker::DatabaseClosed(const string16& origin_identifier, | 77 void DatabaseTracker::DatabaseClosed(const string16& origin_identifier, |
74 const string16& database_name) { | 78 const string16& database_name) { |
75 // TODO(dumi): figure out how to use this information at a later time | 79 database_connections_.RemoveConnection(origin_identifier, database_name); |
| 80 } |
| 81 |
| 82 void DatabaseTracker::CloseDatabases(const DatabaseConnections& connections) { |
| 83 database_connections_.RemoveConnections(connections); |
76 } | 84 } |
77 | 85 |
78 void DatabaseTracker::AddObserver(Observer* observer) { | 86 void DatabaseTracker::AddObserver(Observer* observer) { |
79 observers_.AddObserver(observer); | 87 observers_.AddObserver(observer); |
80 } | 88 } |
81 | 89 |
82 void DatabaseTracker::RemoveObserver(Observer* observer) { | 90 void DatabaseTracker::RemoveObserver(Observer* observer) { |
83 // When we remove a listener, we do not know which cached information | 91 // When we remove a listener, we do not know which cached information |
84 // is still needed and which information can be discarded. So we just | 92 // is still needed and which information can be discarded. So we just |
85 // clear all caches and re-populate them as needed. | 93 // clear all caches and re-populate them as needed. |
86 observers_.RemoveObserver(observer); | 94 observers_.RemoveObserver(observer); |
87 ClearAllCachedOriginInfo(); | 95 ClearAllCachedOriginInfo(); |
88 } | 96 } |
89 | 97 |
90 void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() { | 98 void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() { |
91 ClearAllCachedOriginInfo(); | 99 ClearAllCachedOriginInfo(); |
92 meta_table_.reset(NULL); | 100 meta_table_.reset(NULL); |
93 databases_table_.reset(NULL); | 101 databases_table_.reset(NULL); |
| 102 quota_table_.reset(NULL); |
94 db_->Close(); | 103 db_->Close(); |
95 initialized_ = false; | 104 initialized_ = false; |
96 } | 105 } |
97 | 106 |
98 FilePath DatabaseTracker::GetFullDBFilePath( | 107 FilePath DatabaseTracker::GetFullDBFilePath( |
99 const string16& origin_identifier, | 108 const string16& origin_identifier, |
100 const string16& database_name) const { | 109 const string16& database_name) const { |
101 DCHECK(!origin_identifier.empty()); | 110 DCHECK(!origin_identifier.empty()); |
102 DCHECK(!database_name.empty()); | 111 DCHECK(!database_name.empty()); |
103 int64 id = databases_table_->GetDatabaseID( | 112 int64 id = databases_table_->GetDatabaseID( |
104 origin_identifier, database_name); | 113 origin_identifier, database_name); |
105 if (id < 0) | 114 if (id < 0) |
106 return FilePath(); | 115 return FilePath(); |
107 | 116 |
108 FilePath file_name = FilePath::FromWStringHack(Int64ToWString(id)); | 117 FilePath file_name = FilePath::FromWStringHack(Int64ToWString(id)); |
109 return db_dir_.Append(FilePath::FromWStringHack( | 118 return db_dir_.Append(FilePath::FromWStringHack( |
110 UTF16ToWide(origin_identifier))).Append(file_name); | 119 UTF16ToWide(origin_identifier))).Append(file_name); |
111 } | 120 } |
112 | 121 |
| 122 bool DatabaseTracker::GetAllOriginsInfo(std::vector<OriginInfo>* origins_info) { |
| 123 DCHECK(origins_info); |
| 124 DCHECK(origins_info->empty()); |
| 125 std::vector<string16> origins; |
| 126 if (!databases_table_->GetAllOrigins(&origins)) |
| 127 return false; |
| 128 |
| 129 for (std::vector<string16>::const_iterator it = origins.begin(); |
| 130 it != origins.end(); it++) { |
| 131 CachedOriginInfo* origin_info = GetCachedOriginInfo(*it); |
| 132 if (!origin_info) { |
| 133 // Restore 'origins_info' to its initial state. |
| 134 origins_info->clear(); |
| 135 return false; |
| 136 } |
| 137 origins_info->push_back(OriginInfo(*origin_info)); |
| 138 } |
| 139 |
| 140 return true; |
| 141 } |
| 142 |
| 143 void DatabaseTracker::SetOriginQuota(const string16& origin_identifier, |
| 144 int64 new_quota) { |
| 145 if (quota_table_->SetOriginQuota(origin_identifier, new_quota) && |
| 146 (origins_info_map_.find(origin_identifier) != origins_info_map_.end())) { |
| 147 origins_info_map_[origin_identifier].SetQuota(new_quota); |
| 148 } |
| 149 } |
| 150 |
| 151 bool DatabaseTracker::DeleteDatabase(const string16& origin_identifier, |
| 152 const string16& database_name) { |
| 153 // Check if the database is opened by any renderer. |
| 154 if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) |
| 155 return false; |
| 156 |
| 157 // Try to delete the file on the hard drive. |
| 158 FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); |
| 159 if (file_util::PathExists(db_file) && !file_util::Delete(db_file, false)) |
| 160 return false; |
| 161 |
| 162 // Clean up the main database and invalidate the cached record. |
| 163 databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); |
| 164 origins_info_map_.erase(origin_identifier); |
| 165 return true; |
| 166 } |
| 167 |
| 168 bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { |
| 169 // Check if any database in this origin is opened by any renderer. |
| 170 if (database_connections_.IsOriginUsed(origin_identifier)) |
| 171 return false; |
| 172 |
| 173 // We need to invalidate the cached record whether file_util::Delete() |
| 174 // succeeds or not, because even if it fails, it might still delete some |
| 175 // DB files on the hard drive. |
| 176 origins_info_map_.erase(origin_identifier); |
| 177 FilePath origin_dir = db_dir_.Append(FilePath::FromWStringHack( |
| 178 UTF16ToWide(origin_identifier))); |
| 179 if (!file_util::Delete(origin_dir, true)) |
| 180 return false; |
| 181 |
| 182 databases_table_->DeleteOrigin(origin_identifier); |
| 183 return true; |
| 184 } |
| 185 |
113 bool DatabaseTracker::LazyInit() { | 186 bool DatabaseTracker::LazyInit() { |
114 if (!initialized_) { | 187 if (!initialized_) { |
115 DCHECK(!db_->is_open()); | 188 DCHECK(!db_->is_open()); |
116 DCHECK(!databases_table_.get()); | 189 DCHECK(!databases_table_.get()); |
| 190 DCHECK(!quota_table_.get()); |
117 DCHECK(!meta_table_.get()); | 191 DCHECK(!meta_table_.get()); |
118 | 192 |
119 // If the tracker database exists, but it's corrupt or doesn't | 193 // If the tracker database exists, but it's corrupt or doesn't |
120 // have a meta table, delete the database directory | 194 // have a meta table, delete the database directory. |
121 const FilePath kTrackerDatabaseFullPath = | 195 const FilePath kTrackerDatabaseFullPath = |
122 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); | 196 db_dir_.Append(FilePath(kTrackerDatabaseFileName)); |
123 if (file_util::DirectoryExists(db_dir_) && | 197 if (file_util::DirectoryExists(db_dir_) && |
124 file_util::PathExists(kTrackerDatabaseFullPath) && | 198 file_util::PathExists(kTrackerDatabaseFullPath) && |
125 (!db_->Open(kTrackerDatabaseFullPath) || | 199 (!db_->Open(kTrackerDatabaseFullPath) || |
126 !db_->DoesTableExist("meta"))) { | 200 !sql::MetaTable::DoesTableExist(db_.get()))) { |
127 db_->Close(); | 201 db_->Close(); |
128 if (!file_util::Delete(db_dir_, true)) | 202 if (!file_util::Delete(db_dir_, true)) |
129 return false; | 203 return false; |
130 } | 204 } |
131 | 205 |
132 databases_table_.reset(new DatabasesTable(db_.get())); | 206 databases_table_.reset(new DatabasesTable(db_.get())); |
| 207 quota_table_.reset(new QuotaTable(db_.get())); |
133 meta_table_.reset(new sql::MetaTable()); | 208 meta_table_.reset(new sql::MetaTable()); |
134 | 209 |
135 initialized_ = | 210 initialized_ = |
136 file_util::CreateDirectory(db_dir_) && | 211 file_util::CreateDirectory(db_dir_) && |
137 (db_->is_open() || db_->Open(kTrackerDatabaseFullPath)) && | 212 (db_->is_open() || db_->Open(kTrackerDatabaseFullPath)) && |
138 meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) && | 213 UpgradeToCurrentVersion(); |
139 (meta_table_->GetCompatibleVersionNumber() <= kCurrentVersion) && | |
140 databases_table_->Init(); | |
141 if (!initialized_) { | 214 if (!initialized_) { |
142 databases_table_.reset(NULL); | 215 databases_table_.reset(NULL); |
| 216 quota_table_.reset(NULL); |
143 meta_table_.reset(NULL); | 217 meta_table_.reset(NULL); |
144 db_->Close(); | 218 db_->Close(); |
145 } | 219 } |
146 } | 220 } |
147 return initialized_; | 221 return initialized_; |
148 } | 222 } |
149 | 223 |
| 224 bool DatabaseTracker::UpgradeToCurrentVersion() { |
| 225 sql::Transaction transaction(db_.get()); |
| 226 if (!transaction.Begin() || |
| 227 !meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion) || |
| 228 (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) || |
| 229 !databases_table_->Init() || |
| 230 !quota_table_->Init()) |
| 231 return false; |
| 232 |
| 233 if (meta_table_->GetVersionNumber() < kCurrentVersion) |
| 234 meta_table_->SetVersionNumber(kCurrentVersion); |
| 235 |
| 236 return transaction.Commit(); |
| 237 } |
| 238 |
150 void DatabaseTracker::InsertOrUpdateDatabaseDetails( | 239 void DatabaseTracker::InsertOrUpdateDatabaseDetails( |
151 const string16& origin_identifier, | 240 const string16& origin_identifier, |
152 const string16& database_name, | 241 const string16& database_name, |
153 const string16& database_description, | 242 const string16& database_description, |
154 int64 estimated_size) { | 243 int64 estimated_size) { |
155 DatabaseDetails details; | 244 DatabaseDetails details; |
156 if (!databases_table_->GetDatabaseDetails( | 245 if (!databases_table_->GetDatabaseDetails( |
157 origin_identifier, database_name, &details)) { | 246 origin_identifier, database_name, &details)) { |
158 details.origin_identifier = origin_identifier; | 247 details.origin_identifier = origin_identifier; |
159 details.database_name = database_name; | 248 details.database_name = database_name; |
160 details.description = database_description; | 249 details.description = database_description; |
161 details.estimated_size = estimated_size; | 250 details.estimated_size = estimated_size; |
162 databases_table_->InsertDatabaseDetails(details); | 251 databases_table_->InsertDatabaseDetails(details); |
163 } else if ((details.description != database_description) || | 252 } else if ((details.description != database_description) || |
164 (details.estimated_size != estimated_size)) { | 253 (details.estimated_size != estimated_size)) { |
165 details.description = database_description; | 254 details.description = database_description; |
166 details.estimated_size = estimated_size; | 255 details.estimated_size = estimated_size; |
167 databases_table_->UpdateDatabaseDetails(details); | 256 databases_table_->UpdateDatabaseDetails(details); |
168 } | 257 } |
169 } | 258 } |
170 | 259 |
171 int64 DatabaseTracker::GetDBFileSize(const string16& origin_identifier, | |
172 const string16& database_name) const { | |
173 FilePath db_file_name = GetFullDBFilePath(origin_identifier, database_name); | |
174 int64 db_file_size = 0; | |
175 if (!file_util::GetFileSize(db_file_name, &db_file_size)) | |
176 db_file_size = 0; | |
177 return db_file_size; | |
178 } | |
179 | |
180 void DatabaseTracker::ClearAllCachedOriginInfo() { | 260 void DatabaseTracker::ClearAllCachedOriginInfo() { |
181 origins_info_map_.clear(); | 261 origins_info_map_.clear(); |
182 } | 262 } |
183 | 263 |
184 DatabaseTracker::CachedOriginInfo* DatabaseTracker::GetCachedOriginInfo( | 264 DatabaseTracker::CachedOriginInfo* DatabaseTracker::GetCachedOriginInfo( |
185 const string16& origin_identifier) { | 265 const string16& origin_identifier) { |
| 266 if (!LazyInit()) |
| 267 return NULL; |
| 268 |
186 // Populate the cache with data for this origin if needed. | 269 // Populate the cache with data for this origin if needed. |
187 if (origins_info_map_.find(origin_identifier) == origins_info_map_.end()) { | 270 if (origins_info_map_.find(origin_identifier) == origins_info_map_.end()) { |
188 std::vector<DatabaseDetails> details; | 271 std::vector<DatabaseDetails> details; |
189 if (!databases_table_->GetAllDatabaseDetailsForOrigin( | 272 if (!databases_table_->GetAllDatabaseDetailsForOrigin( |
190 origin_identifier, &details)) { | 273 origin_identifier, &details)) { |
191 return NULL; | 274 return NULL; |
192 } | 275 } |
193 | 276 |
194 CachedOriginInfo& origin_info = origins_info_map_[origin_identifier]; | 277 CachedOriginInfo& origin_info = origins_info_map_[origin_identifier]; |
| 278 origin_info.SetOrigin(origin_identifier); |
195 for (std::vector<DatabaseDetails>::const_iterator it = details.begin(); | 279 for (std::vector<DatabaseDetails>::const_iterator it = details.begin(); |
196 it != details.end(); it++) { | 280 it != details.end(); it++) { |
197 int64 db_file_size = | 281 int64 db_file_size = |
198 GetDBFileSize(it->origin_identifier, it->database_name); | 282 GetDBFileSize(origin_identifier, it->database_name); |
199 origin_info.SetCachedDatabaseSize(it->database_name, db_file_size); | 283 origin_info.SetDatabaseSize(it->database_name, db_file_size); |
| 284 } |
| 285 |
| 286 int64 origin_quota = quota_table_->GetOriginQuota(origin_identifier); |
| 287 if (origin_quota > 0) { |
| 288 origin_info.SetQuota(origin_quota); |
| 289 } else if (StartsWith(origin_identifier, |
| 290 ASCIIToUTF16(kExtensionOriginIdentifierPrefix), |
| 291 true)) { |
| 292 origin_info.SetQuota(kDefaultExtensionQuota); |
| 293 } else { |
| 294 origin_info.SetQuota(kDefaultQuota); |
200 } | 295 } |
201 } | 296 } |
202 | 297 |
203 return &origins_info_map_[origin_identifier]; | 298 return &origins_info_map_[origin_identifier]; |
204 } | 299 } |
205 | 300 |
206 int64 DatabaseTracker::GetCachedDatabaseFileSize( | 301 int64 DatabaseTracker::GetDBFileSize(const string16& origin_identifier, |
207 const string16& origin_identifier, | 302 const string16& database_name) const { |
208 const string16& database_name) { | 303 FilePath db_file_name = GetFullDBFilePath(origin_identifier, database_name); |
| 304 int64 db_file_size = 0; |
| 305 if (!file_util::GetFileSize(db_file_name, &db_file_size)) |
| 306 db_file_size = 0; |
| 307 return db_file_size; |
| 308 } |
| 309 |
| 310 int64 DatabaseTracker::GetOriginSpaceAvailable( |
| 311 const string16& origin_identifier) { |
209 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | 312 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); |
210 if (!origin_info) | 313 if (!origin_info) |
211 return 0; | 314 return 0; |
212 return origin_info->GetCachedDatabaseSize(database_name); | 315 int64 space_available = origin_info->Quota() - origin_info->TotalSize(); |
| 316 return (space_available < 0 ? 0 : space_available); |
213 } | 317 } |
214 | 318 |
215 int64 DatabaseTracker::UpdateCachedDatabaseFileSize( | 319 int64 DatabaseTracker::UpdateCachedDatabaseFileSize( |
216 const string16& origin_identifier, | 320 const string16& origin_identifier, |
217 const string16& database_name) { | 321 const string16& database_name) { |
218 int64 new_size = GetDBFileSize(origin_identifier, database_name); | 322 int64 new_size = GetDBFileSize(origin_identifier, database_name); |
219 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | 323 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); |
220 if (origin_info) | 324 if (origin_info) |
221 origin_info->SetCachedDatabaseSize(database_name, new_size); | 325 origin_info->SetDatabaseSize(database_name, new_size); |
222 return new_size; | 326 return new_size; |
223 } | 327 } |
224 | 328 |
225 int64 DatabaseTracker::GetOriginUsage(const string16& origin_identifier) { | |
226 CachedOriginInfo* origin_info = GetCachedOriginInfo(origin_identifier); | |
227 if (!origin_info) | |
228 return kint64max; | |
229 return origin_info->TotalSize(); | |
230 } | |
231 | |
232 int64 DatabaseTracker::GetOriginQuota( | |
233 const string16& origin_identifier) const { | |
234 if (StartsWith(origin_identifier, | |
235 ASCIIToUTF16(kExtensionOriginIdentifierPrefix), true)) { | |
236 return kDefaultExtensionQuota; | |
237 } | |
238 | |
239 return kDefaultQuota; | |
240 } | |
241 | |
242 int64 DatabaseTracker::GetOriginSpaceAvailable( | |
243 const string16& origin_identifier) { | |
244 int64 space_available = GetOriginQuota(origin_identifier) - | |
245 GetOriginUsage(origin_identifier); | |
246 return (space_available < 0 ? 0 : space_available); | |
247 } | |
248 | |
249 } // namespace webkit_database | 329 } // namespace webkit_database |
OLD | NEW |