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