diff options
author | meelapshah@chromium.org <meelapshah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-09 17:30:49 +0000 |
---|---|---|
committer | meelapshah@chromium.org <meelapshah@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-09 17:30:49 +0000 |
commit | 72d0fc7358e46f8646ebb14e3ea882618c260039 (patch) | |
tree | fdce419abfd8903d8d002bb74185f7ac36dbd262 | |
parent | 47c2188f2fc336b6040d6047afca32eb8e9779d8 (diff) | |
download | chromium_src-72d0fc7358e46f8646ebb14e3ea882618c260039.zip chromium_src-72d0fc7358e46f8646ebb14e3ea882618c260039.tar.gz chromium_src-72d0fc7358e46f8646ebb14e3ea882618c260039.tar.bz2 |
Change ThumbnailStore to use sqlite instead of individual files try 2. Previous try failed interactive_ui_tests.
Review URL: http://codereview.chromium.org/149361
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20272 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/dom_ui/dom_ui_thumbnail_source.cc | 4 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui_thumbnail_source.h | 3 | ||||
-rw-r--r-- | chrome/browser/profile.cc | 2 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.cc | 4 | ||||
-rw-r--r-- | chrome/browser/thumbnail_store.cc | 290 | ||||
-rw-r--r-- | chrome/browser/thumbnail_store.h | 78 | ||||
-rw-r--r-- | chrome/browser/thumbnail_store_unittest.cc | 124 |
7 files changed, 226 insertions, 279 deletions
diff --git a/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc b/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc index fd40e867..5a10c7c 100644 --- a/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc +++ b/chrome/browser/dom_ui/dom_ui_thumbnail_source.cc @@ -17,14 +17,14 @@ DOMUIThumbnailSource::DOMUIThumbnailSource(Profile* profile) : DataSource(chrome::kChromeUIThumbnailPath, MessageLoop::current()), - profile_(profile), - store_(profile->GetThumbnailStore()) { + profile_(profile) { } void DOMUIThumbnailSource::StartDataRequest(const std::string& path, int request_id) { if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kThumbnailStore)) { RefCountedBytes* data = NULL; + scoped_refptr<ThumbnailStore> store_ = profile_->GetThumbnailStore(); if (store_->GetPageThumbnail(GURL(path), &data)) { // Got the thumbnail. diff --git a/chrome/browser/dom_ui/dom_ui_thumbnail_source.h b/chrome/browser/dom_ui/dom_ui_thumbnail_source.h index 2d475af..c94967b 100644 --- a/chrome/browser/dom_ui/dom_ui_thumbnail_source.h +++ b/chrome/browser/dom_ui/dom_ui_thumbnail_source.h @@ -40,9 +40,6 @@ class DOMUIThumbnailSource : public ChromeURLDataManager::DataSource { Profile* profile_; CancelableRequestConsumerT<int, 0> cancelable_consumer_; - // The ThumbnailStore from which thumbnails are requested. - scoped_refptr<ThumbnailStore> store_; - // Raw PNG representation of the thumbnail to show when the thumbnail // database doesn't have a thumbnail for a webpage. scoped_refptr<RefCountedBytes> default_thumbnail_; diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 6e0a5a4..085f814 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -1016,7 +1016,7 @@ TabRestoreService* ProfileImpl::GetTabRestoreService() { ThumbnailStore* ProfileImpl::GetThumbnailStore() { if (!thumbnail_store_.get()) { thumbnail_store_ = new ThumbnailStore; - thumbnail_store_->Init(GetPath().AppendASCII("thumbnailstore"), this); + thumbnail_store_->Init(GetPath().AppendASCII("Top Thumbnails"), this); } return thumbnail_store_.get(); } diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index b417f34..cddddfb 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -1878,8 +1878,8 @@ void TabContents::UpdateThumbnail(const GURL& url, const ThumbnailScore& score) { // Tell History about this thumbnail if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kThumbnailStore)) { - profile()->GetThumbnailStore()-> - SetPageThumbnail(url, bitmap, score, !profile()->IsOffTheRecord()); + if (!profile()->IsOffTheRecord()) + profile()->GetThumbnailStore()->SetPageThumbnail(url, bitmap, score); } else { HistoryService* hs; if (!profile()->IsOffTheRecord() && diff --git a/chrome/browser/thumbnail_store.cc b/chrome/browser/thumbnail_store.cc index d5679c9..315f12f 100644 --- a/chrome/browser/thumbnail_store.cc +++ b/chrome/browser/thumbnail_store.cc @@ -8,7 +8,6 @@ #include <algorithm> #include "base/basictypes.h" -#include "base/pickle.h" #include "base/file_util.h" #include "base/gfx/jpeg_codec.h" #include "base/md5.h" @@ -18,51 +17,57 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/profile.h" #include "chrome/common/pref_service.h" -#include "chrome/common/thumbnail_score.h" +#include "chrome/common/sqlite_utils.h" #include "googleurl/src/gurl.h" #include "third_party/skia/include/core/SkBitmap.h" ThumbnailStore::ThumbnailStore() : cache_(NULL), - cache_initialized_(false), + db_(NULL), hs_(NULL), url_blacklist_(NULL) { } ThumbnailStore::~ThumbnailStore() { + CommitCacheToDB(NULL); } -void ThumbnailStore::Init(const FilePath& file_path, Profile* profile) { - file_path_ = file_path; +void ThumbnailStore::Init(const FilePath& db_name, + Profile* profile) { + // Load thumbnails already in the database. + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &ThumbnailStore::InitializeFromDB, + db_name, MessageLoop::current())); + + // Take ownership of a reference to the HistoryService. hs_ = profile->GetHistoryService(Profile::EXPLICIT_ACCESS); - url_blacklist_ = profile->GetPrefs()-> - GetMutableDictionary(prefs::kNTPMostVisitedURLsBlacklist); - g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(this, &ThumbnailStore::GetAllThumbnailsFromDisk, - file_path_, MessageLoop::current())); + // Store a pointer to a persistent table of blacklisted URLs. + url_blacklist_ = profile->GetPrefs()-> + GetMutableDictionary(prefs::kNTPMostVisitedURLsBlacklist); + // Get the list of most visited URLs and redirect information from the + // HistoryService. timer_.Start(base::TimeDelta::FromMinutes(30), this, - &ThumbnailStore::UpdateURLData); + &ThumbnailStore::UpdateURLData); UpdateURLData(); } bool ThumbnailStore::SetPageThumbnail(const GURL& url, const SkBitmap& thumbnail, - const ThumbnailScore& score, - bool write_to_disk) { - if (!cache_initialized_) + const ThumbnailScore& score) { + if (!cache_.get()) return false; if (!ShouldStoreThumbnailForURL(url) || (cache_->find(url) != cache_->end() && - !ShouldReplaceThumbnailWith((*cache_)[url].second, score))) + !ShouldReplaceThumbnailWith((*cache_)[url].score_, score))) return true; base::TimeTicks encode_start = base::TimeTicks::Now(); - // Encode the SkBitmap to jpeg and add to cache. + // Encode the SkBitmap to jpeg. scoped_refptr<RefCountedBytes> jpeg_data = new RefCountedBytes; SkAutoLockPixels thumbnail_lock(thumbnail); bool encoded = JPEGCodec::Encode( @@ -79,38 +84,31 @@ bool ThumbnailStore::SetPageThumbnail(const GURL& url, return false; // Update the cache_ with the new thumbnail. - (*cache_)[url] = std::make_pair(jpeg_data, score); + (*cache_)[url] = CacheEntry(jpeg_data, score, true); - // Write the new thumbnail data to disk in the background on file_thread. - if (write_to_disk) { - g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(this, &ThumbnailStore::WriteThumbnailToDisk, url, - jpeg_data, score)); - } return true; } bool ThumbnailStore::GetPageThumbnail( const GURL& url, RefCountedBytes** data) { - if (!cache_initialized_ || IsURLBlacklisted(url)) + if (!cache_.get() || IsURLBlacklisted(url)) return false; // Look up the |url| in the redirect list to find the final destination // which is the key into the |cache_|. history::RedirectMap::iterator it = redirect_urls_->find(url); - if (it == redirect_urls_->end()) - return false; - - // Return the first available thumbnail starting at the end of the - // redirect list. - history::RedirectList::reverse_iterator rit; - for (rit = it->second->data.rbegin(); - rit != it->second->data.rend(); ++rit) { - if (cache_->find(*rit) != cache_->end()) { - *data = (*cache_)[*rit].first; - (*data)->AddRef(); - return true; + if (it != redirect_urls_->end()) { + // Return the first available thumbnail starting at the end of the + // redirect list. + history::RedirectList::reverse_iterator rit; + for (rit = it->second->data.rbegin(); + rit != it->second->data.rend(); ++rit) { + if (cache_->find(*rit) != cache_->end()) { + *data = (*cache_)[*rit].data_.get(); + (*data)->AddRef(); + return true; + } } } @@ -119,7 +117,7 @@ bool ThumbnailStore::GetPageThumbnail( if (cache_->find(url) == cache_->end()) return false; - *data = (*cache_)[url].first; + *data = (*cache_)[url].data_.get(); (*data)->AddRef(); return true; } @@ -141,14 +139,14 @@ void ThumbnailStore::OnURLDataAvailable(std::vector<GURL>* urls, } void ThumbnailStore::CleanCacheData() { - if (!cache_initialized_) + if (!cache_.get()) return; // For each URL in the cache, search the RedirectMap for the originating URL. // If this URL is blacklisted or not in the most visited list, delete the // thumbnail data for it from the cache and from disk in the background. scoped_refptr<RefCountedVector<GURL> > old_urls = new RefCountedVector<GURL>; - for (ThumbnailStore::Cache::iterator cache_it = cache_->begin(); + for (Cache::iterator cache_it = cache_->begin(); cache_it != cache_->end();) { const GURL* url = NULL; for (history::RedirectMap::iterator it = redirect_urls_->begin(); @@ -171,145 +169,115 @@ void ThumbnailStore::CleanCacheData() { if (old_urls->data.size()) { g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, - NewRunnableMethod(this, &ThumbnailStore::DeleteThumbnails, old_urls)); + NewRunnableMethod(this, &ThumbnailStore::CommitCacheToDB, old_urls)); } } -void ThumbnailStore::DeleteThumbnails( - scoped_refptr<RefCountedVector<GURL> > thumbnail_urls) const { - for (std::vector<GURL>::iterator it = thumbnail_urls->data.begin(); - it != thumbnail_urls->data.end(); ++it) - file_util::Delete(file_path_.AppendASCII(MD5String(it->spec())), false); -} - -void ThumbnailStore::GetAllThumbnailsFromDisk(FilePath filepath, - MessageLoop* cb_loop) { - ThumbnailStore::Cache* cache = new ThumbnailStore::Cache; +void ThumbnailStore::CommitCacheToDB( + scoped_refptr<RefCountedVector<GURL> > stale_urls) const { + if (!db_) + return; - // Create the specified directory if it does not exist. - if (!file_util::DirectoryExists(filepath)) { - file_util::CreateDirectory(filepath); - } else { - // Walk the directory and read the thumbnail data from disk. - FilePath path; - GURL url; - RefCountedBytes* data; - ThumbnailScore score; - file_util::FileEnumerator fenum(filepath, false, - file_util::FileEnumerator::FILES); - - while (!(path = fenum.Next()).empty()) { - data = new RefCountedBytes; - if (GetPageThumbnailFromDisk(path, &url, data, &score)) - (*cache)[url] = std::make_pair(data, score); - else - delete data; + // Delete old thumbnails. + if (stale_urls.get()) { + for (std::vector<GURL>::iterator it = stale_urls->data.begin(); + it != stale_urls->data.end(); ++it) { + SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, + "DELETE FROM thumbnails WHERE url=?"); + statement->bind_string(0, it->spec()); + if (statement->step() != SQLITE_DONE) + NOTREACHED(); } } - cb_loop->PostTask(FROM_HERE, - NewRunnableMethod(this, &ThumbnailStore::OnDiskDataAvailable, cache)); -} -bool ThumbnailStore::GetPageThumbnailFromDisk(const FilePath& file, - GURL* url, - RefCountedBytes* data, - ThumbnailScore* score) const { - int64 file_size; - if (!file_util::GetFileSize(file, &file_size)) - return false; + // Update cached thumbnails. + for (Cache::iterator it = cache_->begin(); it != cache_->end(); ++it) { + if (!it->second.dirty_) + continue; + + SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, + "INSERT OR REPLACE INTO thumbnails " + "(url, boring_score, good_clipping, at_top, time_taken, data) " + "VALUES (?,?,?,?,?,?)"); + statement->bind_string(0, it->first.spec()); + statement->bind_double(1, it->second.score_.boring_score); + statement->bind_bool(2, it->second.score_.good_clipping); + statement->bind_bool(3, it->second.score_.at_top); + statement->bind_int64(4, it->second.score_.time_at_snapshot. + ToInternalValue()); + statement->bind_blob(5, &it->second.data_->data[0], + static_cast<int>(it->second.data_->data.size())); + if (statement->step() != SQLITE_DONE) + DLOG(WARNING) << "Unable to insert thumbnail for URL"; + else + it->second.dirty_ = false; + } +} - // Read the file into a buffer. - std::vector<char> file_data; - file_data.resize(static_cast<unsigned int>(file_size)); - if (file_util::ReadFile(file, &file_data[0], - static_cast<int>(file_size)) == -1) - return false; +void ThumbnailStore::InitializeFromDB(const FilePath& db_name, + MessageLoop* cb_loop) { + if (OpenSqliteDb(db_name, &db_) != SQLITE_OK) + return; - // Unpack the url, ThumbnailScore and JPEG size from the buffer. - std::string url_string; - unsigned int jpeg_len; - void* iter = NULL; - Pickle packed(&file_data[0], static_cast<int>(file_size)); + // Use a large page size since the thumbnails we are storing are typically + // large, a small cache size since we cache in memory and don't go to disk + // often, and take exclusive access since nobody else uses this db. + sqlite3_exec(db_, "PRAGMA page_size=4096 " + "PRAGMA cache_size=64 " + "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL, NULL); + + statement_cache_ = new SqliteStatementCache; + + // Use local DBCloseScoper so that if we cannot create the table and + // need to return, the |db_| and |statement_cache_| are closed properly. + history::DBCloseScoper scoper(&db_, &statement_cache_); + + if (!DoesSqliteTableExist(db_, "thumbnails")) { + if (sqlite3_exec(db_, "CREATE TABLE thumbnails (" + "url LONGVARCHAR PRIMARY KEY," + "boring_score DOUBLE DEFAULT 1.0," + "good_clipping INTEGER DEFAULT 0," + "at_top INTEGER DEFAULT 0," + "time_taken INTEGER DEFAULT 0," + "data BLOB)", NULL, NULL, NULL) != SQLITE_OK) + return; + } - if (!packed.ReadString(&iter, &url_string) || - !UnpackScore(score, packed, iter) || - !packed.ReadUInt32(&iter, &jpeg_len)) - return false; + statement_cache_->set_db(db_); - // Store the url to the out parameter. - GURL temp_url(url_string); - url->Swap(&temp_url); + // Now we can use a DBCloseScoper at the object scope. + scoper.Detach(); + close_scoper_.Attach(&db_, &statement_cache_); - // Unpack the JPEG data from the buffer. - const char* jpeg_data = NULL; - int out_len; + if (cb_loop) + GetAllThumbnailsFromDisk(cb_loop); +} - if (!packed.ReadData(&iter, &jpeg_data, &out_len) || - out_len != static_cast<int>(jpeg_len)) - return false; +void ThumbnailStore::GetAllThumbnailsFromDisk(MessageLoop* cb_loop) { + ThumbnailStore::Cache* cache = new ThumbnailStore::Cache; - // Copy jpeg data to the out parameter. - data->data.resize(jpeg_len); - memcpy(&data->data[0], jpeg_data, jpeg_len); + SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, + "SELECT * FROM thumbnails"); + + while (statement->step() == SQLITE_ROW) { + GURL url(statement->column_string(0)); + ThumbnailScore score(statement->column_double(1), // Boring score + statement->column_bool(2), // Good clipping + statement->column_bool(3), // At top + base::Time::FromInternalValue( + statement->column_int64(4))); // Time taken + scoped_refptr<RefCountedBytes> data = new RefCountedBytes; + if (statement->column_blob_as_vector(5, &data->data)) + (*cache)[url] = CacheEntry(data, score, false); + } - return true; + cb_loop->PostTask(FROM_HERE, + NewRunnableMethod(this, &ThumbnailStore::OnDiskDataAvailable, cache)); } void ThumbnailStore::OnDiskDataAvailable(ThumbnailStore::Cache* cache) { - if (cache) { + if (cache) cache_.reset(cache); - cache_initialized_ = true; - } -} - -bool ThumbnailStore::WriteThumbnailToDisk(const GURL& url, - scoped_refptr<RefCountedBytes> data, - const ThumbnailScore& score) const { - Pickle packed; - FilePath file = file_path_.AppendASCII(MD5String(url.spec())); - - // Pack the url, ThumbnailScore, and the JPEG data. - packed.WriteString(url.spec()); - PackScore(score, &packed); - packed.WriteUInt32(data->data.size()); - packed.WriteData(reinterpret_cast<char*>(&data->data[0]), data->data.size()); - - // Write the packed data to a file. - file_util::Delete(file, false); - return file_util::WriteFile(file, - reinterpret_cast<const char*>(packed.data()), - packed.size()) != -1; -} - -void ThumbnailStore::PackScore(const ThumbnailScore& score, - Pickle* packed) const { - // Pack the contents of the given ThumbnailScore into the given Pickle. - packed->WriteData(reinterpret_cast<const char*>(&score.boring_score), - sizeof(score.boring_score)); - packed->WriteBool(score.at_top); - packed->WriteBool(score.good_clipping); - packed->WriteInt64(score.time_at_snapshot.ToInternalValue()); -} - -bool ThumbnailStore::UnpackScore(ThumbnailScore* score, const Pickle& packed, - void*& iter) const { - // Unpack a ThumbnailScore from the given Pickle and iterator. - const char* boring = NULL; - int out_len; - int64 us; - - if (!packed.ReadData(&iter, &boring, &out_len) || - !packed.ReadBool(&iter, &score->at_top) || - !packed.ReadBool(&iter, &score->good_clipping) || - !packed.ReadInt64(&iter, &us)) - return false; - - if (out_len != sizeof(score->boring_score)) - return false; - - memcpy(&score->boring_score, boring, sizeof(score->boring_score)); - score->time_at_snapshot = base::Time::FromInternalValue(us); - return true; } bool ThumbnailStore::ShouldStoreThumbnailForURL(const GURL& url) const { @@ -333,6 +301,6 @@ std::wstring ThumbnailStore::GetDictionaryKeyForURL( bool ThumbnailStore::IsPopular(const GURL& url) const { return most_visited_urls_->end() != find(most_visited_urls_->begin(), - most_visited_urls_->end(), - url); + most_visited_urls_->end(), + url); } diff --git a/chrome/browser/thumbnail_store.h b/chrome/browser/thumbnail_store.h index cf29e43..0ec81f4 100644 --- a/chrome/browser/thumbnail_store.h +++ b/chrome/browser/thumbnail_store.h @@ -15,17 +15,19 @@ #include "base/timer.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/history/history.h" +#include "chrome/browser/history/url_database.h" // For DBCloseScoper #include "chrome/common/pref_names.h" #include "chrome/common/ref_counted_util.h" +#include "chrome/common/sqlite_compiled_statement.h" +#include "chrome/common/thumbnail_score.h" #include "testing/gtest/include/gtest/gtest_prod.h" class DictionaryValue; class GURL; class HistoryService; -class Pickle; class Profile; class SkBitmap; -struct ThumbnailScore; +struct sqlite3; namespace base { class Time; } @@ -38,17 +40,13 @@ class ThumbnailStore : public base::RefCountedThreadSafe<ThumbnailStore> { ~ThumbnailStore(); // Must be called after creation but before other methods are called. - // file_path is a directory where a new database should be created - // or the location of an existing databse. - void Init(const FilePath& file_path, Profile* profile); + void Init(const FilePath& db_name, // The location of the database. + Profile* profile); // To get to the HistoryService. // Stores the given thumbnail and score with the associated url in the cache. - // If write_to_disk is true, the thumbnail data is written to disk on the - // file_thread. bool SetPageThumbnail(const GURL& url, const SkBitmap& thumbnail, - const ThumbnailScore& score, - bool write_to_disk); + const ThumbnailScore& score); // Sets *data to point to the thumbnail for the given url. // Returns false if no thumbnail available. @@ -61,9 +59,22 @@ class ThumbnailStore : public base::RefCountedThreadSafe<ThumbnailStore> { FRIEND_TEST(ThumbnailStoreTest, FollowRedirects); friend class ThumbnailStoreTest; + struct CacheEntry { + scoped_refptr<RefCountedBytes> data_; + ThumbnailScore score_; + bool dirty_; + + CacheEntry() : data_(NULL), score_(ThumbnailScore()), dirty_(false) {} + CacheEntry(RefCountedBytes* data, + const ThumbnailScore& score, + bool dirty) + : data_(data), + score_(score), + dirty_(dirty) {} + }; + // Data structure used to store thumbnail data in memory. - typedef std::map<GURL, std::pair<scoped_refptr<RefCountedBytes>, - ThumbnailScore> > Cache; + typedef std::map<GURL, CacheEntry> Cache; // Most visited URLs and their redirect lists ------------------------------- @@ -72,8 +83,7 @@ class ThumbnailStore : public base::RefCountedThreadSafe<ThumbnailStore> { // callback is OnURLDataAvailable. void UpdateURLData(); - // The callback for UpdateURLData. The ThumbnailStore takes ownership of - // the most visited urls list and redirect lists passed in. + // The callback for UpdateURLData. void OnURLDataAvailable(std::vector<GURL>* urls, history::RedirectMap* redirects); @@ -84,43 +94,26 @@ class ThumbnailStore : public base::RefCountedThreadSafe<ThumbnailStore> { // visited sites. void CleanCacheData(); - // Deletes thumbnail data from disk for the given list of urls. - void DeleteThumbnails( - scoped_refptr<RefCountedVector<GURL> > thumbnail_urls) const; - // Disk operations ---------------------------------------------------------- + // Initialize |db_| to the database specified in |db_name|. If |cb_loop| + // is non-null, calls GetAllThumbnailsFromDisk. Done on the file_thread. + void InitializeFromDB(const FilePath& db_name, MessageLoop* cb_loop); + // Read all thumbnail data from the specified FilePath into a Cache object. // Done on the file_thread and returns to OnDiskDataAvailable on the thread // owning the specified MessageLoop. - void GetAllThumbnailsFromDisk(FilePath filepath, MessageLoop* cb_loop); - - // Read the thumbnail data from the given file and stores it in the - // out parameters GURL, SkBitmap, and ThumbnailScore. - bool GetPageThumbnailFromDisk(const FilePath& file, - GURL* url, - RefCountedBytes* data, - ThumbnailScore* score) const; + void GetAllThumbnailsFromDisk(MessageLoop* cb_loop); // Once thumbnail data from the disk is available from the file_thread, // this function is invoked on the main thread. It takes ownership of the // Cache* passed in and retains this Cache* for the lifetime of the object. void OnDiskDataAvailable(Cache* cache); - // Write thumbnail data to disk for a given url. - bool WriteThumbnailToDisk(const GURL& url, - scoped_refptr<RefCountedBytes> data, - const ThumbnailScore& score) const; - - - // Pack the given ThumbnailScore into the given Pickle. - void PackScore(const ThumbnailScore& score, Pickle* packed) const; - - // Unpack a ThumbnailScore from a given Pickle and associated iterator. - // Returns false is a ThumbnailScore could not be unpacked. - bool UnpackScore(ThumbnailScore* score, - const Pickle& packed, - void*& iter) const; + // Delete each URL in the given vector from the DB and write all dirty + // cache entries to the DB. + void CommitCacheToDB( + scoped_refptr<RefCountedVector<GURL> > stale_urls) const; // Decide whether to store data --------------------------------------------- @@ -139,10 +132,11 @@ class ThumbnailStore : public base::RefCountedThreadSafe<ThumbnailStore> { // The Cache maintained by the object. scoped_ptr<Cache> cache_; - bool cache_initialized_; - // The location of the thumbnail store. - FilePath file_path_; + // The database holding the thumbnails on disk. + sqlite3* db_; + SqliteStatementCache* statement_cache_; + history::DBCloseScoper close_scoper_; // We hold a reference to the history service to query for most visited URLs // and redirect information. diff --git a/chrome/browser/thumbnail_store_unittest.cc b/chrome/browser/thumbnail_store_unittest.cc index 72a5013..8d3cb8a 100644 --- a/chrome/browser/thumbnail_store_unittest.cc +++ b/chrome/browser/thumbnail_store_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <string.h> +#include <algorithm> #include <iostream> #include <vector> @@ -12,12 +13,13 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/gfx/jpeg_codec.h" -#include "base/md5.h" #include "base/path_service.h" #include "base/ref_counted.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/ref_counted_util.h" #include "chrome/common/thumbnail_score.h" +#include "chrome/common/sqlite_compiled_statement.h" +#include "chrome/common/sqlite_utils.h" #include "chrome/tools/profiles/thumbnail-inl.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,9 +32,10 @@ inline unsigned int diff(unsigned int a, unsigned int b) { class ThumbnailStoreTest : public testing::Test { public: - ThumbnailStoreTest() : score1_(.5, true, false), - url1_("http://www.google.com/"), url2_("http://www.elgoog.com") { + ThumbnailStoreTest() : score_(.5, true, false), + url_("http://www.google.com/") { } + ~ThumbnailStoreTest() { } @@ -40,33 +43,32 @@ class ThumbnailStoreTest : public testing::Test { void SetUp(); void TearDown() { - file_util::Delete(file_path_.AppendASCII(url1_.host()), false); - file_util::Delete(file_path_.AppendASCII(url2_.host()), false); + file_util::Delete(db_name_, false); } // Compute the max difference over all pixels for each RGBA component. void PrintPixelDiff(SkBitmap* image_a, SkBitmap* image_b); // The directory where ThumbnailStore will store data. - FilePath file_path_; + FilePath db_name_; scoped_refptr<ThumbnailStore> store_; scoped_ptr<SkBitmap> google_; scoped_ptr<SkBitmap> weewar_; scoped_refptr<RefCountedBytes> jpeg_google_; scoped_refptr<RefCountedBytes> jpeg_weewar_; - ThumbnailScore score1_; - GURL url1_, url2_; + ThumbnailScore score_; + GURL url_; base::Time time_; }; void ThumbnailStoreTest::SetUp() { - if (!file_util::GetTempDir(&file_path_)) + if (!file_util::GetTempDir(&db_name_)) FAIL(); // Delete any old thumbnail files if they exist. - file_util::Delete(file_path_.AppendASCII(url1_.host()), false); - file_util::Delete(file_path_.AppendASCII(url2_.host()), false); + db_name_ = db_name_.AppendASCII("ThumbnailDB"); + file_util::Delete(db_name_, false); google_.reset(JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail))); weewar_.reset(JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail))); @@ -90,12 +92,12 @@ void ThumbnailStoreTest::SetUp() { &(jpeg_weewar_->data)); store_ = new ThumbnailStore; - store_->cache_initialized_ = true; - store_->file_path_ = file_path_; - store_->most_visited_urls_.reset(new std::vector<GURL>); - store_->most_visited_urls_->push_back(url1_); + store_->cache_.reset(new ThumbnailStore::Cache); store_->redirect_urls_.reset(new history::RedirectMap); + + store_->most_visited_urls_.reset(new std::vector<GURL>); + store_->most_visited_urls_->push_back(url_); } void ThumbnailStoreTest::PrintPixelDiff(SkBitmap* image_a, SkBitmap* image_b) { @@ -139,21 +141,14 @@ void ThumbnailStoreTest::PrintPixelDiff(SkBitmap* image_a, SkBitmap* image_b) { TEST_F(ThumbnailStoreTest, UpdateThumbnail) { RefCountedBytes* read_image = NULL; ThumbnailScore score2(0.1, true, true); - store_->cache_->clear(); - store_->redirect_urls_->clear(); // store_ google_ with a low score, then weewar_ with a higher score // and check that weewar_ overwrote google_. - EXPECT_TRUE(store_->SetPageThumbnail(url1_, *google_, score1_, false)); - EXPECT_TRUE(store_->SetPageThumbnail(url1_, *weewar_, score2, false)); - - // Set fake redirects list. - scoped_ptr<std::vector<GURL> > redirects(new std::vector<GURL>); - redirects->push_back(url1_); - (*store_->redirect_urls_)[url1_] = new RefCountedVector<GURL>(*redirects); + EXPECT_TRUE(store_->SetPageThumbnail(url_, *google_, score_)); + EXPECT_TRUE(store_->SetPageThumbnail(url_, *weewar_, score2)); - EXPECT_TRUE(store_->GetPageThumbnail(url1_, &read_image)); + EXPECT_TRUE(store_->GetPageThumbnail(url_, &read_image)); EXPECT_EQ(read_image->data.size(), jpeg_weewar_->data.size()); EXPECT_EQ(0, memcmp(&read_image->data[0], &jpeg_weewar_->data[0], jpeg_weewar_->data.size())); @@ -163,24 +158,16 @@ TEST_F(ThumbnailStoreTest, UpdateThumbnail) { TEST_F(ThumbnailStoreTest, RetrieveFromCache) { RefCountedBytes* read_image = NULL; - store_->cache_->clear(); - store_->redirect_urls_->clear(); // Retrieve a thumbnail/score for a page not in the cache. - EXPECT_FALSE(store_->GetPageThumbnail(url2_, &read_image)); - - // store_ a thumbnail into the cache and retrieve it. + EXPECT_FALSE(store_->GetPageThumbnail(GURL("nonexistent"), &read_image)); - EXPECT_TRUE(store_->SetPageThumbnail(url1_, *google_, score1_, false)); + // Store a thumbnail into the cache and retrieve it. - // Set fake redirects list. - scoped_ptr<std::vector<GURL> > redirects(new std::vector<GURL>); - redirects->push_back(url1_); - (*store_->redirect_urls_)[url1_] = new RefCountedVector<GURL>(*redirects); - - EXPECT_TRUE(store_->GetPageThumbnail(url1_, &read_image)); - EXPECT_TRUE(score1_.Equals((*store_->cache_)[url1_].second)); + EXPECT_TRUE(store_->SetPageThumbnail(url_, *google_, score_)); + EXPECT_TRUE(store_->GetPageThumbnail(url_, &read_image)); + EXPECT_TRUE(score_.Equals((*store_->cache_)[url_].score_)); EXPECT_TRUE(read_image->data.size() == jpeg_google_->data.size()); EXPECT_EQ(0, memcmp(&read_image->data[0], &jpeg_google_->data[0], jpeg_google_->data.size())); @@ -189,46 +176,47 @@ TEST_F(ThumbnailStoreTest, RetrieveFromCache) { } TEST_F(ThumbnailStoreTest, RetrieveFromDisk) { - scoped_refptr<RefCountedBytes> read_image = new RefCountedBytes; - ThumbnailScore score2; - store_->cache_->clear(); - store_->redirect_urls_->clear(); - - // store_ a thumbnail onto the disk and retrieve it. - - EXPECT_TRUE(store_->SetPageThumbnail(url1_, *google_, score1_, false)); - EXPECT_TRUE(store_->WriteThumbnailToDisk(url1_, jpeg_google_, score1_)); - EXPECT_TRUE(store_->GetPageThumbnailFromDisk(file_path_.AppendASCII( - MD5String(url1_.spec())), &url2_, read_image, &score2)); - EXPECT_TRUE(url1_ == url2_); - EXPECT_TRUE(score1_.Equals(score2)); - EXPECT_TRUE(read_image->data.size() == jpeg_google_->data.size()); - EXPECT_EQ(0, memcmp(&read_image->data[0], &jpeg_google_->data[0], + EXPECT_TRUE(store_->SetPageThumbnail(url_, *google_, score_)); + + // Write the thumbnail to disk and retrieve it. + + store_->InitializeFromDB(db_name_, NULL); + store_->CommitCacheToDB(NULL); // Write to the DB (dirty bit sould be set) + store_->cache_->clear(); // Clear it from the cache. + + // Read from the DB. + SQLITE_UNIQUE_STATEMENT(statement, *store_->statement_cache_, + "SELECT * FROM thumbnails"); + EXPECT_TRUE(statement->step() == SQLITE_ROW); + GURL url(statement->column_string(0)); + ThumbnailScore score(statement->column_double(1), + statement->column_bool(2), + statement->column_bool(3), + base::Time::FromInternalValue( + statement->column_int64(4))); + scoped_refptr<RefCountedBytes> data = new RefCountedBytes; + EXPECT_TRUE(statement->column_blob_as_vector(5, &data->data)); + + EXPECT_TRUE(url == url_); + EXPECT_TRUE(score.Equals(score_)); + EXPECT_TRUE(data->data.size() == jpeg_google_->data.size()); + EXPECT_EQ(0, memcmp(&data->data[0], &jpeg_google_->data[0], jpeg_google_->data.size())); } TEST_F(ThumbnailStoreTest, FollowRedirects) { RefCountedBytes* read_image = NULL; - scoped_ptr<std::vector<GURL> > redirects(new std::vector<GURL>); - store_->cache_->clear(); - store_->redirect_urls_->clear(); + std::vector<GURL> redirects; GURL my_url("google"); - redirects->push_back(GURL("google.com")); - redirects->push_back(GURL("www.google.com")); - redirects->push_back(url1_); // url1_ = http://www.google.com/ + redirects.push_back(GURL("google.com")); + redirects.push_back(GURL("www.google.com")); + redirects.push_back(url_); // url_ = http://www.google.com/ + (*store_->redirect_urls_)[my_url] = new RefCountedVector<GURL>(redirects); store_->most_visited_urls_->push_back(my_url); - (*store_->redirect_urls_)[my_url] = new RefCountedVector<GURL>(*redirects); - EXPECT_TRUE(store_->SetPageThumbnail(url1_, *google_, score1_, false)); - EXPECT_TRUE(store_->GetPageThumbnail(my_url, &read_image)); - - read_image->Release(); - store_->cache_->erase(store_->cache_->find(url1_)); - - EXPECT_TRUE(store_->SetPageThumbnail(GURL("google.com"), *google_, score1_, - false)); + EXPECT_TRUE(store_->SetPageThumbnail(GURL("google.com"), *google_, score_)); EXPECT_TRUE(store_->GetPageThumbnail(my_url, &read_image)); read_image->Release(); |