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.
5 #include "webkit/database/database_tracker.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/file_util.h"
13 #include "base/message_loop_proxy.h"
14 #include "base/platform_file.h"
15 #include "base/string_number_conversions.h"
16 #include "base/utf_string_conversions.h"
17 #include "net/base/net_errors.h"
18 #include "sql/connection.h"
19 #include "sql/diagnostic_error_delegate.h"
20 #include "sql/meta_table.h"
21 #include "sql/transaction.h"
22 #include "third_party/sqlite/sqlite3.h"
23 #include "webkit/database/database_quota_client.h"
24 #include "webkit/database/database_util.h"
25 #include "webkit/database/databases_table.h"
26 #include "webkit/quota/quota_manager.h"
27 #include "webkit/quota/special_storage_policy.h"
31 class HistogramUniquifier
{
33 static const char* name() { return "Sqlite.DatabaseTracker.Error"; }
36 sql::ErrorDelegate
* GetErrorHandlerForTrackerDb() {
37 return new sql::DiagnosticErrorDelegate
<HistogramUniquifier
>();
42 namespace webkit_database
{
44 const FilePath::CharType kDatabaseDirectoryName
[] =
45 FILE_PATH_LITERAL("databases");
46 const FilePath::CharType kIncognitoDatabaseDirectoryName
[] =
47 FILE_PATH_LITERAL("databases-incognito");
48 const FilePath::CharType kTrackerDatabaseFileName
[] =
49 FILE_PATH_LITERAL("Databases.db");
50 static const int kCurrentVersion
= 2;
51 static const int kCompatibleVersion
= 1;
53 const FilePath::CharType kTemporaryDirectoryPrefix
[] =
54 FILE_PATH_LITERAL("DeleteMe");
55 const FilePath::CharType kTemporaryDirectoryPattern
[] =
56 FILE_PATH_LITERAL("DeleteMe*");
58 OriginInfo::OriginInfo()
61 OriginInfo::OriginInfo(const OriginInfo
& origin_info
)
62 : origin_(origin_info
.origin_
),
63 total_size_(origin_info
.total_size_
),
64 database_info_(origin_info
.database_info_
) {}
66 OriginInfo::~OriginInfo() {}
68 void OriginInfo::GetAllDatabaseNames(std::vector
<string16
>* databases
) const {
69 for (DatabaseInfoMap::const_iterator it
= database_info_
.begin();
70 it
!= database_info_
.end(); it
++) {
71 databases
->push_back(it
->first
);
75 int64
OriginInfo::GetDatabaseSize(const string16
& database_name
) const {
76 DatabaseInfoMap::const_iterator it
= database_info_
.find(database_name
);
77 if (it
!= database_info_
.end())
78 return it
->second
.first
;
82 string16
OriginInfo::GetDatabaseDescription(
83 const string16
& database_name
) const {
84 DatabaseInfoMap::const_iterator it
= database_info_
.find(database_name
);
85 if (it
!= database_info_
.end())
86 return it
->second
.second
;
90 OriginInfo::OriginInfo(const string16
& origin
, int64 total_size
)
91 : origin_(origin
), total_size_(total_size
) {}
93 DatabaseTracker::DatabaseTracker(
94 const FilePath
& profile_path
,
96 quota::SpecialStoragePolicy
* special_storage_policy
,
97 quota::QuotaManagerProxy
* quota_manager_proxy
,
98 base::MessageLoopProxy
* db_tracker_thread
)
99 : is_initialized_(false),
100 is_incognito_(is_incognito
),
101 force_keep_session_state_(false),
102 shutting_down_(false),
103 profile_path_(profile_path
),
104 db_dir_(is_incognito_
?
105 profile_path_
.Append(kIncognitoDatabaseDirectoryName
) :
106 profile_path_
.Append(kDatabaseDirectoryName
)),
107 db_(new sql::Connection()),
108 databases_table_(NULL
),
110 special_storage_policy_(special_storage_policy
),
111 quota_manager_proxy_(quota_manager_proxy
),
112 db_tracker_thread_(db_tracker_thread
),
113 incognito_origin_directories_generator_(0) {
114 if (quota_manager_proxy
) {
115 quota_manager_proxy
->RegisterClient(
116 new DatabaseQuotaClient(db_tracker_thread
, this));
120 DatabaseTracker::~DatabaseTracker() {
121 DCHECK(dbs_to_be_deleted_
.empty());
122 DCHECK(deletion_callbacks_
.empty());
125 void DatabaseTracker::DatabaseOpened(const string16
& origin_identifier
,
126 const string16
& database_name
,
127 const string16
& database_description
,
128 int64 estimated_size
,
129 int64
* database_size
) {
130 if (shutting_down_
|| !LazyInit()) {
135 if (quota_manager_proxy_
)
136 quota_manager_proxy_
->NotifyStorageAccessed(
137 quota::QuotaClient::kDatabase
,
138 DatabaseUtil::GetOriginFromIdentifier(origin_identifier
),
139 quota::kStorageTypeTemporary
);
141 InsertOrUpdateDatabaseDetails(origin_identifier
, database_name
,
142 database_description
, estimated_size
);
143 if (database_connections_
.AddConnection(origin_identifier
, database_name
)) {
144 *database_size
= SeedOpenDatabaseInfo(origin_identifier
,
146 database_description
);
149 *database_size
= UpdateOpenDatabaseInfoAndNotify(origin_identifier
,
151 &database_description
);
154 void DatabaseTracker::DatabaseModified(const string16
& origin_identifier
,
155 const string16
& database_name
) {
158 UpdateOpenDatabaseSizeAndNotify(origin_identifier
, database_name
);
161 void DatabaseTracker::DatabaseClosed(const string16
& origin_identifier
,
162 const string16
& database_name
) {
163 if (database_connections_
.IsEmpty()) {
164 DCHECK(!is_initialized_
);
168 // We call NotifiyStorageAccessed when a db is opened and also when
169 // closed because we don't call it for read while open.
170 if (quota_manager_proxy_
)
171 quota_manager_proxy_
->NotifyStorageAccessed(
172 quota::QuotaClient::kDatabase
,
173 DatabaseUtil::GetOriginFromIdentifier(origin_identifier
),
174 quota::kStorageTypeTemporary
);
176 UpdateOpenDatabaseSizeAndNotify(origin_identifier
, database_name
);
177 if (database_connections_
.RemoveConnection(origin_identifier
, database_name
))
178 DeleteDatabaseIfNeeded(origin_identifier
, database_name
);
181 void DatabaseTracker::HandleSqliteError(
182 const string16
& origin_identifier
,
183 const string16
& database_name
,
185 // We only handle errors that indicate corruption and we
186 // do so with a heavy hand, we delete it. Any renderers/workers
187 // with this database open will receive a message to close it
188 // immediately, once all have closed, the files will be deleted.
189 // In the interim, all attempts to open a new connection to that
190 // database will fail.
191 // Note: the client-side filters out all but these two errors as
192 // a small optimization, see WebDatabaseObserverImpl::HandleSqliteError.
193 if (error
== SQLITE_CORRUPT
|| error
== SQLITE_NOTADB
) {
194 DeleteDatabase(origin_identifier
, database_name
,
195 net::CompletionCallback());
199 void DatabaseTracker::CloseDatabases(const DatabaseConnections
& connections
) {
200 if (database_connections_
.IsEmpty()) {
201 DCHECK(!is_initialized_
|| connections
.IsEmpty());
205 // When being closed by this route, there's a chance that
206 // the tracker missed some DatabseModified calls. This method is used
207 // when a renderer crashes to cleanup its open resources.
208 // We need to examine what we have in connections for the
209 // size of each open databases and notify any differences between the
210 // actual file sizes now.
211 std::vector
<std::pair
<string16
, string16
> > open_dbs
;
212 connections
.ListConnections(&open_dbs
);
213 for (std::vector
<std::pair
<string16
, string16
> >::iterator it
=
214 open_dbs
.begin(); it
!= open_dbs
.end(); ++it
)
215 UpdateOpenDatabaseSizeAndNotify(it
->first
, it
->second
);
217 std::vector
<std::pair
<string16
, string16
> > closed_dbs
;
218 database_connections_
.RemoveConnections(connections
, &closed_dbs
);
219 for (std::vector
<std::pair
<string16
, string16
> >::iterator it
=
220 closed_dbs
.begin(); it
!= closed_dbs
.end(); ++it
) {
221 DeleteDatabaseIfNeeded(it
->first
, it
->second
);
225 void DatabaseTracker::DeleteDatabaseIfNeeded(const string16
& origin_identifier
,
226 const string16
& database_name
) {
227 DCHECK(!database_connections_
.IsDatabaseOpened(origin_identifier
,
229 if (IsDatabaseScheduledForDeletion(origin_identifier
, database_name
)) {
230 DeleteClosedDatabase(origin_identifier
, database_name
);
231 dbs_to_be_deleted_
[origin_identifier
].erase(database_name
);
232 if (dbs_to_be_deleted_
[origin_identifier
].empty())
233 dbs_to_be_deleted_
.erase(origin_identifier
);
235 PendingDeletionCallbacks::iterator callback
= deletion_callbacks_
.begin();
236 while (callback
!= deletion_callbacks_
.end()) {
237 DatabaseSet::iterator found_origin
=
238 callback
->second
.find(origin_identifier
);
239 if (found_origin
!= callback
->second
.end()) {
240 std::set
<string16
>& databases
= found_origin
->second
;
241 databases
.erase(database_name
);
242 if (databases
.empty()) {
243 callback
->second
.erase(found_origin
);
244 if (callback
->second
.empty()) {
245 net::CompletionCallback cb
= callback
->first
;
247 callback
= deletion_callbacks_
.erase(callback
);
258 void DatabaseTracker::AddObserver(Observer
* observer
) {
259 observers_
.AddObserver(observer
);
262 void DatabaseTracker::RemoveObserver(Observer
* observer
) {
263 // When we remove a listener, we do not know which cached information
264 // is still needed and which information can be discarded. So we just
265 // clear all caches and re-populate them as needed.
266 observers_
.RemoveObserver(observer
);
267 ClearAllCachedOriginInfo();
270 void DatabaseTracker::CloseTrackerDatabaseAndClearCaches() {
271 ClearAllCachedOriginInfo();
273 if (!is_incognito_
) {
274 meta_table_
.reset(NULL
);
275 databases_table_
.reset(NULL
);
277 is_initialized_
= false;
281 string16
DatabaseTracker::GetOriginDirectory(
282 const string16
& origin_identifier
) {
284 return origin_identifier
;
286 OriginDirectoriesMap::const_iterator it
=
287 incognito_origin_directories_
.find(origin_identifier
);
288 if (it
!= incognito_origin_directories_
.end())
291 string16 origin_directory
=
292 base::IntToString16(incognito_origin_directories_generator_
++);
293 incognito_origin_directories_
[origin_identifier
] = origin_directory
;
294 return origin_directory
;
297 FilePath
DatabaseTracker::GetFullDBFilePath(
298 const string16
& origin_identifier
,
299 const string16
& database_name
) {
300 DCHECK(!origin_identifier
.empty());
304 int64 id
= databases_table_
->GetDatabaseID(
305 origin_identifier
, database_name
);
309 FilePath file_name
= FilePath::FromWStringHack(
310 UTF8ToWide(base::Int64ToString(id
)));
311 return db_dir_
.Append(FilePath::FromWStringHack(
312 UTF16ToWide(GetOriginDirectory(origin_identifier
)))).Append(file_name
);
315 bool DatabaseTracker::GetOriginInfo(const string16
& origin_identifier
,
318 CachedOriginInfo
* cached_info
= GetCachedOriginInfo(origin_identifier
);
321 *info
= OriginInfo(*cached_info
);
325 bool DatabaseTracker::GetAllOriginIdentifiers(
326 std::vector
<string16
>* origin_identifiers
) {
327 DCHECK(origin_identifiers
);
328 DCHECK(origin_identifiers
->empty());
331 return databases_table_
->GetAllOrigins(origin_identifiers
);
334 bool DatabaseTracker::GetAllOriginsInfo(std::vector
<OriginInfo
>* origins_info
) {
335 DCHECK(origins_info
);
336 DCHECK(origins_info
->empty());
338 std::vector
<string16
> origins
;
339 if (!GetAllOriginIdentifiers(&origins
))
342 for (std::vector
<string16
>::const_iterator it
= origins
.begin();
343 it
!= origins
.end(); it
++) {
344 CachedOriginInfo
* origin_info
= GetCachedOriginInfo(*it
);
346 // Restore 'origins_info' to its initial state.
347 origins_info
->clear();
350 origins_info
->push_back(OriginInfo(*origin_info
));
356 bool DatabaseTracker::DeleteClosedDatabase(const string16
& origin_identifier
,
357 const string16
& database_name
) {
361 // Check if the database is opened by any renderer.
362 if (database_connections_
.IsDatabaseOpened(origin_identifier
, database_name
))
365 int64 db_file_size
= quota_manager_proxy_
?
366 GetDBFileSize(origin_identifier
, database_name
) : 0;
368 // Try to delete the file on the hard drive.
369 FilePath db_file
= GetFullDBFilePath(origin_identifier
, database_name
);
370 if (file_util::PathExists(db_file
) && !file_util::Delete(db_file
, false))
373 // Also delete any orphaned journal file.
374 DCHECK(db_file
.Extension().empty());
375 file_util::Delete(db_file
.InsertBeforeExtensionASCII(
376 DatabaseUtil::kJournalFileSuffix
), false);
378 if (quota_manager_proxy_
&& db_file_size
)
379 quota_manager_proxy_
->NotifyStorageModified(
380 quota::QuotaClient::kDatabase
,
381 DatabaseUtil::GetOriginFromIdentifier(origin_identifier
),
382 quota::kStorageTypeTemporary
,
385 // Clean up the main database and invalidate the cached record.
386 databases_table_
->DeleteDatabaseDetails(origin_identifier
, database_name
);
387 origins_info_map_
.erase(origin_identifier
);
389 std::vector
<DatabaseDetails
> details
;
390 if (databases_table_
->GetAllDatabaseDetailsForOrigin(
391 origin_identifier
, &details
) && details
.empty()) {
392 // Try to delete the origin in case this was the last database.
393 DeleteOrigin(origin_identifier
, false);
398 bool DatabaseTracker::DeleteOrigin(const string16
& origin_identifier
,
403 // Check if any database in this origin is opened by any renderer.
404 if (database_connections_
.IsOriginUsed(origin_identifier
) && !force
)
407 int64 deleted_size
= 0;
408 if (quota_manager_proxy_
) {
409 CachedOriginInfo
* origin_info
= GetCachedOriginInfo(origin_identifier
);
411 deleted_size
= origin_info
->TotalSize();
414 origins_info_map_
.erase(origin_identifier
);
415 FilePath origin_dir
= db_dir_
.Append(FilePath::FromWStringHack(
416 UTF16ToWide(origin_identifier
)));
418 // Create a temporary directory to move possibly still existing databases to,
419 // as we can't delete the origin directory on windows if it contains opened
421 FilePath new_origin_dir
;
422 file_util::CreateTemporaryDirInDir(db_dir_
,
423 kTemporaryDirectoryPrefix
,
425 file_util::FileEnumerator
databases(
428 file_util::FileEnumerator::FILES
);
429 for (FilePath database
= databases
.Next(); !database
.empty();
430 database
= databases
.Next()) {
431 FilePath new_file
= new_origin_dir
.Append(database
.BaseName());
432 file_util::Move(database
, new_file
);
434 file_util::Delete(origin_dir
, true);
435 file_util::Delete(new_origin_dir
, true); // might fail on windows.
437 databases_table_
->DeleteOrigin(origin_identifier
);
439 if (quota_manager_proxy_
&& deleted_size
) {
440 quota_manager_proxy_
->NotifyStorageModified(
441 quota::QuotaClient::kDatabase
,
442 DatabaseUtil::GetOriginFromIdentifier(origin_identifier
),
443 quota::kStorageTypeTemporary
,
450 bool DatabaseTracker::IsDatabaseScheduledForDeletion(
451 const string16
& origin_identifier
,
452 const string16
& database_name
) {
453 DatabaseSet::iterator it
= dbs_to_be_deleted_
.find(origin_identifier
);
454 if (it
== dbs_to_be_deleted_
.end())
457 std::set
<string16
>& databases
= it
->second
;
458 return (databases
.find(database_name
) != databases
.end());
461 bool DatabaseTracker::LazyInit() {
462 if (!is_initialized_
&& !shutting_down_
) {
463 DCHECK(!db_
->is_open());
464 DCHECK(!databases_table_
.get());
465 DCHECK(!meta_table_
.get());
467 // If there are left-over directories from failed deletion attempts, clean
469 if (file_util::DirectoryExists(db_dir_
)) {
470 file_util::FileEnumerator
directories(
473 file_util::FileEnumerator::DIRECTORIES
,
474 kTemporaryDirectoryPattern
);
475 for (FilePath directory
= directories
.Next(); !directory
.empty();
476 directory
= directories
.Next()) {
477 file_util::Delete(directory
, true);
481 // If the tracker database exists, but it's corrupt or doesn't
482 // have a meta table, delete the database directory.
483 const FilePath kTrackerDatabaseFullPath
=
484 db_dir_
.Append(FilePath(kTrackerDatabaseFileName
));
485 if (file_util::DirectoryExists(db_dir_
) &&
486 file_util::PathExists(kTrackerDatabaseFullPath
) &&
487 (!db_
->Open(kTrackerDatabaseFullPath
) ||
488 !sql::MetaTable::DoesTableExist(db_
.get()))) {
490 if (!file_util::Delete(db_dir_
, true))
494 db_
->set_error_delegate(GetErrorHandlerForTrackerDb());
496 databases_table_
.reset(new DatabasesTable(db_
.get()));
497 meta_table_
.reset(new sql::MetaTable());
500 file_util::CreateDirectory(db_dir_
) &&
502 (is_incognito_
? db_
->OpenInMemory() :
503 db_
->Open(kTrackerDatabaseFullPath
))) &&
504 UpgradeToCurrentVersion();
505 if (!is_initialized_
) {
506 databases_table_
.reset(NULL
);
507 meta_table_
.reset(NULL
);
511 return is_initialized_
;
514 bool DatabaseTracker::UpgradeToCurrentVersion() {
515 sql::Transaction
transaction(db_
.get());
516 if (!transaction
.Begin() ||
517 !meta_table_
->Init(db_
.get(), kCurrentVersion
, kCompatibleVersion
) ||
518 (meta_table_
->GetCompatibleVersionNumber() > kCurrentVersion
) ||
519 !databases_table_
->Init())
522 if (meta_table_
->GetVersionNumber() < kCurrentVersion
)
523 meta_table_
->SetVersionNumber(kCurrentVersion
);
525 return transaction
.Commit();
528 void DatabaseTracker::InsertOrUpdateDatabaseDetails(
529 const string16
& origin_identifier
,
530 const string16
& database_name
,
531 const string16
& database_description
,
532 int64 estimated_size
) {
533 DatabaseDetails details
;
534 if (!databases_table_
->GetDatabaseDetails(
535 origin_identifier
, database_name
, &details
)) {
536 details
.origin_identifier
= origin_identifier
;
537 details
.database_name
= database_name
;
538 details
.description
= database_description
;
539 details
.estimated_size
= estimated_size
;
540 databases_table_
->InsertDatabaseDetails(details
);
541 } else if ((details
.description
!= database_description
) ||
542 (details
.estimated_size
!= estimated_size
)) {
543 details
.description
= database_description
;
544 details
.estimated_size
= estimated_size
;
545 databases_table_
->UpdateDatabaseDetails(details
);
549 void DatabaseTracker::ClearAllCachedOriginInfo() {
550 origins_info_map_
.clear();
553 DatabaseTracker::CachedOriginInfo
* DatabaseTracker::MaybeGetCachedOriginInfo(
554 const string16
& origin_identifier
, bool create_if_needed
) {
558 // Populate the cache with data for this origin if needed.
559 if (origins_info_map_
.find(origin_identifier
) == origins_info_map_
.end()) {
560 if (!create_if_needed
)
563 std::vector
<DatabaseDetails
> details
;
564 if (!databases_table_
->GetAllDatabaseDetailsForOrigin(
565 origin_identifier
, &details
)) {
569 CachedOriginInfo
& origin_info
= origins_info_map_
[origin_identifier
];
570 origin_info
.SetOrigin(origin_identifier
);
571 for (std::vector
<DatabaseDetails
>::const_iterator it
= details
.begin();
572 it
!= details
.end(); it
++) {
574 if (database_connections_
.IsDatabaseOpened(
575 origin_identifier
, it
->database_name
)) {
576 db_file_size
= database_connections_
.GetOpenDatabaseSize(
577 origin_identifier
, it
->database_name
);
579 db_file_size
= GetDBFileSize(origin_identifier
, it
->database_name
);
581 origin_info
.SetDatabaseSize(it
->database_name
, db_file_size
);
582 origin_info
.SetDatabaseDescription(it
->database_name
, it
->description
);
586 return &origins_info_map_
[origin_identifier
];
589 int64
DatabaseTracker::GetDBFileSize(const string16
& origin_identifier
,
590 const string16
& database_name
) {
591 FilePath db_file_name
= GetFullDBFilePath(origin_identifier
, database_name
);
592 int64 db_file_size
= 0;
593 if (!file_util::GetFileSize(db_file_name
, &db_file_size
))
598 int64
DatabaseTracker::SeedOpenDatabaseInfo(
599 const string16
& origin_id
, const string16
& name
,
600 const string16
& description
) {
601 DCHECK(database_connections_
.IsDatabaseOpened(origin_id
, name
));
602 int64 size
= GetDBFileSize(origin_id
, name
);
603 database_connections_
.SetOpenDatabaseSize(origin_id
, name
, size
);
604 CachedOriginInfo
* info
= MaybeGetCachedOriginInfo(origin_id
, false);
606 info
->SetDatabaseSize(name
, size
);
607 info
->SetDatabaseDescription(name
, description
);
612 int64
DatabaseTracker::UpdateOpenDatabaseInfoAndNotify(
613 const string16
& origin_id
, const string16
& name
,
614 const string16
* opt_description
) {
615 DCHECK(database_connections_
.IsDatabaseOpened(origin_id
, name
));
616 int64 new_size
= GetDBFileSize(origin_id
, name
);
617 int64 old_size
= database_connections_
.GetOpenDatabaseSize(origin_id
, name
);
618 CachedOriginInfo
* info
= MaybeGetCachedOriginInfo(origin_id
, false);
619 if (info
&& opt_description
)
620 info
->SetDatabaseDescription(name
, *opt_description
);
621 if (old_size
!= new_size
) {
622 database_connections_
.SetOpenDatabaseSize(origin_id
, name
, new_size
);
624 info
->SetDatabaseSize(name
, new_size
);
625 if (quota_manager_proxy_
)
626 quota_manager_proxy_
->NotifyStorageModified(
627 quota::QuotaClient::kDatabase
,
628 DatabaseUtil::GetOriginFromIdentifier(origin_id
),
629 quota::kStorageTypeTemporary
,
630 new_size
- old_size
);
631 FOR_EACH_OBSERVER(Observer
, observers_
, OnDatabaseSizeChanged(
632 origin_id
, name
, new_size
));
637 void DatabaseTracker::ScheduleDatabaseForDeletion(
638 const string16
& origin_identifier
,
639 const string16
& database_name
) {
640 DCHECK(database_connections_
.IsDatabaseOpened(origin_identifier
,
642 dbs_to_be_deleted_
[origin_identifier
].insert(database_name
);
643 FOR_EACH_OBSERVER(Observer
, observers_
, OnDatabaseScheduledForDeletion(
644 origin_identifier
, database_name
));
647 void DatabaseTracker::ScheduleDatabasesForDeletion(
648 const DatabaseSet
& databases
,
649 const net::CompletionCallback
& callback
) {
650 DCHECK(!databases
.empty());
652 if (!callback
.is_null())
653 deletion_callbacks_
.push_back(std::make_pair(callback
, databases
));
654 for (DatabaseSet::const_iterator ori
= databases
.begin();
655 ori
!= databases
.end(); ++ori
) {
656 for (std::set
<string16
>::const_iterator db
= ori
->second
.begin();
657 db
!= ori
->second
.end(); ++db
)
658 ScheduleDatabaseForDeletion(ori
->first
, *db
);
662 int DatabaseTracker::DeleteDatabase(const string16
& origin_identifier
,
663 const string16
& database_name
,
664 const net::CompletionCallback
& callback
) {
666 return net::ERR_FAILED
;
668 if (database_connections_
.IsDatabaseOpened(origin_identifier
,
670 if (!callback
.is_null()) {
672 set
[origin_identifier
].insert(database_name
);
673 deletion_callbacks_
.push_back(std::make_pair(callback
, set
));
675 ScheduleDatabaseForDeletion(origin_identifier
, database_name
);
676 return net::ERR_IO_PENDING
;
678 DeleteClosedDatabase(origin_identifier
, database_name
);
682 int DatabaseTracker::DeleteDataModifiedSince(
683 const base::Time
& cutoff
,
684 const net::CompletionCallback
& callback
) {
686 return net::ERR_FAILED
;
688 DatabaseSet to_be_deleted
;
690 std::vector
<string16
> origins_identifiers
;
691 if (!databases_table_
->GetAllOrigins(&origins_identifiers
))
692 return net::ERR_FAILED
;
694 for (std::vector
<string16
>::const_iterator ori
= origins_identifiers
.begin();
695 ori
!= origins_identifiers
.end(); ++ori
) {
696 if (special_storage_policy_
.get() &&
697 special_storage_policy_
->IsStorageProtected(
698 DatabaseUtil::GetOriginFromIdentifier(*ori
))) {
702 std::vector
<DatabaseDetails
> details
;
703 if (!databases_table_
->GetAllDatabaseDetailsForOrigin(*ori
, &details
))
704 rv
= net::ERR_FAILED
;
705 for (std::vector
<DatabaseDetails
>::const_iterator db
= details
.begin();
706 db
!= details
.end(); ++db
) {
707 FilePath db_file
= GetFullDBFilePath(*ori
, db
->database_name
);
708 base::PlatformFileInfo file_info
;
709 file_util::GetFileInfo(db_file
, &file_info
);
710 if (file_info
.last_modified
< cutoff
)
713 // Check if the database is opened by any renderer.
714 if (database_connections_
.IsDatabaseOpened(*ori
, db
->database_name
))
715 to_be_deleted
[*ori
].insert(db
->database_name
);
717 DeleteClosedDatabase(*ori
, db
->database_name
);
724 if (!to_be_deleted
.empty()) {
725 ScheduleDatabasesForDeletion(to_be_deleted
, callback
);
726 return net::ERR_IO_PENDING
;
731 int DatabaseTracker::DeleteDataForOrigin(
732 const string16
& origin
, const net::CompletionCallback
& callback
) {
734 return net::ERR_FAILED
;
736 DatabaseSet to_be_deleted
;
738 std::vector
<DatabaseDetails
> details
;
739 if (!databases_table_
->GetAllDatabaseDetailsForOrigin(origin
, &details
))
740 return net::ERR_FAILED
;
741 for (std::vector
<DatabaseDetails
>::const_iterator db
= details
.begin();
742 db
!= details
.end(); ++db
) {
743 // Check if the database is opened by any renderer.
744 if (database_connections_
.IsDatabaseOpened(origin
, db
->database_name
))
745 to_be_deleted
[origin
].insert(db
->database_name
);
747 DeleteClosedDatabase(origin
, db
->database_name
);
750 if (!to_be_deleted
.empty()) {
751 ScheduleDatabasesForDeletion(to_be_deleted
, callback
);
752 return net::ERR_IO_PENDING
;
757 void DatabaseTracker::GetIncognitoFileHandle(
758 const string16
& vfs_file_name
, base::PlatformFile
* file_handle
) const {
759 DCHECK(is_incognito_
);
760 FileHandlesMap::const_iterator it
=
761 incognito_file_handles_
.find(vfs_file_name
);
762 if (it
!= incognito_file_handles_
.end())
763 *file_handle
= it
->second
;
765 *file_handle
= base::kInvalidPlatformFileValue
;
768 void DatabaseTracker::SaveIncognitoFileHandle(
769 const string16
& vfs_file_name
, const base::PlatformFile
& file_handle
) {
770 DCHECK(is_incognito_
);
771 DCHECK(incognito_file_handles_
.find(vfs_file_name
) ==
772 incognito_file_handles_
.end());
773 if (file_handle
!= base::kInvalidPlatformFileValue
)
774 incognito_file_handles_
[vfs_file_name
] = file_handle
;
777 bool DatabaseTracker::CloseIncognitoFileHandle(const string16
& vfs_file_name
) {
778 DCHECK(is_incognito_
);
779 DCHECK(incognito_file_handles_
.find(vfs_file_name
) !=
780 incognito_file_handles_
.end());
782 bool handle_closed
= false;
783 FileHandlesMap::iterator it
= incognito_file_handles_
.find(vfs_file_name
);
784 if (it
!= incognito_file_handles_
.end()) {
785 handle_closed
= base::ClosePlatformFile(it
->second
);
787 incognito_file_handles_
.erase(it
);
789 return handle_closed
;
792 bool DatabaseTracker::HasSavedIncognitoFileHandle(
793 const string16
& vfs_file_name
) const {
794 return (incognito_file_handles_
.find(vfs_file_name
) !=
795 incognito_file_handles_
.end());
798 void DatabaseTracker::DeleteIncognitoDBDirectory() {
799 shutting_down_
= true;
800 is_initialized_
= false;
802 for (FileHandlesMap::iterator it
= incognito_file_handles_
.begin();
803 it
!= incognito_file_handles_
.end(); it
++)
804 base::ClosePlatformFile(it
->second
);
806 FilePath incognito_db_dir
=
807 profile_path_
.Append(kIncognitoDatabaseDirectoryName
);
808 if (file_util::DirectoryExists(incognito_db_dir
))
809 file_util::Delete(incognito_db_dir
, true);
812 void DatabaseTracker::ClearSessionOnlyOrigins() {
813 shutting_down_
= true;
815 bool has_session_only_databases
=
816 special_storage_policy_
.get() &&
817 special_storage_policy_
->HasSessionOnlyOrigins();
819 // Clearing only session-only databases, and there are none.
820 if (!has_session_only_databases
)
826 std::vector
<string16
> origin_identifiers
;
827 GetAllOriginIdentifiers(&origin_identifiers
);
829 for (std::vector
<string16
>::iterator origin
= origin_identifiers
.begin();
830 origin
!= origin_identifiers
.end(); ++origin
) {
832 webkit_database::DatabaseUtil::GetOriginFromIdentifier(*origin
);
833 if (!special_storage_policy_
->IsStorageSessionOnly(origin_url
))
835 if (special_storage_policy_
->IsStorageProtected(origin_url
))
837 webkit_database::OriginInfo origin_info
;
838 std::vector
<string16
> databases
;
839 GetOriginInfo(*origin
, &origin_info
);
840 origin_info
.GetAllDatabaseNames(&databases
);
842 for (std::vector
<string16
>::iterator database
= databases
.begin();
843 database
!= databases
.end(); ++database
) {
844 base::PlatformFile file_handle
= base::CreatePlatformFile(
845 GetFullDBFilePath(*origin
, *database
),
846 base::PLATFORM_FILE_OPEN_ALWAYS
|
847 base::PLATFORM_FILE_SHARE_DELETE
|
848 base::PLATFORM_FILE_DELETE_ON_CLOSE
|
849 base::PLATFORM_FILE_READ
,
851 base::ClosePlatformFile(file_handle
);
853 DeleteOrigin(*origin
, true);
858 void DatabaseTracker::Shutdown() {
859 DCHECK(db_tracker_thread_
.get());
860 DCHECK(db_tracker_thread_
->BelongsToCurrentThread());
861 if (shutting_down_
) {
866 DeleteIncognitoDBDirectory();
867 else if (!force_keep_session_state_
)
868 ClearSessionOnlyOrigins();
871 void DatabaseTracker::SetForceKeepSessionState() {
872 DCHECK(db_tracker_thread_
.get());
873 if (!db_tracker_thread_
->BelongsToCurrentThread()) {
874 db_tracker_thread_
->PostTask(
876 base::Bind(&DatabaseTracker::SetForceKeepSessionState
, this));
879 force_keep_session_state_
= true;
882 } // namespace webkit_database