diff options
author | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-18 10:02:26 +0000 |
---|---|---|
committer | jochen@chromium.org <jochen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-18 10:02:26 +0000 |
commit | ec3d1456bdbfd7d7806e51dd1e59088f691495ec (patch) | |
tree | 7f85308cde8eab9ffebcb4910894c184060ba15f | |
parent | 4236df757e45014700da962b9a20d46f61b4440f (diff) | |
download | chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.zip chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.tar.gz chromium_src-ec3d1456bdbfd7d7806e51dd1e59088f691495ec.tar.bz2 |
Actually delete databases in CookiesTreeModel.
BUG=34633
TEST=delete a database while it's opened in the renderer
Review URL: http://codereview.chromium.org/600104
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39346 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/file_util.h | 5 | ||||
-rw-r--r-- | base/file_util_posix.cc | 10 | ||||
-rw-r--r-- | base/file_util_unittest.cc | 26 | ||||
-rw-r--r-- | base/file_util_win.cc | 12 | ||||
-rw-r--r-- | base/time.h | 10 | ||||
-rw-r--r-- | base/time_posix.cc | 10 | ||||
-rw-r--r-- | chrome/browser/browsing_data_database_helper.cc | 15 | ||||
-rw-r--r-- | chrome/browser/browsing_data_database_helper.h | 3 | ||||
-rw-r--r-- | chrome/browser/cookies_tree_model.cc | 19 | ||||
-rw-r--r-- | chrome/browser/cookies_tree_model_unittest.cc | 3 | ||||
-rw-r--r-- | chrome/browser/views/options/cookies_view.cc | 10 | ||||
-rw-r--r-- | webkit/database/database_tracker.cc | 103 | ||||
-rw-r--r-- | webkit/database/database_tracker.h | 44 | ||||
-rw-r--r-- | webkit/database/database_tracker_unittest.cc | 106 |
14 files changed, 296 insertions, 80 deletions
diff --git a/base/file_util.h b/base/file_util.h index cdedfc4..a145acc 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -332,6 +332,9 @@ bool GetFileInfo(const FilePath& file_path, FileInfo* info); // Deprecated temporary compatibility function. bool GetFileInfo(const std::wstring& file_path, FileInfo* info); +// Set the time of the last modification. Useful for unit tests. +bool SetLastModifiedTime(const FilePath& file_path, base::Time last_modified); + #if defined(OS_POSIX) // Store inode number of |path| in |inode|. Return true on success. bool GetInode(const FilePath& path, ino_t* inode); diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index fe24259..6e8735a 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include <sys/errno.h> #include <sys/mman.h> #include <sys/stat.h> +#include <sys/time.h> #include <sys/types.h> #include <time.h> #include <unistd.h> @@ -458,6 +459,13 @@ bool GetFileInfo(const FilePath& file_path, FileInfo* results) { return true; } +bool SetLastModifiedTime(const FilePath& file_path, base::Time last_modified) { + struct timeval times[2]; + times[0] = last_modified.ToTimeVal(); + times[1] = last_modified.ToTimeVal(); + return (utimes(file_path.value().c_str(), times) == 0); +} + bool GetInode(const FilePath& path, ino_t* inode) { struct stat buffer; int result = stat(path.value().c_str(), &buffer); diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc index 35233ba..31bb46f 100644 --- a/base/file_util_unittest.cc +++ b/base/file_util_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -1389,4 +1389,28 @@ TEST_F(FileUtilTest, Contains) { #endif } +TEST_F(FileUtilTest, LastModified) { + FilePath data_dir = test_dir_.Append(FILE_PATH_LITERAL("FilePathTest")); + + // Create a fresh, empty copy of this directory. + if (file_util::PathExists(data_dir)) { + ASSERT_TRUE(file_util::Delete(data_dir, true)); + } + ASSERT_TRUE(file_util::CreateDirectory(data_dir)); + + FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); + std::string data("hello"); + ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length())); + + base::Time modification_time; + // Note that this timestamp is divisible by two (seconds) - FAT stores + // modification times with 2s resolution. + ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994, 12:45:26 GMT", + &modification_time)); + ASSERT_TRUE(file_util::SetLastModifiedTime(foobar, modification_time)); + file_util::FileInfo file_info; + ASSERT_TRUE(file_util::GetFileInfo(foobar, &file_info)); + ASSERT_TRUE(file_info.last_modified == modification_time); +} + } // namespace diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 9265ce2..32b6811 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -589,6 +589,16 @@ bool GetFileInfo(const FilePath& file_path, FileInfo* results) { return true; } +bool SetLastModifiedTime(const FilePath& file_path, base::Time last_modified) { + FILETIME timestamp(last_modified.ToFileTime()); + ScopedHandle file_handle( + CreateFile(file_path.value().c_str(), FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + BOOL ret = SetFileTime(file_handle.Get(), NULL, ×tamp, ×tamp); + return ret != 0; +} + FILE* OpenFile(const FilePath& filename, const char* mode) { std::wstring w_mode = ASCIIToWide(std::string(mode)); FILE* file; diff --git a/base/time.h b/base/time.h index 538659d..6e9a8a4 100644 --- a/base/time.h +++ b/base/time.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,6 +26,11 @@ #include "base/basictypes.h" +#if defined(OS_POSIX) +// For struct timeval. +#include <sys/time.h> +#endif + #if defined(OS_WIN) // For FILETIME in FromFileTime, until it moves to a new converter class. // See TODO(iyengar) below. @@ -241,6 +246,9 @@ class Time { static Time FromDoubleT(double dt); double ToDoubleT() const; +#if defined(OS_POSIX) + struct timeval ToTimeVal() const; +#endif #if defined(OS_WIN) static Time FromFileTime(FILETIME ft); diff --git a/base/time_posix.cc b/base/time_posix.cc index af7ee25..60771af 100644 --- a/base/time_posix.cc +++ b/base/time_posix.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -190,4 +190,12 @@ struct timespec TimeDelta::ToTimeSpec() const { return result; } +struct timeval Time::ToTimeVal() const { + struct timeval result; + int64 us = us_ - kTimeTToMicrosecondsOffset; + result.tv_sec = us / Time::kMicrosecondsPerSecond; + result.tv_usec = us % Time::kMicrosecondsPerSecond; + return result; +} + } // namespace base diff --git a/chrome/browser/browsing_data_database_helper.cc b/chrome/browser/browsing_data_database_helper.cc index addbf93..47489a5 100644 --- a/chrome/browser/browsing_data_database_helper.cc +++ b/chrome/browser/browsing_data_database_helper.cc @@ -8,17 +8,16 @@ #include "base/message_loop.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/profile.h" +#include "net/base/net_errors.h" #include "third_party/WebKit/WebKit/chromium/public/WebCString.h" #include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" -#include "webkit/database/database_tracker.h" #include "webkit/glue/webkit_glue.h" BrowsingDataDatabaseHelper::BrowsingDataDatabaseHelper(Profile* profile) - : profile_(profile), + : tracker_(profile->GetDatabaseTracker()), completion_callback_(NULL), is_fetching_(false) { - DCHECK(profile_); } BrowsingDataDatabaseHelper::~BrowsingDataDatabaseHelper() { @@ -52,9 +51,7 @@ void BrowsingDataDatabaseHelper::DeleteDatabase(const std::string& origin, void BrowsingDataDatabaseHelper::FetchDatabaseInfoInFileThread() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); std::vector<webkit_database::OriginInfo> origins_info; - scoped_refptr<webkit_database::DatabaseTracker> tracker = - profile_->GetDatabaseTracker(); - if (tracker.get() && tracker->GetAllOriginsInfo(&origins_info)) { + if (tracker_.get() && tracker_->GetAllOriginsInfo(&origins_info)) { for (std::vector<webkit_database::OriginInfo>::const_iterator ori = origins_info.begin(); ori != origins_info.end(); ++ori) { scoped_ptr<WebKit::WebSecurityOrigin> web_security_origin( @@ -64,7 +61,7 @@ void BrowsingDataDatabaseHelper::FetchDatabaseInfoInFileThread() { ori->GetAllDatabaseNames(&databases); for (std::vector<string16>::const_iterator db = databases.begin(); db != databases.end(); ++db) { - FilePath file_path = tracker->GetFullDBFilePath(ori->GetOrigin(), *db); + FilePath file_path = tracker_->GetFullDBFilePath(ori->GetOrigin(), *db); file_util::FileInfo file_info; if (file_util::GetFileInfo(file_path, &file_info)) { database_info_.push_back(DatabaseInfo( @@ -98,5 +95,7 @@ void BrowsingDataDatabaseHelper::DeleteDatabaseInFileThread( const std::string& origin, const std::string& name) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); - // TODO(jochen): delete the given database. + if (!tracker_.get()) + return; + tracker_->DeleteDatabase(UTF8ToUTF16(origin), UTF8ToUTF16(name), NULL); } diff --git a/chrome/browser/browsing_data_database_helper.h b/chrome/browser/browsing_data_database_helper.h index 0b2440c..d126a54 100644 --- a/chrome/browser/browsing_data_database_helper.h +++ b/chrome/browser/browsing_data_database_helper.h @@ -10,6 +10,7 @@ #include "base/scoped_ptr.h" #include "base/task.h" +#include "webkit/database/database_tracker.h" class Profile; @@ -82,7 +83,7 @@ class BrowsingDataDatabaseHelper void DeleteDatabaseInFileThread(const std::string& origin, const std::string& name); - Profile* profile_; + scoped_refptr<webkit_database::DatabaseTracker> tracker_; // This only mutates on the UI thread. scoped_ptr<Callback1<const std::vector<DatabaseInfo>& >::Type > diff --git a/chrome/browser/cookies_tree_model.cc b/chrome/browser/cookies_tree_model.cc index b6b56b8..0238c12 100644 --- a/chrome/browser/cookies_tree_model.cc +++ b/chrome/browser/cookies_tree_model.cc @@ -129,7 +129,9 @@ class OriginNodeComparator { CookieTreeDatabaseNode::CookieTreeDatabaseNode( BrowsingDataDatabaseHelper::DatabaseInfo* database_info) - : CookieTreeNode(UTF8ToWide(database_info->database_name)), + : CookieTreeNode(UTF8ToWide(database_info->database_name.empty() ? + database_info->origin_identifier : + database_info->database_name)), database_info_(database_info) { } @@ -143,7 +145,10 @@ void CookieTreeDatabaseNode::DeleteStoredObjects() { CookieTreeLocalStorageNode::CookieTreeLocalStorageNode( BrowsingDataLocalStorageHelper::LocalStorageInfo* local_storage_info) - : CookieTreeNode(UTF8ToWide(local_storage_info->origin)), + : CookieTreeNode(UTF8ToWide( + local_storage_info->origin.empty() ? + local_storage_info->database_identifier : + local_storage_info->origin)), local_storage_info_(local_storage_info) { } @@ -213,9 +218,9 @@ CookieTreeLocalStoragesNode* CookieTreeLocalStoragesNode* retval = new CookieTreeLocalStoragesNode; int index = 0; if (cookies_child_) - index++; + ++index; if (databases_child_) - index++; + ++index; GetModel()->Add(this, index, retval); local_storages_child_ = retval; return retval; @@ -442,7 +447,8 @@ void CookiesTreeModel::PopulateDatabaseInfoWithFilter( for (DatabaseInfoList::iterator database_info = database_info_list_.begin(); database_info != database_info_list_.end(); ++database_info) { - std::string origin = database_info->host; + std::string origin = database_info->host.empty() ? + database_info->origin_identifier : database_info->host; if (!filter.size() || (UTF8ToWide(origin).find(filter) != std::wstring::npos)) { CookieTreeOriginNode* origin_node = root->GetOrCreateOriginNode( @@ -469,7 +475,8 @@ void CookiesTreeModel::PopulateLocalStorageInfoWithFilter( local_storage_info_list_.begin(); local_storage_info != local_storage_info_list_.end(); ++local_storage_info) { - std::string origin = local_storage_info->host; + std::string origin = local_storage_info->host.empty() ? + local_storage_info->database_identifier : local_storage_info->host; if (!filter.size() || (UTF8ToWide(origin).find(filter) != std::wstring::npos)) { CookieTreeOriginNode* origin_node = root->GetOrCreateOriginNode( diff --git a/chrome/browser/cookies_tree_model_unittest.cc b/chrome/browser/cookies_tree_model_unittest.cc index 48d280b..925a141 100644 --- a/chrome/browser/cookies_tree_model_unittest.cc +++ b/chrome/browser/cookies_tree_model_unittest.cc @@ -217,7 +217,8 @@ TEST_F(CookiesTreeModelTest, Remove) { EXPECT_STREQ("B,C", GetMonsterCookies(monster).c_str()); EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str()); EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get())); - EXPECT_EQ("origin1,origin2", GetDisplayedLocalStorages(cookies_model.get())); + EXPECT_EQ("origin1,origin2", + GetDisplayedLocalStorages(cookies_model.get())); EXPECT_EQ(16, cookies_model->GetRoot()->GetTotalNodeCount()); } diff --git a/chrome/browser/views/options/cookies_view.cc b/chrome/browser/views/options/cookies_view.cc index e952d51..51833b2 100644 --- a/chrome/browser/views/options/cookies_view.cc +++ b/chrome/browser/views/options/cookies_view.cc @@ -348,11 +348,7 @@ void CookiesView::UpdateRemoveButtonsState() { } void CookiesView::UpdateVisibleDetailedInfo(views::View* view) { - view->SetVisible(true); - if (view != cookie_info_view_) - cookie_info_view_->SetVisible(false); - if (view != database_info_view_) - database_info_view_->SetVisible(false); - if (view != local_storage_info_view_) - local_storage_info_view_->SetVisible(false); + cookie_info_view_->SetVisible(view == cookie_info_view_); + database_info_view_->SetVisible(view == database_info_view_); + local_storage_info_view_->SetVisible(view == local_storage_info_view_); } diff --git a/webkit/database/database_tracker.cc b/webkit/database/database_tracker.cc index f2bb4ae..1d75260 100644 --- a/webkit/database/database_tracker.cc +++ b/webkit/database/database_tracker.cc @@ -36,14 +36,13 @@ DatabaseTracker::DatabaseTracker(const FilePath& profile_path) db_(new sql::Connection()), databases_table_(NULL), meta_table_(NULL), - dbs_deleted_callback_(NULL), default_quota_(5 * 1024 * 1024) { } DatabaseTracker::~DatabaseTracker() { DCHECK(observers_.size() == 0); DCHECK(dbs_to_be_deleted_.empty()); - DCHECK(!dbs_deleted_callback_); + DCHECK(deletion_callbacks_.empty()); } void DatabaseTracker::SetDefaultQuota(int64 quota) { @@ -105,15 +104,32 @@ void DatabaseTracker::DeleteDatabaseIfNeeded(const string16& origin_identifier, DCHECK(!database_connections_.IsDatabaseOpened(origin_identifier, database_name)); if (IsDatabaseScheduledForDeletion(origin_identifier, database_name)) { - DeleteDatabase(origin_identifier, database_name); + DeleteClosedDatabase(origin_identifier, database_name); dbs_to_be_deleted_[origin_identifier].erase(database_name); if (dbs_to_be_deleted_[origin_identifier].empty()) dbs_to_be_deleted_.erase(origin_identifier); - if (dbs_to_be_deleted_.empty()) { - DCHECK(dbs_deleted_callback_); - dbs_deleted_callback_->Run(net::OK); - dbs_deleted_callback_ = NULL; + + std::vector<net::CompletionCallback*> to_be_deleted; + for (PendingCompletionMap::iterator callback = deletion_callbacks_.begin(); + callback != deletion_callbacks_.end(); ++callback) { + DatabaseSet::iterator found_origin = + callback->second.find(origin_identifier); + if (found_origin != callback->second.end()) { + std::set<string16>& databases = found_origin->second; + databases.erase(database_name); + if (databases.empty()) { + callback->second.erase(found_origin); + if (callback->second.empty()) { + net::CompletionCallback* cb = callback->first; + cb->Run(net::OK); + to_be_deleted.push_back(cb); + } + } + } } + for (std::vector<net::CompletionCallback*>::iterator cb = + to_be_deleted.begin(); cb != to_be_deleted.end(); ++cb) + deletion_callbacks_.erase(*cb); } } @@ -185,14 +201,13 @@ void DatabaseTracker::SetOriginQuota(const string16& origin_identifier, } } -bool DatabaseTracker::DeleteDatabase(const string16& origin_identifier, - const string16& database_name) { +bool DatabaseTracker::DeleteClosedDatabase(const string16& origin_identifier, + const string16& database_name) { // Check if the database is opened by any renderer. if (database_connections_.IsDatabaseOpened(origin_identifier, database_name)) return false; // Try to delete the file on the hard drive. - // TODO(jochen): Delete directory if this was the last database. // TODO(jochen): Delete journal files associated with this database. FilePath db_file = GetFullDBFilePath(origin_identifier, database_name); if (file_util::PathExists(db_file) && !file_util::Delete(db_file, false)) @@ -201,6 +216,12 @@ bool DatabaseTracker::DeleteDatabase(const string16& origin_identifier, // Clean up the main database and invalidate the cached record. databases_table_->DeleteDatabaseDetails(origin_identifier, database_name); origins_info_map_.erase(origin_identifier); + + // Try to delete the origin in case this was the last database. + std::vector<DatabaseDetails> details; + if (databases_table_->GetAllDatabaseDetailsForOrigin( + origin_identifier, &details) && details.empty()) + DeleteOrigin(origin_identifier); return true; } @@ -225,8 +246,7 @@ bool DatabaseTracker::DeleteOrigin(const string16& origin_identifier) { bool DatabaseTracker::IsDatabaseScheduledForDeletion( const string16& origin_identifier, const string16& database_name) { - std::map<string16, std::set<string16> >::iterator it = - dbs_to_be_deleted_.find(origin_identifier); + DatabaseSet::iterator it = dbs_to_be_deleted_.find(origin_identifier); if (it == dbs_to_be_deleted_.end()) return false; @@ -388,31 +408,47 @@ void DatabaseTracker::ScheduleDatabaseForDeletion( origin_identifier, database_name)); } +int DatabaseTracker::DeleteDatabase(const string16& origin_identifier, + const string16& database_name, + net::CompletionCallback* callback) { + if (!LazyInit()) + return net::ERR_FAILED; + + DCHECK(!callback || + deletion_callbacks_.find(callback) == deletion_callbacks_.end()); + + if (database_connections_.IsDatabaseOpened(origin_identifier, + database_name)) { + if (callback) + deletion_callbacks_[callback][origin_identifier].insert(database_name); + ScheduleDatabaseForDeletion(origin_identifier, database_name); + return net::ERR_IO_PENDING; + } + DeleteClosedDatabase(origin_identifier, database_name); + return net::OK; +} + int DatabaseTracker::DeleteDataModifiedSince( const base::Time& cutoff, net::CompletionCallback* callback) { - // Check for reentrancy. - if (dbs_deleted_callback_ || !LazyInit()) + if (!LazyInit()) return net::ERR_FAILED; - DCHECK(callback); - dbs_deleted_callback_ = callback; + DCHECK(!callback || + deletion_callbacks_.find(callback) == deletion_callbacks_.end()); + DatabaseSet to_be_deleted; std::vector<string16> origins; - if (!databases_table_->GetAllOrigins(&origins)) { - dbs_deleted_callback_ = NULL; + if (!databases_table_->GetAllOrigins(&origins)) return net::ERR_FAILED; - } int rv = net::OK; for (std::vector<string16>::const_iterator ori = origins.begin(); ori != origins.end(); ++ori) { if (StartsWith(*ori, ASCIIToUTF16(kExtensionOriginIdentifierPrefix), true)) continue; std::vector<DatabaseDetails> details; - if (!databases_table_->GetAllDatabaseDetailsForOrigin(*ori, &details)) { + if (!databases_table_->GetAllDatabaseDetailsForOrigin(*ori, &details)) rv = net::ERR_FAILED; - continue; - } for (std::vector<DatabaseDetails>::const_iterator db = details.begin(); db != details.end(); ++db) { FilePath db_file = GetFullDBFilePath(*ori, db->database_name); @@ -422,18 +458,23 @@ int DatabaseTracker::DeleteDataModifiedSince( continue; // Check if the database is opened by any renderer. - if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) { - ScheduleDatabaseForDeletion(*ori, db->database_name); - rv = net::ERR_IO_PENDING; - } else { - DeleteDatabase(*ori, db->database_name); - } + if (database_connections_.IsDatabaseOpened(*ori, db->database_name)) + to_be_deleted[*ori].insert(db->database_name); + else + DeleteClosedDatabase(*ori, db->database_name); } } - if (rv != net::ERR_IO_PENDING) { - dbs_to_be_deleted_.clear(); - dbs_deleted_callback_ = NULL; + if (!to_be_deleted.empty()) { + if (callback) + deletion_callbacks_[callback] = to_be_deleted; + for (DatabaseSet::iterator ori = to_be_deleted.begin(); + ori != to_be_deleted.end(); ++ori) { + for (std::set<string16>::iterator db = ori->second.begin(); + db != ori->second.end(); ++db) + ScheduleDatabaseForDeletion(ori->first, *db); + } + rv = net::ERR_IO_PENDING; } return rv; } diff --git a/webkit/database/database_tracker.h b/webkit/database/database_tracker.h index a6c7720..139e5fe 100644 --- a/webkit/database/database_tracker.h +++ b/webkit/database/database_tracker.h @@ -41,28 +41,26 @@ class OriginInfo { int64 TotalSize() const { return total_size_; } int64 Quota() const { return quota_; } void GetAllDatabaseNames(std::vector<string16>* databases) const { - for (std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.begin(); it != database_info_.end(); it++) { + for (DatabaseInfoMap::const_iterator it = database_info_.begin(); + it != database_info_.end(); it++) { databases->push_back(it->first); } } int64 GetDatabaseSize(const string16& database_name) const { - std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.find(database_name); + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); if (it != database_info_.end()) return it->second.first; return 0; } string16 GetDatabaseDescription(const string16& database_name) const { - std::map<string16, DatabaseInfo>::const_iterator it = - database_info_.find(database_name); + DatabaseInfoMap::const_iterator it = database_info_.find(database_name); if (it != database_info_.end()) return it->second.second; return string16(); } protected: - typedef std::pair<int64, string16> DatabaseInfo; + typedef std::map<string16, std::pair<int64, string16> > DatabaseInfoMap; OriginInfo(const string16& origin, int64 total_size, int64 quota) : origin_(origin), total_size_(total_size), quota_(quota) { } @@ -70,7 +68,7 @@ class OriginInfo { string16 origin_; int64 total_size_; int64 quota_; - std::map<string16, DatabaseInfo> database_info_; + DatabaseInfoMap database_info_; }; // This class manages the main database, and keeps track of per origin quotas. @@ -101,9 +99,6 @@ class DatabaseTracker explicit DatabaseTracker(const FilePath& profile_path); - // Sets the default quota for all origins. Should be used in tests only. - void SetDefaultQuota(int64 quota); - void DatabaseOpened(const string16& origin_identifier, const string16& database_name, const string16& database_details, @@ -129,16 +124,24 @@ class DatabaseTracker bool GetAllOriginsInfo(std::vector<OriginInfo>* origins_info); void SetOriginQuota(const string16& origin_identifier, int64 new_quota); - bool DeleteDatabase(const string16& origin_identifier, - const string16& database_name); - bool DeleteOrigin(const string16& origin_identifier); + + // Sets the default quota for all origins. Should be used in tests only. + void SetDefaultQuota(int64 quota); + bool IsDatabaseScheduledForDeletion(const string16& origin_identifier, const string16& database_name); + // Deletes a single database. Returns net::OK on success, net::FAILED on + // failure, or net::ERR_IO_PENDING and |callback| is invoked upon completion, + // if non-NULL. + int DeleteDatabase(const string16& origin_identifier, + const string16& database_name, + net::CompletionCallback* callback); + // Delete any databases that have been touched since the cutoff date that's // supplied. Returns net::OK on success, net::FAILED if not all databases // could be deleted, and net::ERR_IO_PENDING and |callback| is invoked upon - // completion. + // completion, if non-NULL. int DeleteDataModifiedSince(const base::Time& cutoff, net::CompletionCallback* callback); @@ -148,6 +151,9 @@ class DatabaseTracker // Need this here to allow RefCountedThreadSafe to call ~DatabaseTracker(). friend class base::RefCountedThreadSafe<DatabaseTracker>; + typedef std::map<string16, std::set<string16> > DatabaseSet; + typedef std::map<net::CompletionCallback*, DatabaseSet> PendingCompletionMap; + class CachedOriginInfo : public OriginInfo { public: CachedOriginInfo() : OriginInfo(string16(), 0, 0) {} @@ -169,6 +175,10 @@ class DatabaseTracker ~DatabaseTracker(); + bool DeleteClosedDatabase(const string16& origin_identifier, + const string16& database_name); + bool DeleteOrigin(const string16& origin_identifier); + bool LazyInit(); bool UpgradeToCurrentVersion(); void InsertOrUpdateDatabaseDetails(const string16& origin_identifier, @@ -200,8 +210,8 @@ class DatabaseTracker DatabaseConnections database_connections_; // The set of databases that should be deleted but are still opened - std::map<string16, std::set<string16> > dbs_to_be_deleted_; - net::CompletionCallback* dbs_deleted_callback_; + DatabaseSet dbs_to_be_deleted_; + PendingCompletionMap deletion_callbacks_; // Default quota for all origins; changed only by tests int64 default_quota_; diff --git a/webkit/database/database_tracker_unittest.cc b/webkit/database/database_tracker_unittest.cc index 0ed707d..55d13e1 100644 --- a/webkit/database/database_tracker_unittest.cc +++ b/webkit/database/database_tracker_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,8 @@ #include "base/scoped_ptr.h" #include "base/scoped_temp_dir.h" #include "base/string_util.h" +#include "base/time.h" +#include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/database/database_tracker.h" @@ -28,6 +30,9 @@ class TestObserver : public webkit_database::DatabaseTracker::Observer { } virtual void OnDatabaseScheduledForDeletion(const string16& origin_identifier, const string16& database_name) { + new_notification_received_ = true; + origin_identifier_ = origin_identifier; + database_name_ = database_name; } bool DidReceiveNewNotification() { bool temp_new_notification_received = new_notification_received_; @@ -67,6 +72,101 @@ void CheckNotificationReceived(TestObserver* observer, namespace webkit_database { +TEST(DatabaseTrackerTest, DeleteOpenDatabase) { + // Initialize the tracker database. + ScopedTempDir temp_dir; + EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); + scoped_refptr<DatabaseTracker> tracker(new DatabaseTracker(temp_dir.path())); + + // Create and open three databases. + int64 database_size = 0; + int64 space_available = 0; + const string16 kOrigin1 = ASCIIToUTF16("origin1"); + const string16 kOrigin2 = ASCIIToUTF16("origin2"); + const string16 kDB1 = ASCIIToUTF16("db1"); + const string16 kDB2 = ASCIIToUTF16("db2"); + const string16 kDB3 = ASCIIToUTF16("db3"); + const string16 kDescription = ASCIIToUTF16("database_description"); + + tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, + &database_size, &space_available); + tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0, + &database_size, &space_available); + tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0, + &database_size, &space_available); + + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin2))))); + EXPECT_EQ(1, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); + EXPECT_EQ(2, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2)); + EXPECT_EQ(3, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin2, kDB3), "aaa", 3)); + tracker->DatabaseModified(kOrigin1, kDB1); + tracker->DatabaseModified(kOrigin2, kDB2); + tracker->DatabaseModified(kOrigin2, kDB3); + + // Delete db1. Should also delete origin1. + TestObserver observer; + tracker->AddObserver(&observer); + TestCompletionCallback callback; + int result = tracker->DeleteDatabase(kOrigin1, kDB1, &callback); + EXPECT_EQ(net::ERR_IO_PENDING, result); + ASSERT_FALSE(callback.have_result()); + EXPECT_TRUE(observer.DidReceiveNewNotification()); + EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier()); + EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName()); + tracker->DatabaseClosed(kOrigin1, kDB1); + result = callback.GetResult(result); + EXPECT_EQ(net::OK, result); + EXPECT_FALSE(file_util::PathExists(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + + // Recreate db1. + tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, + &database_size, &space_available); + EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_EQ(1, file_util::WriteFile( + tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1)); + tracker->DatabaseModified(kOrigin1, kDB1); + + // Setup file modification times. db1 and db2 are modified now, db3 three + // days ago. + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin1, kDB1), base::Time::Now())); + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin2, kDB2), base::Time::Now())); + base::Time three_days_ago = base::Time::Now(); + three_days_ago -= base::TimeDelta::FromDays(3); + EXPECT_TRUE(file_util::SetLastModifiedTime( + tracker->GetFullDBFilePath(kOrigin2, kDB3), three_days_ago)); + + // Delete databases modified since yesterday. + base::Time yesterday = base::Time::Now(); + yesterday -= base::TimeDelta::FromDays(1); + result = tracker->DeleteDataModifiedSince(yesterday, &callback); + EXPECT_EQ(net::ERR_IO_PENDING, result); + ASSERT_FALSE(callback.have_result()); + EXPECT_TRUE(observer.DidReceiveNewNotification()); + tracker->DatabaseClosed(kOrigin1, kDB1); + tracker->DatabaseClosed(kOrigin2, kDB2); + result = callback.GetResult(result); + EXPECT_EQ(net::OK, result); + EXPECT_FALSE(file_util::PathExists(tracker->DatabaseDirectory().Append( + FilePath::FromWStringHack(UTF16ToWide(kOrigin1))))); + EXPECT_FALSE( + file_util::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2))); + EXPECT_TRUE( + file_util::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3))); + + tracker->DatabaseClosed(kOrigin2, kDB3); + tracker->RemoveObserver(&observer); +} + TEST(DatabaseTrackerTest, TestIt) { // Initialize the tracker database. ScopedTempDir temp_dir; @@ -210,14 +310,14 @@ TEST(DatabaseTrackerTest, TestIt) { // Trying to delete a database in use should fail tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0, &database_size, &space_available); - EXPECT_FALSE(tracker->DeleteDatabase(kOrigin1, kDB3)); + EXPECT_FALSE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); origin1_info = tracker->GetCachedOriginInfo(kOrigin1); EXPECT_TRUE(origin1_info); EXPECT_EQ(6, origin1_info->GetDatabaseSize(kDB3)); tracker->DatabaseClosed(kOrigin1, kDB3); // Delete a database and make sure the space used by that origin is updated - EXPECT_TRUE(tracker->DeleteDatabase(kOrigin1, kDB3)); + EXPECT_TRUE(tracker->DeleteClosedDatabase(kOrigin1, kDB3)); origin1_info = tracker->GetCachedOriginInfo(kOrigin1); EXPECT_TRUE(origin1_info); EXPECT_EQ(origin1_quota - 5, tracker->GetOriginSpaceAvailable(kOrigin1)); |