Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / webkit / browser / database / database_tracker.cc
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