summaryrefslogtreecommitdiffstats
path: root/chrome/browser/history
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-11-18 18:32:45 +0000
committerBen Murdoch <benm@google.com>2010-11-18 18:38:07 +0000
commit513209b27ff55e2841eac0e4120199c23acce758 (patch)
treeaeba30bb08c5f47c57003544e378a377c297eee6 /chrome/browser/history
parent164f7496de0fbee436b385a79ead9e3cb81a50c1 (diff)
downloadexternal_chromium-513209b27ff55e2841eac0e4120199c23acce758.zip
external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.gz
external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.bz2
Merge Chromium at r65505: Initial merge by git.
Change-Id: I31d8f1d8cd33caaf7f47ffa7350aef42d5fbdb45
Diffstat (limited to 'chrome/browser/history')
-rw-r--r--chrome/browser/history/archived_database.cc1
-rw-r--r--chrome/browser/history/expire_history_backend_unittest.cc74
-rw-r--r--chrome/browser/history/history.cc87
-rw-r--r--chrome/browser/history/history.h16
-rw-r--r--chrome/browser/history/history_backend.cc63
-rw-r--r--chrome/browser/history/history_backend.h5
-rw-r--r--chrome/browser/history/history_backend_unittest.cc8
-rw-r--r--chrome/browser/history/history_database.cc39
-rw-r--r--chrome/browser/history/history_database.h9
-rw-r--r--chrome/browser/history/history_notifications.cc4
-rw-r--r--chrome/browser/history/history_notifications.h21
-rw-r--r--chrome/browser/history/history_types.cc3
-rw-r--r--chrome/browser/history/history_types.h69
-rw-r--r--chrome/browser/history/in_memory_database.cc39
-rw-r--r--chrome/browser/history/in_memory_history_backend.cc39
-rw-r--r--chrome/browser/history/in_memory_history_backend.h4
-rw-r--r--chrome/browser/history/in_memory_url_index.cc6
-rw-r--r--chrome/browser/history/in_memory_url_index.h2
-rw-r--r--chrome/browser/history/text_database.cc4
-rw-r--r--chrome/browser/history/thumbnail_database.cc20
-rw-r--r--chrome/browser/history/top_sites.cc1234
-rw-r--r--chrome/browser/history/top_sites.h350
-rw-r--r--chrome/browser/history/top_sites_backend.cc153
-rw-r--r--chrome/browser/history/top_sites_backend.h106
-rw-r--r--chrome/browser/history/top_sites_cache.cc110
-rw-r--r--chrome/browser/history/top_sites_cache.h108
-rw-r--r--chrome/browser/history/top_sites_database.cc176
-rw-r--r--chrome/browser/history/top_sites_database.h87
-rw-r--r--chrome/browser/history/top_sites_unittest.cc1852
-rw-r--r--chrome/browser/history/url_database.cc28
-rw-r--r--chrome/browser/history/url_database.h9
-rw-r--r--chrome/browser/history/url_database_unittest.cc1
32 files changed, 2734 insertions, 1993 deletions
diff --git a/chrome/browser/history/archived_database.cc b/chrome/browser/history/archived_database.cc
index 72fa998..08a742f 100644
--- a/chrome/browser/history/archived_database.cc
+++ b/chrome/browser/history/archived_database.cc
@@ -62,6 +62,7 @@ bool ArchivedDatabase::Init(const FilePath& file_name) {
return false;
}
CreateMainURLIndex();
+ CreateKeywordSearchTermsIndices();
if (EnsureCurrentVersion() != sql::INIT_OK) {
db_.Close();
diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc
index 91129ed..3177dde 100644
--- a/chrome/browser/history/expire_history_backend_unittest.cc
+++ b/chrome/browser/history/expire_history_backend_unittest.cc
@@ -8,6 +8,7 @@
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/scoped_ptr.h"
+#include "base/scoped_temp_dir.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
@@ -31,7 +32,6 @@ using base::TimeDelta;
using base::TimeTicks;
// Filename constants.
-static const FilePath::CharType kTestDir[] = FILE_PATH_LITERAL("ExpireTest");
static const FilePath::CharType kHistoryFile[] = FILE_PATH_LITERAL("History");
static const FilePath::CharType kArchivedHistoryFile[] =
FILE_PATH_LITERAL("Archived History");
@@ -49,6 +49,8 @@ class ExpireHistoryTest : public testing::Test,
public:
ExpireHistoryTest()
: bookmark_model_(NULL),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB, &message_loop_),
ALLOW_THIS_IN_INITIALIZER_LIST(expirer_(this, &bookmark_model_)),
now_(Time::Now()) {
}
@@ -85,9 +87,17 @@ class ExpireHistoryTest : public testing::Test,
static bool IsStringInFile(const FilePath& filename, const char* str);
+ // Returns the path the db files are created in.
+ const FilePath& path() const { return tmp_dir_.path(); }
+
+ // This must be destroyed last.
+ ScopedTempDir tmp_dir_;
+
BookmarkModel bookmark_model_;
- MessageLoop message_loop_;
+ MessageLoopForUI message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread db_thread_;
ExpireHistoryBackend expirer_;
@@ -108,43 +118,40 @@ class ExpireHistoryTest : public testing::Test,
NotificationList;
NotificationList notifications_;
- // Directory for the history files.
- FilePath dir_;
-
private:
void SetUp() {
- FilePath temp_dir;
- PathService::Get(base::DIR_TEMP, &temp_dir);
- dir_ = temp_dir.Append(kTestDir);
- file_util::Delete(dir_, true);
- file_util::CreateDirectory(dir_);
+ ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
- FilePath history_name = dir_.Append(kHistoryFile);
+ FilePath history_name = path().Append(kHistoryFile);
main_db_.reset(new HistoryDatabase);
if (main_db_->Init(history_name, FilePath()) != sql::INIT_OK)
main_db_.reset();
- FilePath archived_name = dir_.Append(kArchivedHistoryFile);
+ FilePath archived_name = path().Append(kArchivedHistoryFile);
archived_db_.reset(new ArchivedDatabase);
if (!archived_db_->Init(archived_name))
archived_db_.reset();
- FilePath thumb_name = dir_.Append(kThumbnailFile);
+ FilePath thumb_name = path().Append(kThumbnailFile);
thumb_db_.reset(new ThumbnailDatabase);
if (thumb_db_->Init(thumb_name, NULL) != sql::INIT_OK)
thumb_db_.reset();
- text_db_.reset(new TextDatabaseManager(dir_,
+ text_db_.reset(new TextDatabaseManager(path(),
main_db_.get(), main_db_.get()));
if (!text_db_->Init(NULL))
text_db_.reset();
expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(),
text_db_.get());
+ profile_.CreateTopSites();
+ profile_.BlockUntilTopSitesLoaded();
top_sites_ = profile_.GetTopSites();
}
void TearDown() {
+ top_sites_ = NULL;
+
ClearLastNotifications();
expirer_.SetDatabases(NULL, NULL, NULL, NULL);
@@ -153,8 +160,6 @@ class ExpireHistoryTest : public testing::Test,
archived_db_.reset();
thumb_db_.reset();
text_db_.reset();
- TopSites::DeleteTopSites(top_sites_);
- file_util::Delete(dir_, true);
}
// BroadcastNotificationDelegate implementation.
@@ -309,11 +314,13 @@ bool ExpireHistoryTest::HasFavIcon(FavIconID favicon_id) {
}
bool ExpireHistoryTest::HasThumbnail(URLID url_id) {
+ // TODO(sky): fix this. This test isn't really valid for TopSites. For
+ // TopSites we should be checking URL always, not the id.
URLRow info;
if (!main_db_->GetURLRow(url_id, &info))
return false;
GURL url = info.url();
- RefCountedBytes *data;
+ scoped_refptr<RefCountedBytes> data;
return top_sites_->GetPageThumbnail(url, &data);
}
@@ -350,7 +357,8 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row) {
EXPECT_EQ(0U, visits.size());
// Thumbnail should be gone.
- EXPECT_FALSE(HasThumbnail(row.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_FALSE(HasThumbnail(row.id()));
// Check the notifications. There should be a delete notification with this
// URL in it. There should also be a "typed URL changed" notification if the
@@ -439,7 +447,8 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) {
URLRow last_row;
ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row));
EXPECT_TRUE(HasFavIcon(last_row.favicon_id()));
- EXPECT_TRUE(HasThumbnail(url_ids[2]));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(url_ids[2]));
VisitVector visits;
main_db_->GetVisitsForURL(url_ids[2], &visits);
@@ -452,14 +461,14 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) {
visits[0].visit_time);
// Compute the text DB filename.
- FilePath fts_filename = dir_.Append(
+ FilePath fts_filename = path().Append(
TextDatabase::IDToFileName(text_db_->TimeToID(visit_times[3])));
// When checking the file, the database must be closed. We then re-initialize
// it just like the test set-up did.
text_db_.reset();
EXPECT_TRUE(IsStringInFile(fts_filename, "goats"));
- text_db_.reset(new TextDatabaseManager(dir_,
+ text_db_.reset(new TextDatabaseManager(path(),
main_db_.get(), main_db_.get()));
ASSERT_TRUE(text_db_->Init(NULL));
expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(),
@@ -472,7 +481,7 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) {
// doesn't remove it from the file, we want to be sure we're doing the latter.
text_db_.reset();
EXPECT_FALSE(IsStringInFile(fts_filename, "goats"));
- text_db_.reset(new TextDatabaseManager(dir_,
+ text_db_.reset(new TextDatabaseManager(path(),
main_db_.get(), main_db_.get()));
ASSERT_TRUE(text_db_->Init(NULL));
expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get(),
@@ -500,7 +509,8 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) {
URLRow last_row;
ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row));
EXPECT_TRUE(HasFavIcon(last_row.favicon_id()));
- EXPECT_TRUE(HasThumbnail(url_ids[1]));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(url_ids[1]));
VisitVector visits;
main_db_->GetVisitsForURL(url_ids[1], &visits);
@@ -546,7 +556,8 @@ TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
ASSERT_EQ(0U, visits.size());
// Should still have the thumbnail.
- ASSERT_TRUE(HasThumbnail(url_row.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // ASSERT_TRUE(HasThumbnail(url_row.id()));
// Unstar the URL and delete again.
bookmark_model_.SetURLStarred(url, string16(), false);
@@ -604,7 +615,8 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
// Verify that the middle URL's favicon and thumbnail is still there.
EXPECT_TRUE(HasFavIcon(url_row1.favicon_id()));
- EXPECT_TRUE(HasThumbnail(url_row1.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(url_row1.id()));
// Verify that the last URL was deleted.
EnsureURLInfoGone(url_row2);
@@ -660,12 +672,14 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
// Verify that the middle URL's favicon and thumbnail is still there.
EXPECT_TRUE(HasFavIcon(url_row1.favicon_id()));
- EXPECT_TRUE(HasThumbnail(url_row1.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(url_row1.id()));
// Verify that the last URL was not touched.
EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
EXPECT_TRUE(HasFavIcon(url_row2.favicon_id()));
- EXPECT_TRUE(HasThumbnail(url_row2.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(url_row2.id()));
}
// Expire a starred URL, it shouldn't get deleted
@@ -706,9 +720,11 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
// exists in history, this should not be a privacy problem, we only update
// the visit counts in this case for consistency anyway.
EXPECT_TRUE(HasFavIcon(new_url_row1.favicon_id()));
- EXPECT_TRUE(HasThumbnail(new_url_row1.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(new_url_row1.id()));
EXPECT_TRUE(HasFavIcon(new_url_row2.favicon_id()));
- EXPECT_TRUE(HasThumbnail(new_url_row2.id()));
+ // TODO(sky): fix this, see comment in HasThumbnail.
+ // EXPECT_TRUE(HasThumbnail(new_url_row2.id()));
}
TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) {
diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc
index bc5db7d..53a5420 100644
--- a/chrome/browser/history/history.cc
+++ b/chrome/browser/history/history.cc
@@ -127,12 +127,8 @@ HistoryService::HistoryService()
profile_(NULL),
backend_loaded_(false),
bookmark_service_(NULL),
- no_db_(false) {
- // Is NULL when running generate_profile.
- if (NotificationService::current()) {
- registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
- Source<Profile>(profile_));
- }
+ no_db_(false),
+ needs_top_sites_migration_(false) {
}
HistoryService::HistoryService(Profile* profile)
@@ -140,9 +136,13 @@ HistoryService::HistoryService(Profile* profile)
profile_(profile),
backend_loaded_(false),
bookmark_service_(NULL),
- no_db_(false) {
+ no_db_(false),
+ needs_top_sites_migration_(false) {
+ DCHECK(profile_);
registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
Source<Profile>(profile_));
+ registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED,
+ Source<Profile>(profile_));
}
HistoryService::~HistoryService() {
@@ -609,30 +609,40 @@ HistoryService::Handle HistoryService::QueryMostVisitedURLs(
void HistoryService::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
- if (type != NotificationType::HISTORY_URLS_DELETED) {
- NOTREACHED();
+ if (!thread_)
return;
- }
- // Update the visited link system for deleted URLs. We will update the
- // visited link system for added URLs as soon as we get the add
- // notification (we don't have to wait for the backend, which allows us to
- // be faster to update the state).
- //
- // For deleted URLs, we don't typically know what will be deleted since
- // delete notifications are by time. We would also like to be more
- // respectful of privacy and never tell the user something is gone when it
- // isn't. Therefore, we update the delete URLs after the fact.
- if (!profile_)
- return; // No profile, probably unit testing.
- Details<history::URLsDeletedDetails> deleted_details(details);
- VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
- if (!visited_links)
- return; // Nobody to update.
- if (deleted_details->all_history)
- visited_links->DeleteAllURLs();
- else // Delete individual ones.
- visited_links->DeleteURLs(deleted_details->urls);
+ switch (type.value) {
+ case NotificationType::HISTORY_URLS_DELETED: {
+ // Update the visited link system for deleted URLs. We will update the
+ // visited link system for added URLs as soon as we get the add
+ // notification (we don't have to wait for the backend, which allows us to
+ // be faster to update the state).
+ //
+ // For deleted URLs, we don't typically know what will be deleted since
+ // delete notifications are by time. We would also like to be more
+ // respectful of privacy and never tell the user something is gone when it
+ // isn't. Therefore, we update the delete URLs after the fact.
+ if (!profile_)
+ return; // No profile, probably unit testing.
+ Details<history::URLsDeletedDetails> deleted_details(details);
+ VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
+ if (!visited_links)
+ return; // Nobody to update.
+ if (deleted_details->all_history)
+ visited_links->DeleteAllURLs();
+ else // Delete individual ones.
+ visited_links->DeleteURLs(deleted_details->urls);
+ break;
+ }
+
+ case NotificationType::TEMPLATE_URL_REMOVED:
+ DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr()));
+ break;
+
+ default:
+ NOTREACHED();
+ }
}
bool HistoryService::Init(const FilePath& history_dir,
@@ -673,6 +683,7 @@ bool HistoryService::CanAddURL(const GURL& url) {
// typed. Right now, however, these are marked as typed even when triggered
// by a shortcut or menu action.
if (url.SchemeIs(chrome::kJavaScriptScheme) ||
+ url.SchemeIs(chrome::kChromeDevToolsScheme) ||
url.SchemeIs(chrome::kChromeUIScheme) ||
url.SchemeIs(chrome::kViewSourceScheme) ||
url.SchemeIs(chrome::kChromeInternalScheme))
@@ -733,6 +744,9 @@ void HistoryService::BroadcastNotifications(
if (!g_browser_process)
return;
+ if (!thread_)
+ return;
+
// The source of all of our notifications is the profile. Note that this
// pointer is NULL in unit tests.
Source<Profile> source(profile_);
@@ -769,12 +783,21 @@ void HistoryService::OnDBLoaded() {
NotificationService::current()->Notify(NotificationType::HISTORY_LOADED,
Source<Profile>(profile_),
Details<HistoryService>(this));
+ if (thread_ && profile_ && history::TopSites::IsEnabled()) {
+ // We don't want to force creation of TopSites.
+ history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
+ if (ts)
+ ts->HistoryLoaded();
+ }
}
void HistoryService::StartTopSitesMigration() {
- if (history::TopSites::IsEnabled()) {
- history::TopSites* ts = profile_->GetTopSites();
- ts->StartMigration();
+ needs_top_sites_migration_ = true;
+ if (thread_ && profile_ && history::TopSites::IsEnabled()) {
+ // We don't want to force creation of TopSites.
+ history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
+ if (ts)
+ ts->MigrateFromHistory();
}
}
diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h
index 83e629e..9d2fd20 100644
--- a/chrome/browser/history/history.h
+++ b/chrome/browser/history/history.h
@@ -118,6 +118,9 @@ class HistoryService : public CancelableRequestProvider,
// it's finished loading.
bool BackendLoaded();
+ // Returns true if the backend has finished loading.
+ bool backend_loaded() const { return backend_loaded_; }
+
// Unloads the backend without actually shutting down the history service.
// This can be used to temporarily reduce the browser process' memory
// footprint.
@@ -405,7 +408,8 @@ class HistoryService : public CancelableRequestProvider,
// Implemented by the caller of 'CreateDownload' below, and is called when the
// history service has created a new entry for a download in the history db.
- typedef Callback2<DownloadCreateInfo, int64>::Type DownloadCreateCallback;
+ typedef Callback2<const DownloadCreateInfo&, int64>::Type
+ DownloadCreateCallback;
// Begins a history request to create a new persistent entry for a download.
// 'info' contains all the download's creation state, and 'callback' runs
@@ -515,6 +519,10 @@ class HistoryService : public CancelableRequestProvider,
virtual Handle ScheduleDBTask(HistoryDBTask* task,
CancelableRequestConsumerBase* consumer);
+ // Returns true if top sites needs to be migrated out of history into its own
+ // db.
+ bool needs_top_sites_migration() const { return needs_top_sites_migration_; }
+
// Testing -------------------------------------------------------------------
// Designed for unit tests, this passes the given task on to the history
@@ -819,7 +827,8 @@ class HistoryService : public CancelableRequestProvider,
// when done. We use this internal consumer for this purpose.
CancelableRequestConsumer internal_consumer_;
- // The thread used by the history service to run complicated operations
+ // The thread used by the history service to run complicated operations.
+ // |thread_| is NULL once |Cleanup| is NULL.
base::Thread* thread_;
// This class has most of the implementation and runs on the 'thread_'.
@@ -847,6 +856,9 @@ class HistoryService : public CancelableRequestProvider,
BookmarkService* bookmark_service_;
bool no_db_;
+ // True if needs top site migration.
+ bool needs_top_sites_migration_;
+
DISALLOW_COPY_AND_ASSIGN(HistoryService);
};
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index fb8e3d3..0bf6045 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -581,12 +581,9 @@ void HistoryBackend::InitImpl(const std::string& languages) {
// Thumbnail database.
thumbnail_db_.reset(new ThumbnailDatabase());
- if (history::TopSites::IsEnabled()) {
- // TODO(sky): once we reenable top sites this needs to be fixed.
- // if (!db_->needs_version_18_migration()) {
+ if (history::TopSites::IsEnabled() && !db_->GetNeedsThumbnailMigration()) {
// No convertion needed - use new filename right away.
- // thumbnail_name = GetFaviconsFileName();
- // }
+ thumbnail_name = GetFaviconsFileName();
}
if (thumbnail_db_->Init(thumbnail_name,
history_publisher_.get()) != sql::INIT_OK) {
@@ -599,12 +596,9 @@ void HistoryBackend::InitImpl(const std::string& languages) {
thumbnail_db_.reset();
}
- if (history::TopSites::IsEnabled()) {
- // TODO(sky): fix when reenabling top sites migration.
- // if (db_->needs_version_18_migration()) {
- // VLOG(1) << "Starting TopSites migration";
- // delegate_->StartTopSitesMigration();
- // }
+ if (history::TopSites::IsEnabled() && db_->GetNeedsThumbnailMigration()) {
+ VLOG(1) << "Starting TopSites migration";
+ delegate_->StartTopSitesMigration();
}
// Archived database.
@@ -1041,6 +1035,14 @@ void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url,
}
db_->SetKeywordSearchTermsForURL(url_row.id(), keyword_id, term);
+
+ // details is deleted by BroadcastNotifications.
+ KeywordSearchTermDetails* details = new KeywordSearchTermDetails;
+ details->url = url;
+ details->keyword_id = keyword_id;
+ details->term = term;
+ BroadcastNotifications(NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
+ details);
ScheduleCommit();
}
@@ -1341,6 +1343,16 @@ void HistoryBackend::QueryMostVisitedURLs(
}
MostVisitedURLList* result = &request->value;
+ QueryMostVisitedURLsImpl(result_count, days_back, result);
+ request->ForwardResult(QueryMostVisitedURLsRequest::TupleType(
+ request->handle(), *result));
+}
+
+void HistoryBackend::QueryMostVisitedURLsImpl(int result_count,
+ int days_back,
+ MostVisitedURLList* result) {
+ if (!db_.get())
+ return;
ScopedVector<PageUsageData> data;
db_->QuerySegmentUsage(base::Time::Now() -
@@ -1354,9 +1366,6 @@ void HistoryBackend::QueryMostVisitedURLs(
MostVisitedURL url = MakeMostVisitedURL(*current_data, redirects);
result->push_back(url);
}
-
- request->ForwardResult(QueryMostVisitedURLsRequest::TupleType(
- request->handle(), *result));
}
void HistoryBackend::GetRedirectsFromSpecificVisit(
@@ -1516,6 +1525,19 @@ void HistoryBackend::GetPageThumbnailDirectly(
}
}
+void HistoryBackend::MigrateThumbnailsDatabase() {
+ // If there is no History DB, we can't record that the migration was done.
+ // It will be recorded on the next run.
+ if (db_.get()) {
+ // If there is no thumbnail DB, we can still record a successful migration.
+ if (thumbnail_db_.get()) {
+ thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(),
+ GetFaviconsFileName());
+ }
+ db_->ThumbnailMigrationDone();
+ }
+}
+
bool HistoryBackend::GetThumbnailFromOlderRedirect(
const GURL& page_url,
std::vector<unsigned char>* data) {
@@ -2149,17 +2171,4 @@ BookmarkService* HistoryBackend::GetBookmarkService() {
return bookmark_service_;
}
-void HistoryBackend::MigrateThumbnailsDatabase() {
- // If there is no History DB, we can't record that the migration was done.
- // It will be recorded on the next run.
- if (db_.get()) {
- // If there is no thumbnail DB, we can still record a successful migration.
- if (thumbnail_db_.get()) {
- thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(),
- GetFaviconsFileName());
- }
- db_->MigrationToTopSitesDone();
- }
-}
-
} // namespace history
diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h
index 3a5cea3..54cbf29 100644
--- a/chrome/browser/history/history_backend.h
+++ b/chrome/browser/history/history_backend.h
@@ -162,6 +162,11 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
int result_count,
int days_back);
+ // QueryMostVisitedURLs without the request.
+ void QueryMostVisitedURLsImpl(int result_count,
+ int days_back,
+ MostVisitedURLList* result);
+
// Computes the most recent URL(s) that the given canonical URL has
// redirected to and returns true on success. There may be more than one
// redirect in a row, so this function will fill the given array with the
diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc
index 70760bd..ad16e9c 100644
--- a/chrome/browser/history/history_backend_unittest.cc
+++ b/chrome/browser/history/history_backend_unittest.cc
@@ -139,7 +139,8 @@ class HistoryBackendTest : public testing::Test {
backend_->Init(std::string(), false);
}
virtual void TearDown() {
- backend_->Closing();
+ if (backend_.get())
+ backend_->Closing();
backend_ = NULL;
mem_backend_.reset();
file_util::Delete(test_dir_, true);
@@ -792,6 +793,8 @@ TEST_F(HistoryBackendTest, RemoveVisitsSource) {
// Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest, MigrationVisitSource) {
ASSERT_TRUE(backend_.get());
+ backend_->Closing();
+ backend_ = NULL;
FilePath old_history_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
@@ -806,11 +809,12 @@ TEST_F(HistoryBackendTest, MigrationVisitSource) {
FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename);
ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file));
- backend_->Closing();
backend_ = new HistoryBackend(new_history_path,
new HistoryBackendTestDelegate(this),
&bookmark_model_);
backend_->Init(std::string(), false);
+ backend_->Closing();
+ backend_ = NULL;
// Now the database should already be migrated.
// Check version first.
diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc
index 56ee054..f0c546c 100644
--- a/chrome/browser/history/history_database.cc
+++ b/chrome/browser/history/history_database.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.
@@ -10,15 +10,16 @@
#include "app/sql/transaction.h"
#include "base/command_line.h"
#include "base/file_util.h"
-#if defined(OS_MACOSX)
-#include "base/mac_util.h"
-#endif
#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "base/string_util.h"
#include "chrome/browser/diagnostics/sqlite_diagnostics.h"
#include "chrome/common/chrome_switches.h"
+#if defined(OS_MACOSX)
+#include "base/mac_util.h"
+#endif
+
namespace history {
namespace {
@@ -26,10 +27,14 @@ namespace {
// Current version number. We write databases at the "current" version number,
// but any previous version that can read the "compatible" one can make do with
// or database without *too* many bad effects.
-static const int kCurrentVersionNumber = 19;
+static const int kCurrentVersionNumber = 20;
static const int kCompatibleVersionNumber = 16;
static const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
+// Key in the meta table used to determine if we need to migrate thumbnails out
+// of history.
+static const char kNeedsThumbnailMigrationKey[] = "needs_thumbnail_migration";
+
void ComputeDatabaseMetrics(const FilePath& history_name,
sql::Connection& db) {
if (base::RandInt(1, 100) != 50)
@@ -114,6 +119,7 @@ sql::InitStatus HistoryDatabase::Init(const FilePath& history_name,
!InitSegmentTables())
return sql::INIT_FAILURE;
CreateMainURLIndex();
+ CreateKeywordSearchTermsIndices();
CreateSupplimentaryURLIndices();
// Version check.
@@ -164,6 +170,7 @@ bool HistoryDatabase::RecreateAllTablesButURL() {
// over parts of the URL table that weren't automatically created when the
// temporary URL table was
CreateSupplimentaryURLIndices();
+ CreateKeywordSearchTermsIndices();
return true;
}
@@ -173,6 +180,16 @@ void HistoryDatabase::Vacuum() {
db_.Execute("VACUUM");
}
+void HistoryDatabase::ThumbnailMigrationDone() {
+ meta_table_.SetValue(kNeedsThumbnailMigrationKey, 0);
+}
+
+bool HistoryDatabase::GetNeedsThumbnailMigration() {
+ int value = 0;
+ return (meta_table_.GetValue(kNeedsThumbnailMigrationKey, &value) &&
+ value != 0);
+}
+
bool HistoryDatabase::SetSegmentID(VisitID visit_id, SegmentID segment_id) {
sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE visits SET segment_id = ? WHERE id = ?"));
@@ -289,6 +306,14 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion(
meta_table_.SetVersionNumber(cur_version);
}
+ if (cur_version == 19) {
+ cur_version++;
+ meta_table_.SetVersionNumber(cur_version);
+ // Set a key indicating we need to migrate thumbnails. When successfull the
+ // key is removed (ThumbnailMigrationDone).
+ meta_table_.SetValue(kNeedsThumbnailMigrationKey, 1);
+ }
+
// When the version is too old, we just try to continue anyway, there should
// not be a released product that makes a database too old for us to handle.
LOG_IF(WARNING, cur_version < GetCurrentVersion()) <<
@@ -322,8 +347,4 @@ void HistoryDatabase::MigrateTimeEpoch() {
}
#endif
-void HistoryDatabase::MigrationToTopSitesDone() {
- // TODO(sky): implement me.
-}
-
} // namespace history
diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h
index fe74e89..1f0c219 100644
--- a/chrome/browser/history/history_database.h
+++ b/chrome/browser/history/history_database.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.
@@ -123,8 +123,11 @@ class HistoryDatabase : public DownloadDatabase,
return needs_version_17_migration_;
}
- // Update the database version after the TopSites migration.
- void MigrationToTopSitesDone();
+ // Marks the database as no longer needing migration.
+ void ThumbnailMigrationDone();
+
+ // Returns true if thumbnails needs to be migrated.
+ bool GetNeedsThumbnailMigration();
// Visit table functions ----------------------------------------------------
diff --git a/chrome/browser/history/history_notifications.cc b/chrome/browser/history/history_notifications.cc
index c846eaf..0fdfa94 100644
--- a/chrome/browser/history/history_notifications.cc
+++ b/chrome/browser/history/history_notifications.cc
@@ -28,4 +28,8 @@ FavIconChangeDetails::FavIconChangeDetails() {}
FavIconChangeDetails::~FavIconChangeDetails() {}
+KeywordSearchTermDetails::KeywordSearchTermDetails() : keyword_id(0) {}
+
+KeywordSearchTermDetails::~KeywordSearchTermDetails() {}
+
} // namespace history
diff --git a/chrome/browser/history/history_notifications.h b/chrome/browser/history/history_notifications.h
index 7a2404c..0d82135 100644
--- a/chrome/browser/history/history_notifications.h
+++ b/chrome/browser/history/history_notifications.h
@@ -13,6 +13,7 @@
#include "googleurl/src/gurl.h"
#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/search_engines/template_url_id.h"
namespace history {
@@ -27,7 +28,7 @@ struct HistoryDetails {
// Details for HISTORY_URL_VISITED.
struct URLVisitedDetails : public HistoryDetails {
URLVisitedDetails();
- ~URLVisitedDetails();
+ virtual ~URLVisitedDetails();
PageTransition::Type transition;
URLRow row;
@@ -42,7 +43,7 @@ struct URLVisitedDetails : public HistoryDetails {
// Details for NOTIFY_HISTORY_TYPED_URLS_MODIFIED.
struct URLsModifiedDetails : public HistoryDetails {
URLsModifiedDetails();
- ~URLsModifiedDetails();
+ virtual ~URLsModifiedDetails();
// Lists the information for each of the URLs affected.
std::vector<URLRow> changed_urls;
@@ -51,7 +52,7 @@ struct URLsModifiedDetails : public HistoryDetails {
// Details for NOTIFY_HISTORY_URLS_DELETED.
struct URLsDeletedDetails : public HistoryDetails {
URLsDeletedDetails();
- ~URLsDeletedDetails();
+ virtual ~URLsDeletedDetails();
// Set when all history was deleted. False means just a subset was deleted.
bool all_history;
@@ -65,7 +66,7 @@ struct URLsDeletedDetails : public HistoryDetails {
// Details for NOTIFY_URLS_STARRED.
struct URLsStarredDetails : public HistoryDetails {
explicit URLsStarredDetails(bool being_starred);
- ~URLsStarredDetails();
+ virtual ~URLsStarredDetails();
// The new starred state of the list of URLs. True when they are being
// starred, false when they are being unstarred.
@@ -78,11 +79,21 @@ struct URLsStarredDetails : public HistoryDetails {
// Details for NOTIFY_FAVICON_CHANGED.
struct FavIconChangeDetails : public HistoryDetails {
FavIconChangeDetails();
- ~FavIconChangeDetails();
+ virtual ~FavIconChangeDetails();
std::set<GURL> urls;
};
+// Details for HISTORY_KEYWORD_SEARCH_TERM_UPDATED.
+struct KeywordSearchTermDetails : public HistoryDetails {
+ KeywordSearchTermDetails();
+ ~KeywordSearchTermDetails();
+
+ GURL url;
+ TemplateURLID keyword_id;
+ string16 term;
+};
+
} // namespace history
#endif // CHROME_BROWSER_HISTORY_HISTORY_NOTIFICATIONS_H__
diff --git a/chrome/browser/history/history_types.cc b/chrome/browser/history/history_types.cc
index 408bb93..eebe5a7 100644
--- a/chrome/browser/history/history_types.cc
+++ b/chrome/browser/history/history_types.cc
@@ -366,4 +366,7 @@ HistoryAddPageArgs* HistoryAddPageArgs::Clone() const {
visit_source, did_replace_entry);
}
+MostVisitedThumbnails::MostVisitedThumbnails() {
+}
+
} // namespace history
diff --git a/chrome/browser/history/history_types.h b/chrome/browser/history/history_types.h
index 831c0b0..536064a 100644
--- a/chrome/browser/history/history_types.h
+++ b/chrome/browser/history/history_types.h
@@ -544,20 +544,6 @@ struct MostVisitedURL {
}
};
-// Used by TopSites to store the thumbnails.
-struct Images {
- Images();
- ~Images();
-
- scoped_refptr<RefCountedBytes> thumbnail;
- ThumbnailScore thumbnail_score;
-
- // TODO(brettw): this will eventually store the favicon.
- // scoped_refptr<RefCountedBytes> favicon;
-};
-
-typedef std::vector<MostVisitedURL> MostVisitedURLList;
-
// Navigation -----------------------------------------------------------------
// Marshalling structure for AddPage.
@@ -598,6 +584,61 @@ class HistoryAddPageArgs
DISALLOW_COPY_AND_ASSIGN(HistoryAddPageArgs);
};
+// TopSites -------------------------------------------------------------------
+
+typedef std::vector<MostVisitedURL> MostVisitedURLList;
+
+// Used by TopSites to store the thumbnails.
+struct Images {
+ Images();
+ ~Images();
+
+ scoped_refptr<RefCountedBytes> thumbnail;
+ ThumbnailScore thumbnail_score;
+
+ // TODO(brettw): this will eventually store the favicon.
+ // scoped_refptr<RefCountedBytes> favicon;
+};
+
+typedef std::vector<MostVisitedURL> MostVisitedURLList;
+
+struct MostVisitedURLWithRank {
+ MostVisitedURL url;
+ int rank;
+};
+
+typedef std::vector<MostVisitedURLWithRank> MostVisitedURLWithRankList;
+
+struct TopSitesDelta {
+ MostVisitedURLList deleted;
+ MostVisitedURLWithRankList added;
+ MostVisitedURLWithRankList moved;
+};
+
+typedef std::map<GURL, scoped_refptr<RefCountedBytes> > URLToThumbnailMap;
+
+// Used when migrating most visited thumbnails out of history and into topsites.
+struct ThumbnailMigration {
+ MostVisitedURLList most_visited;
+ URLToThumbnailMap url_to_thumbnail_map;
+};
+
+typedef std::map<GURL, Images> URLToImagesMap;
+
+class MostVisitedThumbnails
+ : public base::RefCountedThreadSafe<MostVisitedThumbnails> {
+ public:
+ MostVisitedThumbnails();
+
+ MostVisitedURLList most_visited;
+ URLToImagesMap url_to_images_map;
+
+ private:
+ friend class base::RefCountedThreadSafe<MostVisitedThumbnails>;
+
+ DISALLOW_COPY_AND_ASSIGN(MostVisitedThumbnails);
+};
+
} // namespace history
#endif // CHROME_BROWSER_HISTORY_HISTORY_TYPES_H_
diff --git a/chrome/browser/history/in_memory_database.cc b/chrome/browser/history/in_memory_database.cc
index 3227a7a..168a544 100644
--- a/chrome/browser/history/in_memory_database.cc
+++ b/chrome/browser/history/in_memory_database.cc
@@ -41,6 +41,13 @@ bool InMemoryDatabase::InitDB() {
return false;
}
+ // Create the keyword search terms table.
+ if (!InitKeywordSearchTermsTable()) {
+ NOTREACHED() << "Unable to create keyword search terms";
+ db_.Close();
+ return false;
+ }
+
return true;
}
@@ -51,6 +58,7 @@ bool InMemoryDatabase::InitFromScratch() {
// InitDB doesn't create the index so in the disk-loading case, it can be
// added afterwards.
CreateMainURLIndex();
+ CreateKeywordSearchTermsIndices();
return true;
}
@@ -87,6 +95,36 @@ bool InMemoryDatabase::InitFromDisk(const FilePath& history_name) {
end_load - begin_load);
UMA_HISTOGRAM_COUNTS("History.InMemoryDBItemCount", db_.GetLastChangeCount());
+ // Insert keyword search related URLs.
+ begin_load = base::TimeTicks::Now();
+ if (!db_.Execute(
+ "INSERT INTO urls SELECT u.id, u.url, u.title, u.visit_count, "
+ "u.typed_count, u.last_visit_time, u.hidden, u.favicon_id "
+ "FROM history.urls u JOIN history.keyword_search_terms kst "
+ "WHERE u.typed_count = 0 AND u.id = kst.url_id")) {
+ // Unable to get data from the history database. This is OK, the file may
+ // just not exist yet.
+ }
+ end_load = base::TimeTicks::Now();
+ UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordURLPopulate",
+ end_load - begin_load);
+ UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordURLItemCount",
+ db_.GetLastChangeCount());
+
+ // Copy search terms to memory.
+ begin_load = base::TimeTicks::Now();
+ if (!db_.Execute(
+ "INSERT INTO keyword_search_terms SELECT * FROM "
+ "history.keyword_search_terms")) {
+ // Unable to get data from the history database. This is OK, the file may
+ // just not exist yet.
+ }
+ end_load = base::TimeTicks::Now();
+ UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordTermsPopulate",
+ end_load - begin_load);
+ UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordTermsCount",
+ db_.GetLastChangeCount());
+
// Detach from the history database on disk.
if (!db_.Execute("DETACH history")) {
NOTREACHED() << "Unable to detach from history database.";
@@ -96,6 +134,7 @@ bool InMemoryDatabase::InitFromDisk(const FilePath& history_name) {
// Index the table, this is faster than creating the index first and then
// inserting into it.
CreateMainURLIndex();
+ CreateKeywordSearchTermsIndices();
return true;
}
diff --git a/chrome/browser/history/in_memory_history_backend.cc b/chrome/browser/history/in_memory_history_backend.cc
index 6827a78..be82f58 100644
--- a/chrome/browser/history/in_memory_history_backend.cc
+++ b/chrome/browser/history/in_memory_history_backend.cc
@@ -72,6 +72,9 @@ void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source);
registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source);
registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source);
+ registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
+ source);
+ registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source);
}
void InMemoryHistoryBackend::Observe(NotificationType type,
@@ -80,13 +83,20 @@ void InMemoryHistoryBackend::Observe(NotificationType type,
switch (type.value) {
case NotificationType::HISTORY_URL_VISITED: {
Details<history::URLVisitedDetails> visited_details(details);
- if (visited_details->row.typed_count() > 0) {
+ PageTransition::Type primary_type =
+ PageTransition::StripQualifier(visited_details->transition);
+ if (visited_details->row.typed_count() > 0 ||
+ primary_type == PageTransition::KEYWORD) {
URLsModifiedDetails modified_details;
modified_details.changed_urls.push_back(visited_details->row);
OnTypedURLsModified(modified_details);
}
break;
}
+ case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
+ OnKeywordSearchTermUpdated(
+ *Details<history::KeywordSearchTermDetails>(details).ptr());
+ break;
case NotificationType::HISTORY_TYPED_URLS_MODIFIED:
OnTypedURLsModified(
*Details<history::URLsModifiedDetails>(details).ptr());
@@ -94,6 +104,10 @@ void InMemoryHistoryBackend::Observe(NotificationType type,
case NotificationType::HISTORY_URLS_DELETED:
OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr());
break;
+ case NotificationType::TEMPLATE_URL_REMOVED:
+ db_->DeleteAllSearchTermsForKeyword(
+ *(Details<TemplateURLID>(details).ptr()));
+ break;
default:
// For simplicity, the unit tests send us all notifications, even when
// we haven't registered for them, so don't assert here.
@@ -145,4 +159,27 @@ void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
}
}
+void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
+ const KeywordSearchTermDetails& details) {
+ // The url won't exist for new search terms (as the user hasn't typed it), so
+ // we force it to be added. If we end up adding a URL it won't be
+ // autocompleted as the typed count is 0.
+ URLRow url_row;
+ URLID url_id;
+ if (!db_->GetRowForURL(details.url, &url_row)) {
+ // Because this row won't have a typed count the title and other stuff
+ // doesn't matter. If the user ends up typing the url we'll update the title
+ // in OnTypedURLsModified.
+ URLRow new_row(details.url);
+ new_row.set_last_visit(base::Time::Now());
+ url_id = db_->AddURL(new_row);
+ if (!url_id)
+ return; // Error adding.
+ } else {
+ url_id = url_row.id();
+ }
+
+ db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
+}
+
} // namespace history
diff --git a/chrome/browser/history/in_memory_history_backend.h b/chrome/browser/history/in_memory_history_backend.h
index 4fdc3a5..c775800 100644
--- a/chrome/browser/history/in_memory_history_backend.h
+++ b/chrome/browser/history/in_memory_history_backend.h
@@ -30,6 +30,7 @@ namespace history {
class InMemoryDatabase;
class InMemoryURLIndex;
+struct KeywordSearchTermDetails;
class URLDatabase;
struct URLsDeletedDetails;
struct URLsModifiedDetails;
@@ -73,6 +74,9 @@ class InMemoryHistoryBackend : public NotificationObserver {
// Handler for NOTIFY_HISTORY_URLS_DELETED.
void OnURLsDeleted(const URLsDeletedDetails& details);
+ // Handler for HISTORY_KEYWORD_SEARCH_TERM_UPDATED.
+ void OnKeywordSearchTermUpdated(const KeywordSearchTermDetails& details);
+
NotificationRegistrar registrar_;
scoped_ptr<InMemoryDatabase> db_;
diff --git a/chrome/browser/history/in_memory_url_index.cc b/chrome/browser/history/in_memory_url_index.cc
index 654afdc..98c2acd 100644
--- a/chrome/browser/history/in_memory_url_index.cc
+++ b/chrome/browser/history/in_memory_url_index.cc
@@ -34,7 +34,9 @@ ScoredHistoryMatch::ScoredHistoryMatch(const URLRow& url_info,
}
struct InMemoryURLIndex::TermCharWordSet {
- TermCharWordSet(Char16Set char_set, WordIDSet word_id_set, bool used)
+ TermCharWordSet(const Char16Set& char_set,
+ const WordIDSet& word_id_set,
+ bool used)
: char_set_(char_set),
word_id_set_(word_id_set),
used_(used) {}
@@ -81,7 +83,7 @@ bool InMemoryURLIndex::Init(history::URLDatabase* history_db,
return true;
}
-bool InMemoryURLIndex::IndexRow(URLRow row) {
+bool InMemoryURLIndex::IndexRow(const URLRow& row) {
const GURL& gurl(row.url());
string16 url(net::FormatUrl(gurl, languages_,
net::kFormatUrlOmitUsernamePassword,
diff --git a/chrome/browser/history/in_memory_url_index.h b/chrome/browser/history/in_memory_url_index.h
index a2ac0f3..81336f1 100644
--- a/chrome/browser/history/in_memory_url_index.h
+++ b/chrome/browser/history/in_memory_url_index.h
@@ -159,7 +159,7 @@ class InMemoryURLIndex {
// URL History indexing support functions.
// Index one URL history item.
- bool IndexRow(URLRow row);
+ bool IndexRow(const URLRow& row);
// Break a string down into its individual characters.
// Note that this is temporarily intended to work on a single word, but
diff --git a/chrome/browser/history/text_database.cc b/chrome/browser/history/text_database.cc
index 84367d3..60aa7fd 100644
--- a/chrome/browser/history/text_database.cc
+++ b/chrome/browser/history/text_database.cc
@@ -112,8 +112,8 @@ TextDatabase::DBIdent TextDatabase::FileNameToID(const FilePath& file_path) {
}
int year, month;
- base::StringToInt(suffix.substr(0, 4), &year);
- base::StringToInt(suffix.substr(5, 2), &month);
+ base::StringToInt(suffix.begin(), suffix.begin() + 4, &year);
+ base::StringToInt(suffix.begin() + 5, suffix.begin() + 7, &month);
return year * 100 + month;
}
diff --git a/chrome/browser/history/thumbnail_database.cc b/chrome/browser/history/thumbnail_database.cc
index 9cd3f9c..e1054ac 100644
--- a/chrome/browser/history/thumbnail_database.cc
+++ b/chrome/browser/history/thumbnail_database.cc
@@ -104,21 +104,10 @@ sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
// Set the exceptional sqlite error handler.
db->set_error_delegate(GetErrorHandlerForThumbnailDb());
- // Set the database page size to something larger to give us
- // better performance (we're typically seek rather than bandwidth limited).
- // This only has an effect before any tables have been created, otherwise
- // this is a NOP. Must be a power of 2 and a max of 8192. We use a bigger
- // one because we're storing larger data (4-16K) in it, so we want a few
- // blocks per element.
- db->set_page_size(4096);
-
- // The UI is generally designed to work well when the thumbnail database is
- // slow, so we can tolerate much less caching. The file is also very large
- // and so caching won't save a significant percentage of it for us,
- // reducing the benefit of caching in the first place. With the default cache
- // size of 2000 pages, it will take >8MB of memory, so reducing it can be a
- // big savings.
- db->set_cache_size(64);
+ // Thumbnails db now only stores favicons, so we don't need that big a page
+ // size or cache.
+ db->set_page_size(2048);
+ db->set_cache_size(32);
// Run the database in exclusive mode. Nobody else should be accessing the
// database while we're running, and this will give somewhat improved perf.
@@ -310,7 +299,6 @@ bool ThumbnailDatabase::GetPageThumbnail(URLID id,
bool ThumbnailDatabase::DeleteThumbnail(URLID id) {
if (use_top_sites_) {
- LOG(WARNING) << "Use TopSites instead.";
return true; // Not possible after migration to TopSites.
}
diff --git a/chrome/browser/history/top_sites.cc b/chrome/browser/history/top_sites.cc
index 31ce4ac..8bdfe34 100644
--- a/chrome/browser/history/top_sites.cc
+++ b/chrome/browser/history/top_sites.cc
@@ -5,10 +5,10 @@
#include "chrome/browser/history/top_sites.h"
#include <algorithm>
+#include <set>
#include "app/l10n_util.h"
#include "base/command_line.h"
-#include "base/file_util.h"
#include "base/logging.h"
#include "base/md5.h"
#include "base/string_util.h"
@@ -16,14 +16,17 @@
#include "base/values.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/dom_ui/most_visited_handler.h"
+#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/page_usage_data.h"
-#include "chrome/browser/history/top_sites_database.h"
+#include "chrome/browser/history/top_sites_backend.h"
+#include "chrome/browser/history/top_sites_cache.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/thumbnail_score.h"
#include "gfx/codec/jpeg_codec.h"
@@ -36,6 +39,11 @@ namespace history {
// How many top sites to store in the cache.
static const size_t kTopSitesNumber = 20;
+
+// Max number of temporary images we'll cache. See comment above
+// temp_images_ for details.
+static const size_t kMaxTempTopImages = 8;
+
static const size_t kTopSitesShown = 8;
static const int kDaysOfHistory = 90;
// Time from startup to first HistoryService query.
@@ -44,14 +52,91 @@ static const int64 kUpdateIntervalSecs = 15;
static const int64 kMinUpdateIntervalMinutes = 1;
static const int64 kMaxUpdateIntervalMinutes = 60;
+// IDs of the sites we force into top sites.
+static const int kPrepopulatePageIDs[] =
+ { IDS_CHROME_WELCOME_URL, IDS_THEMES_GALLERY_URL };
+
+// Favicons of the sites we force into top sites.
+static const char kPrepopulateFaviconURLs[][54] =
+ { "chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON",
+ "chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON" };
+
+static const int kPrepopulateTitleIDs[] =
+ { IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE,
+ IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE };
+
+namespace {
+
+// HistoryDBTask used during migration of thumbnails from history to top sites.
+// When run on the history thread it collects the top sites and the
+// corresponding thumbnails. When run back on the ui thread it calls into
+// TopSites::FinishHistoryMigration.
+class LoadThumbnailsFromHistoryTask : public HistoryDBTask {
+ public:
+ LoadThumbnailsFromHistoryTask(TopSites* top_sites,
+ int result_count)
+ : top_sites_(top_sites),
+ result_count_(result_count) {
+ // l10n_util isn't thread safe, so cache for use on the db thread.
+ ignore_urls_.insert(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL));
+ ignore_urls_.insert(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL));
+ }
+
+ virtual bool RunOnDBThread(history::HistoryBackend* backend,
+ history::HistoryDatabase* db) {
+ // Get the most visited urls.
+ backend->QueryMostVisitedURLsImpl(result_count_,
+ kDaysOfHistory,
+ &data_.most_visited);
+
+ // And fetch the thumbnails.
+ for (size_t i = 0; i < data_.most_visited.size(); ++i) {
+ const GURL& url = data_.most_visited[i].url;
+ if (ShouldFetchThumbnailFor(url)) {
+ scoped_refptr<RefCountedBytes> data;
+ backend->GetPageThumbnailDirectly(url, &data);
+ data_.url_to_thumbnail_map[url] = data;
+ }
+ }
+ return true;
+ }
+
+ virtual void DoneRunOnMainThread() {
+ top_sites_->FinishHistoryMigration(data_);
+ }
+
+ private:
+ bool ShouldFetchThumbnailFor(const GURL& url) {
+ return ignore_urls_.find(url.spec()) == ignore_urls_.end();
+ }
+
+ // Set of URLs we don't load thumbnails for. This is created on the UI thread
+ // and used on the history thread.
+ std::set<std::string> ignore_urls_;
+
+ scoped_refptr<TopSites> top_sites_;
+
+ // Number of results to request from history.
+ const int result_count_;
+
+ ThumbnailMigration data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadThumbnailsFromHistoryTask);
+};
+
+} // namespace
-TopSites::TopSites(Profile* profile) : profile_(profile),
- mock_history_service_(NULL),
- last_num_urls_changed_(0),
- migration_in_progress_(false),
- waiting_for_results_(true),
- blacklist_(NULL),
- pinned_urls_(NULL) {
+TopSites::TopSites(Profile* profile)
+ : backend_(new TopSitesBackend()),
+ cache_(new TopSitesCache()),
+ thread_safe_cache_(new TopSitesCache()),
+ profile_(profile),
+ last_num_urls_changed_(0),
+ blacklist_(NULL),
+ pinned_urls_(NULL),
+ history_state_(HISTORY_LOADING),
+ top_sites_state_(TOP_SITES_LOADING),
+ loaded_(false) {
if (!profile_)
return;
@@ -70,61 +155,42 @@ TopSites::TopSites(Profile* profile) : profile_(profile),
// static
bool TopSites::IsEnabled() {
- return CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableTopSites);
-}
-
-TopSites::~TopSites() {
- timer_.Stop();
+ std::string switch_value =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kEnableTopSites);
+ return switch_value.empty() || switch_value == "true";
}
void TopSites::Init(const FilePath& db_name) {
- db_path_ = db_name;
- db_.reset(new TopSitesDatabaseImpl());
- if (!db_->Init(db_name)) {
- NOTREACHED() << "Failed to initialize database.";
- return;
- }
+ backend_->Init(db_name);
+ backend_->GetMostVisitedThumbnails(
+ &cancelable_consumer_,
+ NewCallback(this, &TopSites::OnGotMostVisitedThumbnails));
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod(
- this, &TopSites::ReadDatabase));
-
- // Start the one-shot timer.
- timer_.Start(base::TimeDelta::FromSeconds(kUpdateIntervalSecs), this,
- &TopSites::StartQueryForMostVisited);
-}
-
-void TopSites::ReadDatabase() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- std::map<GURL, Images> thumbnails;
-
- DCHECK(db_.get());
- MostVisitedURLList top_urls;
- db_->GetPageThumbnails(&top_urls, &thumbnails);
- MostVisitedURLList copy(top_urls); // StoreMostVisited destroys the list.
- StoreMostVisited(&top_urls);
- if (AddPrepopulatedPages(&copy))
- UpdateMostVisited(copy);
-
- AutoLock lock(lock_);
- for (size_t i = 0; i < top_sites_.size(); i++) {
- GURL url = top_sites_[i].url;
- Images thumbnail = thumbnails[url];
- if (thumbnail.thumbnail.get() && thumbnail.thumbnail->size()) {
- SetPageThumbnailNoDB(url, thumbnail.thumbnail,
- thumbnail.thumbnail_score);
- }
+ // History may have already finished loading by the time we're created.
+ HistoryService* history = profile_->GetHistoryServiceWithoutCreating();
+ if (history && history->backend_loaded()) {
+ if (history->needs_top_sites_migration())
+ MigrateFromHistory();
+ else
+ history_state_ = HISTORY_LOADED;
}
}
-// Public function that encodes the bitmap into RefCountedBytes and
-// updates the database.
bool TopSites::SetPageThumbnail(const GURL& url,
const SkBitmap& thumbnail,
const ThumbnailScore& score) {
- AutoLock lock(lock_);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!loaded_) {
+ // TODO(sky): I need to cache these and apply them after the load
+ // completes.
+ return false;
+ }
+
bool add_temp_thumbnail = false;
- if (canonical_urls_.find(url) == canonical_urls_.end()) {
- if (top_sites_.size() < kTopSitesNumber) {
+ if (!cache_->IsKnownURL(url)) {
+ if (cache_->top_sites().size() < kTopSitesNumber) {
add_temp_thumbnail = true;
} else {
return false; // This URL is not known to us.
@@ -134,18 +200,14 @@ bool TopSites::SetPageThumbnail(const GURL& url,
if (!HistoryService::CanAddURL(url))
return false; // It's not a real webpage.
- scoped_refptr<RefCountedBytes> thumbnail_data = new RefCountedBytes;
- SkAutoLockPixels thumbnail_lock(thumbnail);
- bool encoded = gfx::JPEGCodec::Encode(
- reinterpret_cast<unsigned char*>(thumbnail.getAddr32(0, 0)),
- gfx::JPEGCodec::FORMAT_SkBitmap, thumbnail.width(),
- thumbnail.height(),
- static_cast<int>(thumbnail.rowBytes()), 90,
- &thumbnail_data->data);
- if (!encoded)
+ scoped_refptr<RefCountedBytes> thumbnail_data;
+ if (!EncodeBitmap(thumbnail, &thumbnail_data))
return false;
if (add_temp_thumbnail) {
+ // Always remove the existing entry and then add it back. That way if we end
+ // up with too many temp thumbnails we'll prune the oldest first.
+ RemoveTemporaryThumbnailByURL(url);
AddTemporaryThumbnail(url, thumbnail_data, score);
return true;
}
@@ -153,59 +215,243 @@ bool TopSites::SetPageThumbnail(const GURL& url,
return SetPageThumbnailEncoded(url, thumbnail_data, score);
}
-bool TopSites::SetPageThumbnailEncoded(const GURL& url,
- const RefCountedBytes* thumbnail,
- const ThumbnailScore& score) {
- lock_.AssertAcquired();
- if (!SetPageThumbnailNoDB(url, thumbnail, score))
- return false;
+void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer,
+ GetTopSitesCallback* callback) {
+ // WARNING: this may be invoked on any thread.
+ scoped_refptr<CancelableRequest<GetTopSitesCallback> > request(
+ new CancelableRequest<GetTopSitesCallback>(callback));
+ // This ensures cancelation of requests when either the consumer or the
+ // provider is deleted. Deletion of requests is also guaranteed.
+ AddRequest(request, consumer);
+ MostVisitedURLList filtered_urls;
+ {
+ AutoLock lock(lock_);
+ if (!loaded_) {
+ // A request came in before we finished loading. Put the request in
+ // pending_callbacks_ and we'll notify it when we finish loading.
+ pending_callbacks_.insert(request);
+ return;
+ }
- // Update the database.
- if (!db_.get())
- return true;
- std::map<GURL, size_t>::iterator found = canonical_urls_.find(url);
- if (found == canonical_urls_.end())
- return false;
- size_t index = found->second;
+ filtered_urls = thread_safe_cache_->top_sites();
+ }
+ request->ForwardResult(GetTopSitesCallback::TupleType(filtered_urls));
+}
- MostVisitedURL& most_visited = top_sites_[index];
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod(
- this, &TopSites::WriteThumbnailToDB,
- most_visited, index, top_images_[most_visited.url]));
- return true;
+bool TopSites::GetPageThumbnail(const GURL& url,
+ scoped_refptr<RefCountedBytes>* bytes) {
+ // WARNING: this may be invoked on any thread.
+ AutoLock lock(lock_);
+ return thread_safe_cache_->GetPageThumbnail(url, bytes);
+}
+
+// Returns the index of |url| in |urls|, or -1 if not found.
+static int IndexOf(const MostVisitedURLList& urls, const GURL& url) {
+ for (size_t i = 0; i < urls.size(); i++) {
+ if (urls[i].url == url)
+ return i;
+ }
+ return -1;
}
-void TopSites::WriteThumbnailToDB(const MostVisitedURL& url,
- int url_rank,
- const Images& thumbnail) {
- DCHECK(db_.get());
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- db_->SetPageThumbnail(url, url_rank, thumbnail);
+void TopSites::MigrateFromHistory() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(history_state_, HISTORY_LOADING);
+
+ history_state_ = HISTORY_MIGRATING;
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->ScheduleDBTask(
+ new LoadThumbnailsFromHistoryTask(
+ this,
+ num_results_to_request_from_history()),
+ &cancelable_consumer_);
+ MigratePinnedURLs();
}
-// private
-bool TopSites::SetPageThumbnailNoDB(const GURL& url,
- const RefCountedBytes* thumbnail_data,
- const ThumbnailScore& score) {
- lock_.AssertAcquired();
+void TopSites::FinishHistoryMigration(const ThumbnailMigration& data) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(history_state_, HISTORY_MIGRATING);
- std::map<GURL, size_t>::iterator found = canonical_urls_.find(url);
- if (found == canonical_urls_.end()) {
- if (top_sites_.size() >= kTopSitesNumber)
- return false; // This URL is not known to us.
+ history_state_ = HISTORY_LOADED;
- // We don't have enough Top Sites - add this one too.
- MostVisitedURL mv;
- mv.url = url;
- mv.redirects.push_back(url);
- top_sites_.push_back(mv);
- size_t index = top_sites_.size() - 1;
- StoreRedirectChain(top_sites_[index].redirects, index);
- found = canonical_urls_.find(url);
+ SetTopSites(data.most_visited);
+
+ for (size_t i = 0; i < data.most_visited.size(); ++i) {
+ URLToThumbnailMap::const_iterator image_i =
+ data.url_to_thumbnail_map.find(data.most_visited[i].url);
+ if (image_i != data.url_to_thumbnail_map.end()) {
+ SetPageThumbnailEncoded(data.most_visited[i].url,
+ image_i->second,
+ ThumbnailScore());
+ }
}
- MostVisitedURL& most_visited = top_sites_[found->second];
- Images& image = top_images_[most_visited.url];
+ MoveStateToLoaded();
+
+ ResetThreadSafeImageCache();
+
+ // We've scheduled all the thumbnails and top sites to be written to the top
+ // sites db, but it hasn't happened yet. Schedule a request on the db thread
+ // that notifies us when done. When done we'll know everything was written and
+ // we can tell history to finish its part of migration.
+ backend_->DoEmptyRequest(
+ &cancelable_consumer_,
+ NewCallback(this, &TopSites::OnHistoryMigrationWrittenToDisk));
+}
+
+void TopSites::HistoryLoaded() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_NE(history_state_, HISTORY_LOADED);
+
+ if (history_state_ != HISTORY_MIGRATING) {
+ // No migration from history is needed.
+ history_state_ = HISTORY_LOADED;
+ if (top_sites_state_ == TOP_SITES_LOADED_WAITING_FOR_HISTORY) {
+ // TopSites thought it needed migration, but it really didn't. This
+ // typically happens the first time a profile is run with Top Sites
+ // enabled
+ SetTopSites(MostVisitedURLList());
+ MoveStateToLoaded();
+ }
+ }
+}
+
+bool TopSites::HasBlacklistedItems() const {
+ return !blacklist_->empty();
+}
+
+void TopSites::AddBlacklistedURL(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RemovePinnedURL(url);
+ Value* dummy = Value::CreateNullValue();
+ blacklist_->SetWithoutPathExpansion(GetURLHash(url), dummy);
+
+ ResetThreadSafeCache();
+}
+
+void TopSites::RemoveBlacklistedURL(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ blacklist_->RemoveWithoutPathExpansion(GetURLHash(url), NULL);
+ ResetThreadSafeCache();
+}
+
+bool TopSites::IsBlacklisted(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return blacklist_->HasKey(GetURLHash(url));
+}
+
+void TopSites::ClearBlacklistedURLs() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ blacklist_->Clear();
+ ResetThreadSafeCache();
+}
+
+void TopSites::AddPinnedURL(const GURL& url, size_t pinned_index) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ GURL old;
+ if (GetPinnedURLAtIndex(pinned_index, &old))
+ RemovePinnedURL(old);
+
+ if (IsURLPinned(url))
+ RemovePinnedURL(url);
+
+ Value* index = Value::CreateIntegerValue(pinned_index);
+ pinned_urls_->SetWithoutPathExpansion(GetURLString(url), index);
+
+ ResetThreadSafeCache();
+}
+
+bool TopSites::IsURLPinned(const GURL& url) {
+ int tmp;
+ return pinned_urls_->GetIntegerWithoutPathExpansion(GetURLString(url), &tmp);
+}
+
+void TopSites::RemovePinnedURL(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ pinned_urls_->RemoveWithoutPathExpansion(GetURLString(url), NULL);
+
+ ResetThreadSafeCache();
+}
+
+bool TopSites::GetPinnedURLAtIndex(size_t index, GURL* url) {
+ for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys();
+ it != pinned_urls_->end_keys(); ++it) {
+ int current_index;
+ if (pinned_urls_->GetIntegerWithoutPathExpansion(*it, &current_index)) {
+ if (static_cast<size_t>(current_index) == index) {
+ *url = GURL(*it);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TopSites::Shutdown() {
+ profile_ = NULL;
+ // Cancel all requests so that the service doesn't callback to us after we've
+ // invoked Shutdown (this could happen if we have a pending request and
+ // Shutdown is invoked).
+ cancelable_consumer_.CancelAllRequests();
+ backend_->Shutdown();
+}
+
+// static
+void TopSites::DiffMostVisited(const MostVisitedURLList& old_list,
+ const MostVisitedURLList& new_list,
+ TopSitesDelta* delta) {
+ // Add all the old URLs for quick lookup. This maps URLs to the corresponding
+ // index in the input.
+ std::map<GURL, size_t> all_old_urls;
+ for (size_t i = 0; i < old_list.size(); i++)
+ all_old_urls[old_list[i].url] = i;
+
+ // Check all the URLs in the new set to see which ones are new or just moved.
+ // When we find a match in the old set, we'll reset its index to our special
+ // marker. This allows us to quickly identify the deleted ones in a later
+ // pass.
+ const size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
+ for (size_t i = 0; i < new_list.size(); i++) {
+ std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url);
+ if (found == all_old_urls.end()) {
+ MostVisitedURLWithRank added;
+ added.url = new_list[i];
+ added.rank = i;
+ delta->added.push_back(added);
+ } else {
+ if (found->second != i) {
+ MostVisitedURLWithRank moved;
+ moved.url = new_list[i];
+ moved.rank = i;
+ delta->moved.push_back(moved);
+ }
+ found->second = kAlreadyFoundMarker;
+ }
+ }
+
+ // Any member without the special marker in the all_old_urls list means that
+ // there wasn't a "new" URL that mapped to it, so it was deleted.
+ for (std::map<GURL, size_t>::const_iterator i = all_old_urls.begin();
+ i != all_old_urls.end(); ++i) {
+ if (i->second != kAlreadyFoundMarker)
+ delta->deleted.push_back(old_list[i->second]);
+ }
+}
+
+TopSites::~TopSites() {
+}
+
+bool TopSites::SetPageThumbnailNoDB(const GURL& url,
+ const RefCountedBytes* thumbnail_data,
+ const ThumbnailScore& score) {
+ // This should only be invoked when we know about the url.
+ DCHECK(cache_->IsKnownURL(url));
+
+ const MostVisitedURL& most_visited =
+ cache_->top_sites()[cache_->GetURLIndex(url)];
+ Images* image = cache_->GetImage(url);
// When comparing the thumbnail scores, we need to take into account the
// redirect hops, which are not generated when the thumbnail is because the
@@ -214,95 +460,136 @@ bool TopSites::SetPageThumbnailNoDB(const GURL& url,
new_score_with_redirects.redirect_hops_from_dest =
GetRedirectDistanceForURL(most_visited, url);
- if (!ShouldReplaceThumbnailWith(image.thumbnail_score,
+ if (!ShouldReplaceThumbnailWith(image->thumbnail_score,
new_score_with_redirects) &&
- image.thumbnail.get())
+ image->thumbnail.get())
return false; // The one we already have is better.
- // Take ownership of the thumbnail data.
- image.thumbnail = const_cast<RefCountedBytes*>(thumbnail_data);
- image.thumbnail_score = new_score_with_redirects;
+ image->thumbnail = const_cast<RefCountedBytes*>(thumbnail_data);
+ image->thumbnail_score = new_score_with_redirects;
+ ResetThreadSafeImageCache();
return true;
}
-void TopSites::GetMostVisitedURLs(CancelableRequestConsumer* consumer,
- GetTopSitesCallback* callback) {
- scoped_refptr<CancelableRequest<GetTopSitesCallback> > request(
- new CancelableRequest<GetTopSitesCallback>(callback));
- // This ensures cancelation of requests when either the consumer or the
- // provider is deleted. Deletion of requests is also guaranteed.
- AddRequest(request, consumer);
- MostVisitedURLList filtered_urls;
- {
- AutoLock lock(lock_);
- if (waiting_for_results_) {
- // A request came in before we have any top sites.
- // We have to keep track of the requests ourselves.
- pending_callbacks_.insert(request);
+bool TopSites::SetPageThumbnailEncoded(const GURL& url,
+ const RefCountedBytes* thumbnail,
+ const ThumbnailScore& score) {
+ if (!SetPageThumbnailNoDB(url, thumbnail, score))
+ return false;
+
+ // Update the database.
+ if (!cache_->IsKnownURL(url))
+ return false;
+
+ size_t index = cache_->GetURLIndex(url);
+ const MostVisitedURL& most_visited = cache_->top_sites()[index];
+ backend_->SetPageThumbnail(most_visited,
+ index,
+ *(cache_->GetImage(most_visited.url)));
+ return true;
+}
+
+// static
+bool TopSites::EncodeBitmap(const SkBitmap& bitmap,
+ scoped_refptr<RefCountedBytes>* bytes) {
+ *bytes = new RefCountedBytes();
+ SkAutoLockPixels bitmap_lock(bitmap);
+ std::vector<unsigned char> data;
+ if (!gfx::JPEGCodec::Encode(
+ reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
+ gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(),
+ bitmap.height(),
+ static_cast<int>(bitmap.rowBytes()), 90,
+ &data)) {
+ return false;
+ }
+ // As we're going to cache this data, make sure the vector is only as big as
+ // it needs to be.
+ (*bytes)->data = data;
+ return true;
+}
+
+void TopSites::RemoveTemporaryThumbnailByURL(const GURL& url) {
+ for (TempImages::iterator i = temp_images_.begin(); i != temp_images_.end();
+ ++i) {
+ if (i->first == url) {
+ temp_images_.erase(i);
return;
}
- if (request->canceled())
- return;
-
- ApplyBlacklistAndPinnedURLs(top_sites_, &filtered_urls);
}
- request->ForwardResult(GetTopSitesCallback::TupleType(filtered_urls));
}
-bool TopSites::GetPageThumbnail(const GURL& url, RefCountedBytes** data) const {
- AutoLock lock(lock_);
- std::map<GURL, Images>::const_iterator found =
- top_images_.find(GetCanonicalURL(url));
- if (found == top_images_.end()) {
- found = temp_thumbnails_map_.find(url);
- if (found == temp_thumbnails_map_.end())
- return false; // No thumbnail for this URL.
+void TopSites::AddTemporaryThumbnail(const GURL& url,
+ const RefCountedBytes* thumbnail,
+ const ThumbnailScore& score) {
+ if (temp_images_.size() == kMaxTempTopImages)
+ temp_images_.erase(temp_images_.begin());
+
+ TempImage image;
+ image.first = url;
+ image.second.thumbnail = const_cast<RefCountedBytes*>(thumbnail);
+ image.second.thumbnail_score = score;
+ temp_images_.push_back(image);
+}
+
+void TopSites::StartQueryForMostVisited() {
+ if (!profile_)
+ return;
+
+ HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ // |hs| may be null during unit tests.
+ if (hs) {
+ hs->QueryMostVisitedURLs(
+ num_results_to_request_from_history(),
+ kDaysOfHistory,
+ &cancelable_consumer_,
+ NewCallback(this, &TopSites::OnTopSitesAvailableFromHistory));
}
+}
- Images image = found->second;
- *data = image.thumbnail.get();
- return true;
+// static
+int TopSites::GetRedirectDistanceForURL(const MostVisitedURL& most_visited,
+ const GURL& url) {
+ for (size_t i = 0; i < most_visited.redirects.size(); i++) {
+ if (most_visited.redirects[i] == url)
+ return static_cast<int>(most_visited.redirects.size() - i - 1);
+ }
+ NOTREACHED() << "URL should always be found.";
+ return 0;
}
-static int IndexOf(const MostVisitedURLList& urls, const GURL& url) {
- for (size_t i = 0; i < urls.size(); i++) {
- if (urls[i].url == url)
- return i;
+// static
+MostVisitedURLList TopSites::GetPrepopulatePages() {
+ MostVisitedURLList urls;
+ urls.resize(arraysize(kPrepopulatePageIDs));
+ for (size_t i = 0; i < arraysize(kPrepopulatePageIDs); ++i) {
+ MostVisitedURL& url = urls[i];
+ url.url = GURL(l10n_util::GetStringUTF8(kPrepopulatePageIDs[i]));
+ url.redirects.push_back(url.url);
+ url.favicon_url = GURL(kPrepopulateFaviconURLs[i]);
+ url.title = l10n_util::GetStringUTF16(kPrepopulateTitleIDs[i]);
}
- return -1;
+ return urls;
}
+// static
bool TopSites::AddPrepopulatedPages(MostVisitedURLList* urls) {
- // TODO(arv): This needs to get the data from some configurable place.
- // http://crbug.com/17630
bool added = false;
- GURL welcome_url(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL));
- if (urls->size() < kTopSitesNumber && IndexOf(*urls, welcome_url) == -1) {
- MostVisitedURL url(
- welcome_url,
- GURL("chrome://theme/IDR_NEWTAB_CHROME_WELCOME_PAGE_FAVICON"),
- l10n_util::GetStringUTF16(IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE));
- url.redirects.push_back(welcome_url);
- urls->push_back(url);
- added = true;
- }
-
- GURL themes_url(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL));
- if (urls->size() < kTopSitesNumber && IndexOf(*urls, themes_url) == -1) {
- MostVisitedURL url(
- themes_url,
- GURL("chrome://theme/IDR_NEWTAB_THEMES_GALLERY_FAVICON"),
- l10n_util::GetStringUTF16(IDS_NEW_TAB_THEMES_GALLERY_PAGE_TITLE));
- url.redirects.push_back(themes_url);
- urls->push_back(url);
- added = true;
+ MostVisitedURLList prepopulate_urls = GetPrepopulatePages();
+ for (size_t i = 0; i < prepopulate_urls.size(); ++i) {
+ if (urls->size() < kTopSitesNumber &&
+ IndexOf(*urls, prepopulate_urls[i].url) == -1) {
+ urls->push_back(prepopulate_urls[i]);
+ added = true;
+ }
}
-
return added;
}
void TopSites::MigratePinnedURLs() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
std::map<GURL, size_t> tmp_map;
for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys();
it != pinned_urls_->end_keys(); ++it) {
@@ -327,7 +614,6 @@ void TopSites::MigratePinnedURLs() {
void TopSites::ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls,
MostVisitedURLList* out) {
- lock_.AssertAcquired();
MostVisitedURLList urls_copy;
for (size_t i = 0; i < urls.size(); i++) {
if (!IsBlacklisted(urls[i].url))
@@ -375,521 +661,247 @@ void TopSites::ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls,
}
std::string TopSites::GetURLString(const GURL& url) {
- lock_.AssertAcquired();
- return GetCanonicalURL(url).spec();
+ return cache_->GetCanonicalURL(url).spec();
}
std::string TopSites::GetURLHash(const GURL& url) {
- lock_.AssertAcquired();
// We don't use canonical URLs here to be able to blacklist only one of
// the two 'duplicate' sites, e.g. 'gmail.com' and 'mail.google.com'.
return MD5String(url.spec());
}
-void TopSites::UpdateMostVisited(MostVisitedURLList most_visited) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+base::TimeDelta TopSites::GetUpdateDelay() {
+ if (cache_->top_sites().size() <= arraysize(kPrepopulateTitleIDs))
+ return base::TimeDelta::FromSeconds(30);
- std::vector<size_t> added; // Indices into most_visited.
- std::vector<size_t> deleted; // Indices into top_sites_.
- std::vector<size_t> moved; // Indices into most_visited.
+ int64 range = kMaxUpdateIntervalMinutes - kMinUpdateIntervalMinutes;
+ int64 minutes = kMaxUpdateIntervalMinutes -
+ last_num_urls_changed_ * range / cache_->top_sites().size();
+ return base::TimeDelta::FromMinutes(minutes);
+}
- DiffMostVisited(top_sites_, most_visited, &added, &deleted, &moved);
+// static
+void TopSites::ProcessPendingCallbacks(
+ const PendingCallbackSet& pending_callbacks,
+ const MostVisitedURLList& urls) {
+ PendingCallbackSet::const_iterator i;
+ for (i = pending_callbacks.begin();
+ i != pending_callbacks.end(); ++i) {
+ scoped_refptr<CancelableRequest<GetTopSitesCallback> > request = *i;
+ if (!request->canceled())
+ request->ForwardResult(GetTopSitesCallback::TupleType(urls));
+ }
+}
- // #added == #deleted; #added + #moved = total.
- last_num_urls_changed_ = added.size() + moved.size();
+void TopSites::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (!loaded_)
+ return;
- {
- AutoLock lock(lock_);
+ if (type == NotificationType::HISTORY_URLS_DELETED) {
+ Details<history::URLsDeletedDetails> deleted_details(details);
+ if (deleted_details->all_history) {
+ SetTopSites(MostVisitedURLList());
+ backend_->ResetDatabase();
+ } else {
+ std::set<size_t> indices_to_delete; // Indices into top_sites_.
+ for (std::set<GURL>::iterator i = deleted_details->urls.begin();
+ i != deleted_details->urls.end(); ++i) {
+ if (cache_->IsKnownURL(*i))
+ indices_to_delete.insert(cache_->GetURLIndex(*i));
+ }
- // Process the diff: delete from images and disk, add to disk.
- // Delete all the thumbnails associated with URLs that were deleted.
- for (size_t i = 0; i < deleted.size(); i++) {
- const MostVisitedURL& deleted_url = top_sites_[deleted[i]];
- std::map<GURL, Images>::iterator found =
- top_images_.find(deleted_url.url);
- if (found != top_images_.end())
- top_images_.erase(found);
- }
- }
+ if (indices_to_delete.empty())
+ return;
- // Write the updates to the DB.
- if (db_.get()) {
- for (size_t i = 0; i < deleted.size(); i++) {
- const MostVisitedURL& deleted_url = top_sites_[deleted[i]];
- if (db_.get())
- db_->RemoveURL(deleted_url);
- }
- for (size_t i = 0; i < added.size(); i++) {
- const MostVisitedURL& added_url = most_visited[added[i]];
- db_->SetPageThumbnail(added_url, added[i], Images());
- }
- for (size_t i = 0; i < moved.size(); i++) {
- const MostVisitedURL& moved_url = most_visited[moved[i]];
- db_->UpdatePageRank(moved_url, moved[i]);
+ MostVisitedURLList new_top_sites(cache_->top_sites());
+ for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin();
+ i != indices_to_delete.rend(); i++) {
+ size_t index = *i;
+ RemovePinnedURL(new_top_sites[index].url);
+ new_top_sites.erase(new_top_sites.begin() + index);
+ }
+ SetTopSites(new_top_sites);
}
- }
-
- StoreMostVisited(&most_visited);
- if (migration_in_progress_) {
- // Copy all thumnbails from the history service.
- for (size_t i = 0; i < top_sites_.size(); i++) {
- GURL& url = top_sites_[i].url;
- Images& img = top_images_[url];
- if (!img.thumbnail.get() || !img.thumbnail->size()) {
- StartQueryForThumbnail(i);
+ StartQueryForMostVisited();
+ } else if (type == NotificationType::NAV_ENTRY_COMMITTED) {
+ if (cache_->top_sites().size() < kTopSitesNumber) {
+ NavigationController::LoadCommittedDetails* load_details =
+ Details<NavigationController::LoadCommittedDetails>(details).ptr();
+ if (!load_details)
+ return;
+ const GURL& url = load_details->entry->url();
+ if (!cache_->IsKnownURL(url) && HistoryService::CanAddURL(url)) {
+ // To avoid slamming history we throttle requests when the url updates.
+ // To do otherwise negatively impacts perf tests.
+ RestartQueryForTopSitesTimer(GetUpdateDelay());
}
}
}
-
- // If we are not expecting any thumbnails, migration is done.
- if (migration_in_progress_ && migration_pending_urls_.empty())
- OnMigrationDone();
-
- timer_.Stop();
- timer_.Start(GetUpdateDelay(), this,
- &TopSites::StartQueryForMostVisited);
}
-void TopSites::OnMigrationDone() {
- migration_in_progress_ = false;
- if (!profile_)
- return;
+void TopSites::SetTopSites(const MostVisitedURLList& new_top_sites) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- // |hs| may be null during unit tests.
- if (!hs)
- return;
- hs->OnTopSitesReady();
-}
-
-void TopSites::AddTemporaryThumbnail(const GURL& url,
- const RefCountedBytes* thumbnail,
- const ThumbnailScore& score) {
- Images& img = temp_thumbnails_map_[url];
- img.thumbnail = const_cast<RefCountedBytes*>(thumbnail);
- img.thumbnail_score = score;
-}
-
-void TopSites::StartQueryForThumbnail(size_t index) {
- DCHECK(migration_in_progress_);
- if (top_sites_[index].url.spec() ==
- l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL) ||
- top_sites_[index].url.spec() ==
- l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL))
- return; // Don't need thumbnails for prepopulated URLs.
-
- migration_pending_urls_.insert(top_sites_[index].url);
-
- if (mock_history_service_) {
- // Testing with a mockup.
- // QueryMostVisitedURLs is not virtual, so we have to duplicate the code.
- // This calls SetClientData.
- mock_history_service_->GetPageThumbnail(
- top_sites_[index].url,
- &cancelable_consumer_,
- NewCallback(this, &TopSites::OnThumbnailAvailable),
- index);
- return;
- }
+ MostVisitedURLList top_sites(new_top_sites);
+ AddPrepopulatedPages(&top_sites);
- if (!profile_)
- return;
+ TopSitesDelta delta;
+ DiffMostVisited(cache_->top_sites(), top_sites, &delta);
+ if (!delta.deleted.empty() || !delta.added.empty() || !delta.moved.empty())
+ backend_->UpdateTopSites(delta);
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- // |hs| may be null during unit tests.
- if (!hs)
- return;
- HistoryService::Handle handle =
- hs->GetPageThumbnail(top_sites_[index].url,
- &cancelable_consumer_,
- NewCallback(this, &TopSites::OnThumbnailAvailable));
- cancelable_consumer_.SetClientData(hs, handle, index);
-}
+ last_num_urls_changed_ = delta.added.size() + delta.moved.size();
-void TopSites::GenerateCanonicalURLs() {
- lock_.AssertAcquired();
- canonical_urls_.clear();
- for (size_t i = 0; i < top_sites_.size(); i++) {
- const MostVisitedURL& mv = top_sites_[i];
- StoreRedirectChain(mv.redirects, i);
- }
-}
+ // We always do the following steps (setting top sites in cache, and resetting
+ // thread safe cache ...) as this method is invoked during startup at which
+ // point the caches haven't been updated yet.
+ cache_->SetTopSites(top_sites);
-void TopSites::StoreMostVisited(MostVisitedURLList* most_visited) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- MostVisitedURLList filtered_urls;
- PendingCallbackSet callbacks;
- {
- AutoLock lock(lock_);
- top_sites_.clear();
- // Take ownership of the most visited data.
- top_sites_.swap(*most_visited);
- waiting_for_results_ = false;
-
- // Save the redirect information for quickly mapping to the canonical URLs.
- GenerateCanonicalURLs();
-
- for (size_t i = 0; i < top_sites_.size(); i++) {
- const MostVisitedURL& mv = top_sites_[i];
- std::map<GURL, Images>::iterator it = temp_thumbnails_map_.begin();
- GURL canonical_url = GetCanonicalURL(mv.url);
- for (; it != temp_thumbnails_map_.end(); it++) {
- // Must map all temp URLs to canonical ones.
- // temp_thumbnails_map_ contains non-canonical URLs, because
- // when we add a temp thumbnail, redirect chain is not known.
- // This is slow, but temp_thumbnails_map_ should have very few URLs.
- if (canonical_url == GetCanonicalURL(it->first)) {
- SetPageThumbnailEncoded(mv.url, it->second.thumbnail,
+ // See if we have any tmp thumbnails for the new sites.
+ if (!temp_images_.empty()) {
+ for (size_t i = 0; i < top_sites.size(); ++i) {
+ const MostVisitedURL& mv = top_sites[i];
+ GURL canonical_url = cache_->GetCanonicalURL(mv.url);
+ // At the time we get the thumbnail redirects aren't known, so we have to
+ // iterate through all the images.
+ for (TempImages::iterator it = temp_images_.begin();
+ it != temp_images_.end(); ++it) {
+ if (canonical_url == cache_->GetCanonicalURL(it->first)) {
+ SetPageThumbnailEncoded(mv.url,
+ it->second.thumbnail,
it->second.thumbnail_score);
- temp_thumbnails_map_.erase(it);
+ temp_images_.erase(it);
break;
}
}
}
- if (top_sites_.size() >= kTopSitesNumber)
- temp_thumbnails_map_.clear();
+ }
- if (pending_callbacks_.empty())
- return;
+ if (top_sites.size() >= kTopSitesNumber)
+ temp_images_.clear();
- ApplyBlacklistAndPinnedURLs(top_sites_, &filtered_urls);
- callbacks.swap(pending_callbacks_);
- } // lock_ is released.
- // Process callbacks outside the lock - ForwardResults may cause
- // thread switches.
- ProcessPendingCallbacks(callbacks, filtered_urls);
-}
+ ResetThreadSafeCache();
+ ResetThreadSafeImageCache();
-void TopSites::StoreRedirectChain(const RedirectList& redirects,
- size_t destination) {
- lock_.AssertAcquired();
- if (redirects.empty()) {
- NOTREACHED();
- return;
- }
-
- // Map all the redirected URLs to the destination.
- for (size_t i = 0; i < redirects.size(); i++) {
- // If this redirect is already known, don't replace it with a new one.
- if (canonical_urls_.find(redirects[i]) == canonical_urls_.end())
- canonical_urls_[redirects[i]] = destination;
- }
+ // Restart the timer that queries history for top sites. This is done to
+ // ensure we stay in sync with history.
+ RestartQueryForTopSitesTimer(GetUpdateDelay());
}
-GURL TopSites::GetCanonicalURL(const GURL& url) const {
- lock_.AssertAcquired();
- std::map<GURL, size_t>::const_iterator found = canonical_urls_.find(url);
- if (found == canonical_urls_.end())
- return url; // Unknown URL - return unchanged.
- return top_sites_[found->second].url;
-}
+int TopSites::num_results_to_request_from_history() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-// static
-int TopSites::GetRedirectDistanceForURL(const MostVisitedURL& most_visited,
- const GURL& url) {
- for (size_t i = 0; i < most_visited.redirects.size(); i++) {
- if (most_visited.redirects[i] == url)
- return static_cast<int>(most_visited.redirects.size() - i - 1);
- }
- NOTREACHED() << "URL should always be found.";
- return 0;
+ return kTopSitesNumber + blacklist_->size();
}
-// static
-void TopSites::DiffMostVisited(const MostVisitedURLList& old_list,
- const MostVisitedURLList& new_list,
- std::vector<size_t>* added_urls,
- std::vector<size_t>* deleted_urls,
- std::vector<size_t>* moved_urls) {
- added_urls->clear();
- deleted_urls->clear();
- moved_urls->clear();
-
- // Add all the old URLs for quick lookup. This maps URLs to the corresponding
- // index in the input.
- std::map<GURL, size_t> all_old_urls;
- for (size_t i = 0; i < old_list.size(); i++)
- all_old_urls[old_list[i].url] = i;
+void TopSites::MoveStateToLoaded() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // Check all the URLs in the new set to see which ones are new or just moved.
- // When we find a match in the old set, we'll reset its index to our special
- // marker. This allows us to quickly identify the deleted ones in a later
- // pass.
- const size_t kAlreadyFoundMarker = static_cast<size_t>(-1);
- for (size_t i = 0; i < new_list.size(); i++) {
- std::map<GURL, size_t>::iterator found = all_old_urls.find(new_list[i].url);
- if (found == all_old_urls.end()) {
- added_urls->push_back(i);
- } else {
- if (found->second != i)
- moved_urls->push_back(i);
- found->second = kAlreadyFoundMarker;
- }
- }
+ MostVisitedURLList filtered_urls;
+ PendingCallbackSet pending_callbacks;
+ {
+ AutoLock lock(lock_);
- // Any member without the special marker in the all_old_urls list means that
- // there wasn't a "new" URL that mapped to it, so it was deleted.
- for (std::map<GURL, size_t>::const_iterator i = all_old_urls.begin();
- i != all_old_urls.end(); ++i) {
- if (i->second != kAlreadyFoundMarker)
- deleted_urls->push_back(i->second);
- }
-}
+ if (loaded_)
+ return; // Don't do anything if we're already loaded.
+ loaded_ = true;
-void TopSites::StartQueryForMostVisited() {
- if (mock_history_service_) {
- // Testing with a mockup.
- // QueryMostVisitedURLs is not virtual, so we have to duplicate the code.
- mock_history_service_->QueryMostVisitedURLs(
- kTopSitesNumber + blacklist_->size(),
- kDaysOfHistory,
- &cancelable_consumer_,
- NewCallback(this, &TopSites::OnTopSitesAvailable));
- } else {
- if (!profile_)
- return;
-
- HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- // |hs| may be null during unit tests.
- if (hs) {
- hs->QueryMostVisitedURLs(
- kTopSitesNumber + blacklist_->size(),
- kDaysOfHistory,
- &cancelable_consumer_,
- NewCallback(this, &TopSites::OnTopSitesAvailable));
- } else {
- VLOG(1) << "History Service not available.";
+ // Now that we're loaded we can service the queued up callbacks. Copy them
+ // here and service them outside the lock.
+ if (!pending_callbacks_.empty()) {
+ filtered_urls = thread_safe_cache_->top_sites();
+ pending_callbacks.swap(pending_callbacks_);
}
}
-}
-void TopSites::StartMigration() {
- VLOG(1) << "Starting migration to TopSites.";
- migration_in_progress_ = true;
- StartQueryForMostVisited();
- MigratePinnedURLs();
-}
+ ProcessPendingCallbacks(pending_callbacks, filtered_urls);
-bool TopSites::HasBlacklistedItems() const {
- AutoLock lock(lock_);
- return !blacklist_->empty();
+ NotificationService::current()->Notify(NotificationType::TOP_SITES_LOADED,
+ Source<Profile>(profile_),
+ Details<TopSites>(this));
}
-void TopSites::AddBlacklistedURL(const GURL& url) {
+void TopSites::ResetThreadSafeCache() {
AutoLock lock(lock_);
- RemovePinnedURLLocked(url);
- Value* dummy = Value::CreateNullValue();
- blacklist_->SetWithoutPathExpansion(GetURLHash(url), dummy);
+ MostVisitedURLList cached;
+ ApplyBlacklistAndPinnedURLs(cache_->top_sites(), &cached);
+ thread_safe_cache_->SetTopSites(cached);
}
-bool TopSites::IsBlacklisted(const GURL& url) {
- lock_.AssertAcquired();
- bool result = blacklist_->HasKey(GetURLHash(url));
- return result;
-}
-
-void TopSites::RemoveBlacklistedURL(const GURL& url) {
+void TopSites::ResetThreadSafeImageCache() {
AutoLock lock(lock_);
- blacklist_->RemoveWithoutPathExpansion(GetURLHash(url), NULL);
-}
-
-void TopSites::ClearBlacklistedURLs() {
- blacklist_->Clear();
+ thread_safe_cache_->SetThumbnails(cache_->images());
+ thread_safe_cache_->RemoveUnreferencedThumbnails();
}
-void TopSites::AddPinnedURL(const GURL& url, size_t pinned_index) {
- GURL old;
- if (GetPinnedURLAtIndex(pinned_index, &old)) {
- RemovePinnedURL(old);
- }
-
- if (IsURLPinned(url)) {
- RemovePinnedURL(url);
+void TopSites::RestartQueryForTopSitesTimer(base::TimeDelta delta) {
+ if (timer_.IsRunning() && ((timer_start_time_ + timer_.GetCurrentDelay()) <
+ (base::TimeTicks::Now() + delta))) {
+ return;
}
- Value* index = Value::CreateIntegerValue(pinned_index);
- AutoLock lock(lock_);
- pinned_urls_->SetWithoutPathExpansion(GetURLString(url), index);
-}
-
-void TopSites::RemovePinnedURL(const GURL& url) {
- AutoLock lock(lock_);
- RemovePinnedURLLocked(url);
-}
-
-void TopSites::RemovePinnedURLLocked(const GURL& url) {
- lock_.AssertAcquired();
- pinned_urls_->RemoveWithoutPathExpansion(GetURLString(url), NULL);
+ timer_start_time_ = base::TimeTicks::Now();
+ timer_.Stop();
+ timer_.Start(delta, this, &TopSites::StartQueryForMostVisited);
}
-bool TopSites::IsURLPinned(const GURL& url) {
- AutoLock lock(lock_);
- int tmp;
- bool result = pinned_urls_->GetIntegerWithoutPathExpansion(
- GetURLString(url), &tmp);
- return result;
-}
+void TopSites::OnHistoryMigrationWrittenToDisk(TopSitesBackend::Handle handle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-bool TopSites::GetPinnedURLAtIndex(size_t index, GURL* url) {
- for (DictionaryValue::key_iterator it = pinned_urls_->begin_keys();
- it != pinned_urls_->end_keys(); ++it) {
- int current_index;
- if (pinned_urls_->GetIntegerWithoutPathExpansion(*it, &current_index)) {
- if (static_cast<size_t>(current_index) == index) {
- *url = GURL(*it);
- return true;
- }
- }
- }
- return false;
-}
-
-// static
-void TopSites::DeleteTopSites(scoped_refptr<TopSites>& ptr) {
- if (!ptr.get() || !MessageLoop::current())
+ if (!profile_)
return;
- if (BrowserThread::IsWellKnownThread(BrowserThread::UI)) {
- ptr = NULL;
- } else {
- // Need to roll our own UI thread.
- BrowserThread ui_loop(BrowserThread::UI, MessageLoop::current());
- ptr = NULL;
- MessageLoop::current()->RunAllPending();
- }
-}
-
-void TopSites::ClearProfile() {
- profile_ = NULL;
-}
-
-base::TimeDelta TopSites::GetUpdateDelay() {
- AutoLock lock(lock_);
- if (top_sites_.size() == 0)
- return base::TimeDelta::FromSeconds(30);
- int64 range = kMaxUpdateIntervalMinutes - kMinUpdateIntervalMinutes;
- int64 minutes = kMaxUpdateIntervalMinutes -
- last_num_urls_changed_ * range / top_sites_.size();
- return base::TimeDelta::FromMinutes(minutes);
+ HistoryService* history =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (history)
+ history->OnTopSitesReady();
}
-void TopSites::OnTopSitesAvailable(
+void TopSites::OnGotMostVisitedThumbnails(
CancelableRequestProvider::Handle handle,
- MostVisitedURLList pages) {
- AddPrepopulatedPages(&pages);
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, NewRunnableMethod(
- this, &TopSites::UpdateMostVisited, pages));
-}
+ scoped_refptr<MostVisitedThumbnails> data,
+ bool may_need_history_migration) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(top_sites_state_, TOP_SITES_LOADING);
-// static
-void TopSites::ProcessPendingCallbacks(PendingCallbackSet pending_callbacks,
- const MostVisitedURLList& urls) {
- PendingCallbackSet::iterator i;
- for (i = pending_callbacks.begin();
- i != pending_callbacks.end(); ++i) {
- scoped_refptr<CancelableRequest<GetTopSitesCallback> > request = *i;
- if (!request->canceled())
- request->ForwardResult(GetTopSitesCallback::TupleType(urls));
- }
- pending_callbacks.clear();
-}
+ if (!may_need_history_migration) {
+ top_sites_state_ = TOP_SITES_LOADED;
-void TopSites::OnThumbnailAvailable(CancelableRequestProvider::Handle handle,
- scoped_refptr<RefCountedBytes> thumbnail) {
- size_t index;
- if (mock_history_service_) {
- index = handle;
- } else {
- if (!profile_)
- return;
+ // Set the top sites directly in the cache so that SetTopSites diffs
+ // correctly.
+ cache_->SetTopSites(data->most_visited);
+ SetTopSites(data->most_visited);
+ cache_->SetThumbnails(data->url_to_images_map);
- HistoryService* hs = profile_ ->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (!hs)
- return;
- index = cancelable_consumer_.GetClientData(hs, handle);
- }
- DCHECK(static_cast<size_t>(index) < top_sites_.size());
+ ResetThreadSafeImageCache();
- if (migration_in_progress_)
- migration_pending_urls_.erase(top_sites_[index].url);
+ MoveStateToLoaded();
- if (thumbnail.get() && thumbnail->size()) {
- const MostVisitedURL& url = top_sites_[index];
- AutoLock lock(lock_);
- SetPageThumbnailEncoded(url.url, thumbnail, ThumbnailScore());
- }
-
- if (migration_in_progress_ && migration_pending_urls_.empty() &&
- !mock_history_service_)
- OnMigrationDone();
-}
-
-void TopSites::SetMockHistoryService(MockHistoryService* mhs) {
- mock_history_service_ = mhs;
-}
-
-void TopSites::Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- AutoLock lock(lock_);
- if (type == NotificationType::HISTORY_URLS_DELETED) {
- Details<history::URLsDeletedDetails> deleted_details(details);
- if (deleted_details->all_history) {
- top_sites_.clear();
- BrowserThread::PostTask(
- BrowserThread::DB, FROM_HERE,
- NewRunnableMethod(this, &TopSites::ResetDatabase));
+ // Start a timer that refreshes top sites from history.
+ RestartQueryForTopSitesTimer(
+ base::TimeDelta::FromSeconds(kUpdateIntervalSecs));
+ } else {
+ // The top sites file didn't exist or is the wrong version. We need to wait
+ // for history to finish loading to know if we really needed to migrate.
+ if (history_state_ == HISTORY_LOADED) {
+ top_sites_state_ = TOP_SITES_LOADED;
+ SetTopSites(MostVisitedURLList());
+ MoveStateToLoaded();
} else {
- std::set<size_t> indices_to_delete; // Indices into top_sites_.
- std::set<GURL>::iterator it;
- for (it = deleted_details->urls.begin();
- it != deleted_details->urls.end(); ++it) {
- std::map<GURL,size_t>::const_iterator found = canonical_urls_.find(*it);
- if (found != canonical_urls_.end())
- indices_to_delete.insert(found->second);
- }
-
- for (std::set<size_t>::reverse_iterator i = indices_to_delete.rbegin();
- i != indices_to_delete.rend(); i++) {
- size_t index = *i;
- RemovePinnedURLLocked(top_sites_[index].url);
- top_sites_.erase(top_sites_.begin() + index);
- }
- }
- // Canonical URLs are not valid any more.
- GenerateCanonicalURLs();
- StartQueryForMostVisited();
- } else if (type == NotificationType::NAV_ENTRY_COMMITTED) {
- if (top_sites_.size() < kTopSitesNumber) {
- NavigationController::LoadCommittedDetails* load_details =
- Details<NavigationController::LoadCommittedDetails>(details).ptr();
- if (!load_details)
- return;
- GURL url = load_details->entry->url();
- if (canonical_urls_.find(url) == canonical_urls_.end() &&
- HistoryService::CanAddURL(url)) {
- // Add this page to the known pages in case the thumbnail comes
- // in before we get the results.
- MostVisitedURL mv;
- mv.url = url;
- mv.redirects.push_back(url);
- top_sites_.push_back(mv);
- size_t index = top_sites_.size() - 1;
- StoreRedirectChain(top_sites_[index].redirects, index);
- }
- StartQueryForMostVisited();
+ top_sites_state_ = TOP_SITES_LOADED_WAITING_FOR_HISTORY;
+ // Ask for history just in case it hasn't been loaded yet. When history
+ // finishes loading we'll do migration and/or move to loaded.
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
}
}
}
-void TopSites::ResetDatabase() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- db_.reset(new TopSitesDatabaseImpl());
- file_util::Delete(db_path_, false);
- if (!db_->Init(db_path_)) {
- NOTREACHED() << "Failed to initialize database.";
- return;
- }
+void TopSites::OnTopSitesAvailableFromHistory(
+ CancelableRequestProvider::Handle handle,
+ MostVisitedURLList pages) {
+ SetTopSites(pages);
}
} // namespace history
diff --git a/chrome/browser/history/top_sites.h b/chrome/browser/history/top_sites.h
index f080c05..bd406f1 100644
--- a/chrome/browser/history/top_sites.h
+++ b/chrome/browser/history/top_sites.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.
@@ -6,14 +6,14 @@
#define CHROME_BROWSER_HISTORY_TOP_SITES_H_
#pragma once
-#include <map>
+#include <list>
#include <set>
#include <string>
-#include <vector>
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/lock.h"
+#include "base/time.h"
#include "base/timer.h"
#include "base/ref_counted.h"
#include "base/ref_counted_memory.h"
@@ -27,30 +27,26 @@
#include "googleurl/src/gurl.h"
class DictionaryValue;
+class FilePath;
class SkBitmap;
class Profile;
namespace history {
+class TopSitesCache;
class TopSitesBackend;
-class TopSitesDatabase;
class TopSitesTest;
-typedef std::vector<MostVisitedURL> MostVisitedURLList;
-
// Stores the data for the top "most visited" sites. This includes a cache of
// the most visited data from history, as well as the corresponding thumbnails
// of those sites.
//
-// This class IS threadsafe. It is designed to be used from the UI thread of
-// the browser (where history requests must be kicked off and received from)
-// and from the I/O thread (where new tab page requests come in). Handling the
-// new tab page requests on the I/O thread without proxying to the UI thread is
-// a nontrivial performance win, especially when the browser is starting and
-// the UI thread is busy.
-class TopSites :
- public base::RefCountedThreadSafe<TopSites,
- BrowserThread::DeleteOnUIThread>,
+// This class allows requests for most visited urls and thumbnails on any
+// thread. All other methods must be invoked on the UI thread. All mutations
+// to internal state happen on the UI thread and are scheduled to update the
+// db using TopSitesBackend.
+class TopSites
+ : public base::RefCountedThreadSafe<TopSites>,
public NotificationObserver,
public CancelableRequestProvider {
public:
@@ -59,21 +55,6 @@ class TopSites :
// Returns whether top sites is enabled.
static bool IsEnabled();
- class MockHistoryService {
- // A mockup of a HistoryService used for testing TopSites.
- public:
- virtual HistoryService::Handle QueryMostVisitedURLs(
- int result_count, int days_back,
- CancelableRequestConsumerBase* consumer,
- HistoryService::QueryMostVisitedURLsCallback* callback) = 0;
- virtual ~MockHistoryService() {}
- virtual void GetPageThumbnail(
- const GURL& page_url,
- CancelableRequestConsumerTSimple<size_t>* consumer,
- HistoryService::ThumbnailDataCallback* callback,
- size_t index) = 0;
- };
-
// Initializes TopSites.
void Init(const FilePath& db_name);
@@ -85,23 +66,33 @@ class TopSites :
const ThumbnailScore& score);
// Callback for GetMostVisitedURLs.
- typedef Callback1<MostVisitedURLList>::Type GetTopSitesCallback;
+ typedef Callback1<const MostVisitedURLList&>::Type GetTopSitesCallback;
typedef std::set<scoped_refptr<CancelableRequest<GetTopSitesCallback> > >
PendingCallbackSet;
// Returns a list of most visited URLs via a callback.
+ // This may be invoked on any thread.
// NOTE: the callback may be called immediately if we have the data cached.
void GetMostVisitedURLs(CancelableRequestConsumer* consumer,
GetTopSitesCallback* callback);
// Get a thumbnail for a given page. Returns true iff we have the thumbnail.
- bool GetPageThumbnail(const GURL& url, RefCountedBytes** data) const;
+ // This may be invoked on any thread.
+ // As this method may be invoked on any thread the ref count needs to be
+ // upped before this method returns, so this takes a scoped_refptr*.
+ bool GetPageThumbnail(const GURL& url,
+ scoped_refptr<RefCountedBytes>* bytes);
+
+ // Invoked from History if migration is needed. If this is invoked it will
+ // be before HistoryLoaded is invoked.
+ void MigrateFromHistory();
- // For testing with a HistoryService mock.
- void SetMockHistoryService(MockHistoryService* mhs);
+ // Invoked with data from migrating thumbnails out of history.
+ void FinishHistoryMigration(const ThumbnailMigration& data);
- // Start reading thumbnails from the ThumbnailDatabase.
- void StartMigration();
+ // Invoked from history when it finishes loading. If MigrateFromHistory was
+ // not invoked at this point then we load from the top sites service.
+ void HistoryLoaded();
// Blacklisted URLs
@@ -114,6 +105,9 @@ class TopSites :
// Removes a URL from the blacklist.
void RemoveBlacklistedURL(const GURL& url);
+ // Returns true if the URL is blacklisted.
+ bool IsBlacklisted(const GURL& url);
+
// Clear the blacklist.
void ClearBlacklistedURLs();
@@ -132,34 +126,53 @@ class TopSites :
// is a URL pinned at |index|.
bool GetPinnedURLAtIndex(size_t index, GURL* out);
- // TopSites must be deleted on a UI thread. This happens
- // automatically in a real browser, but in unit_tests we don't have
- // a real UI thread. Use this function to delete a TopSites object.
- static void DeleteTopSites(scoped_refptr<TopSites>& ptr);
+ // Shuts down top sites.
+ void Shutdown();
- // Sets the profile pointer to NULL. This is for the case where
- // TopSites outlives the profile, since TopSites is refcounted.
- void ClearProfile();
+ // Generates the diff of things that happened between "old" and "new."
+ //
+ // The URLs that are in "new" but not "old" will be have their index into
+ // "new" put in |added_urls|. The URLs that are in "old" but not "new" will
+ // have their index into "old" put into |deleted_urls|.
+ //
+ // URLs appearing in both old and new lists but having different indices will
+ // have their index into "new" be put into |moved_urls|.
+ static void DiffMostVisited(const MostVisitedURLList& old_list,
+ const MostVisitedURLList& new_list,
+ TopSitesDelta* delta);
private:
- friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
- friend class DeleteTask<TopSites>;
+ friend class base::RefCountedThreadSafe<TopSites>;
friend class TopSitesTest;
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetMostVisited);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, RealDatabase);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, MockDatabase);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, DeleteNotifications);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, PinnedURLsDeleted);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetUpdateDelay);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, Migration);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, QueueingRequestsForTopSites);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, CancelingRequestsForTopSites);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, AddTemporaryThumbnail);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, Blacklisting);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, PinnedURLs);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, BlacklistingAndPinnedURLs);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, AddPrepopulatedPages);
- FRIEND_TEST_ALL_PREFIXES(TopSitesTest, GetPageThumbnail);
+
+ typedef std::pair<GURL, Images> TempImage;
+ typedef std::list<TempImage> TempImages;
+
+ // Enumeration of the possible states history can be in.
+ enum HistoryLoadState {
+ // We're waiting for history to finish loading.
+ HISTORY_LOADING,
+
+ // History finished loading and we need to migrate top sites out of history.
+ HISTORY_MIGRATING,
+
+ // History is loaded.
+ HISTORY_LOADED
+ };
+
+ // Enumeration of possible states the top sites backend can be in.
+ enum TopSitesLoadState {
+ // We're waiting for the backend to finish loading.
+ TOP_SITES_LOADING,
+
+ // The backend finished loading, but we may need to migrate. This is true if
+ // the top sites db didn't exist, or if the db existed but is from an old
+ // version.
+ TOP_SITES_LOADED_WAITING_FOR_HISTORY,
+
+ // Top sites is loaded.
+ TOP_SITES_LOADED
+ };
~TopSites();
@@ -176,44 +189,22 @@ class TopSites :
const RefCountedBytes* thumbnail,
const ThumbnailScore& score);
- // Query history service for the list of available thumbnails.
- void StartQueryForMostVisited();
-
- // Query history service for the thumbnail for a given url. |index|
- // is the index into top_sites_.
- void StartQueryForThumbnail(size_t index);
-
- // Called when history service returns a list of top URLs.
- void OnTopSitesAvailable(CancelableRequestProvider::Handle handle,
- MostVisitedURLList data);
-
- // Returns a list of urls to each pending callback.
- static void ProcessPendingCallbacks(PendingCallbackSet pending_callbacks,
- const MostVisitedURLList& urls);
-
- // Called when history service returns a thumbnail.
- void OnThumbnailAvailable(CancelableRequestProvider::Handle handle,
- scoped_refptr<RefCountedBytes> thumbnail);
+ // Encodes the bitmap to bytes for storage to the db. Returns true if the
+ // bitmap was successfully encoded.
+ static bool EncodeBitmap(const SkBitmap& bitmap,
+ scoped_refptr<RefCountedBytes>* bytes);
- // Sets canonical_urls_ from top_sites_.
- void GenerateCanonicalURLs();
+ // Removes the cached thumbnail for url. Does nothing if |url| if not cached
+ // in |temp_images_|.
+ void RemoveTemporaryThumbnailByURL(const GURL& url);
- // Saves the set of the top URLs visited by this user. The 0th item is the
- // most popular.
- // DANGER! This will clear all data from the input argument.
- void StoreMostVisited(MostVisitedURLList* most_visited);
-
- // Saves the given set of redirects. The redirects are in order of the
- // given vector, so [0] -> [1] -> [2].
- void StoreRedirectChain(const RedirectList& redirects,
- size_t destination);
+ // Add a thumbnail for an unknown url. See temp_thumbnails_map_.
+ void AddTemporaryThumbnail(const GURL& url,
+ const RefCountedBytes* thumbnail,
+ const ThumbnailScore& score);
- // Each item in the most visited view can redirect elsewhere. This returns
- // the canonical URL one identifying the site if the given URL does appear
- // in the "top sites" list.
- //
- // If the given URL is not in the top sites, this will return an empty GURL.
- GURL GetCanonicalURL(const GURL& url) const;
+ // Query history service for the list of available thumbnails.
+ void StartQueryForMostVisited();
// Finds the given URL in the redirect chain for the given TopSite, and
// returns the distance from the destination in hops that the given URL is.
@@ -221,131 +212,114 @@ class TopSites :
static int GetRedirectDistanceForURL(const MostVisitedURL& most_visited,
const GURL& url);
- // Generates the diff of things that happened between "old" and "new."
- //
- // The URLs that are in "new" but not "old" will be have their index into
- // "new" put in |added_urls|. The URLs that are in "old" but not "new" will
- // have their index into "old" put into |deleted_urls|.
- //
- // URLs appearing in both old and new lists but having different indices will
- // have their index into "new" be put into |moved_urls|.
- static void DiffMostVisited(const MostVisitedURLList& old_list,
- const MostVisitedURLList& new_list,
- std::vector<size_t>* added_urls,
- std::vector<size_t>* deleted_urls,
- std::vector<size_t>* moved_urls);
+ // Returns the set of prepopulate pages.
+ static MostVisitedURLList GetPrepopulatePages();
- // Implementation of NotificationObserver.
- virtual void Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details);
+ // Add prepopulated pages: 'welcome to Chrome' and themes gallery to |urls|.
+ // Returns true if any pages were added.
+ static bool AddPrepopulatedPages(MostVisitedURLList* urls);
- // Returns true if the URL is blacklisted.
- bool IsBlacklisted(const GURL& url);
+ // Convert pinned_urls_ dictionary to the new format. Use URLs as
+ // dictionary keys.
+ void MigratePinnedURLs();
+
+ // Takes |urls|, produces it's copy in |out| after removing
+ // blacklisted URLs and reordering pinned URLs.
+ void ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls,
+ MostVisitedURLList* out);
- // A variant of RemovePinnedURL that must be called within a lock.
- void RemovePinnedURLLocked(const GURL& url);
+ // Converts a url into a canonical string representation.
+ std::string GetURLString(const GURL& url);
+
+ // Returns an MD5 hash of the URL. Hashing is required for blacklisted URLs.
+ std::string GetURLHash(const GURL& url);
// Returns the delay until the next update of history is needed.
// Uses num_urls_changed
base::TimeDelta GetUpdateDelay();
- // The following methods must be run on the DB thread since they
- // access the database.
+ // Executes all of the callbacks in |pending_callbacks|. This is used after
+ // we finish loading if any requests came in before we loaded.
+ static void ProcessPendingCallbacks(
+ const PendingCallbackSet& pending_callbacks,
+ const MostVisitedURLList& urls);
- // Reads the database from disk. Called on startup to get the last
- // known top sites.
- void ReadDatabase();
+ // Implementation of NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
- // Write a thumbnail to database.
- void WriteThumbnailToDB(const MostVisitedURL& url,
- int url_rank,
- const Images& thumbnail);
+ // Resets top_sites_ and updates the db (in the background). All mutations to
+ // top_sites_ *must* go through this.
+ void SetTopSites(const MostVisitedURLList& new_top_sites);
- // Updates the top sites list and writes the difference to disk.
- void UpdateMostVisited(MostVisitedURLList most_visited);
+ // Returns the number of most visted results to request from history. This
+ // changes depending upon how many urls have been blacklisted.
+ int num_results_to_request_from_history() const;
- // Deletes the database file, then reinitializes the database.
- void ResetDatabase();
+ // Invoked when transitioning to LOADED. Notifies any queued up callbacks.
+ void MoveStateToLoaded();
- // Called after TopSites completes migration.
- void OnMigrationDone();
+ void ResetThreadSafeCache();
- // Add a thumbnail for an unknown url. See temp_thumbnails_map_.
- void AddTemporaryThumbnail(const GURL& url,
- const RefCountedBytes* thumbnail,
- const ThumbnailScore& score);
+ void ResetThreadSafeImageCache();
- // Add prepopulated pages: 'welcome to Chrome' and themes gallery.
- // Returns true if any pages were added.
- bool AddPrepopulatedPages(MostVisitedURLList* urls);
+ // Stops and starts timer with a delay of |delta|.
+ void RestartQueryForTopSitesTimer(base::TimeDelta delta);
- // Convert pinned_urls_ dictionary to the new format. Use URLs as
- // dictionary keys.
- void MigratePinnedURLs();
+ // Callback after TopSitesBackend has finished migration. This tells history
+ // to finish it's side of migration (nuking thumbnails on disk).
+ void OnHistoryMigrationWrittenToDisk(
+ CancelableRequestProvider::Handle handle);
- // Takes |urls|, produces it's copy in |out| after removing
- // blacklisted URLs and reordering pinned URLs.
- void ApplyBlacklistAndPinnedURLs(const MostVisitedURLList& urls,
- MostVisitedURLList* out);
+ // Callback from TopSites with the top sites/thumbnails.
+ void OnGotMostVisitedThumbnails(CancelableRequestProvider::Handle handle,
+ scoped_refptr<MostVisitedThumbnails> data,
+ bool may_need_history_migration);
- // Converts a url into a canonical string representation.
- std::string GetURLString(const GURL& url);
+ // Called when history service returns a list of top URLs.
+ void OnTopSitesAvailableFromHistory(CancelableRequestProvider::Handle handle,
+ MostVisitedURLList data);
- // Returns an MD5 hash of the URL. Hashing is required for blacklisted URLs.
- std::string GetURLHash(const GURL& url);
+ scoped_refptr<TopSitesBackend> backend_;
- Profile* profile_;
- // A mockup to use for testing. If NULL, use the real HistoryService
- // from the profile_. See SetMockHistoryService.
- MockHistoryService* mock_history_service_;
- CancelableRequestConsumerTSimple<size_t> cancelable_consumer_;
- mutable Lock lock_;
+ // The top sites data.
+ scoped_ptr<TopSitesCache> cache_;
- // The cached version of the top sites. The 0th item in this vector is the
- // #1 site.
- MostVisitedURLList top_sites_;
+ // Copy of the top sites data that may be accessed on any thread (assuming
+ // you hold |lock_|). The data in |thread_safe_cache_| has blacklisted and
+ // pinned urls applied (|cache_| does not).
+ scoped_ptr<TopSitesCache> thread_safe_cache_;
- // The images corresponding to the top_sites. This is indexed by the URL of
- // the top site, so this doesn't have to be shuffled around when the ordering
- // changes of the top sites. Some top_sites_ entries may not have images.
- std::map<GURL, Images> top_images_;
+ Profile* profile_;
+
+ // Lock used to access |thread_safe_cache_|.
+ mutable Lock lock_;
- // Generated from the redirects to and from the most visited pages, this
- // maps the redirects to the index into top_sites_ that contains it.
- std::map<GURL, size_t> canonical_urls_;
+ CancelableRequestConsumer cancelable_consumer_;
- // Timer for updating TopSites data.
+ // Timer that asks history for the top sites. This is used to make sure our
+ // data stays in sync with history.
base::OneShotTimer<TopSites> timer_;
- scoped_ptr<TopSitesDatabase> db_;
- FilePath db_path_;
+ // The time we started |timer_| at. Only valid if |timer_| is running.
+ base::TimeTicks timer_start_time_;
NotificationRegistrar registrar_;
// The number of URLs changed on the last update.
size_t last_num_urls_changed_;
- // Are we in the middle of migration from ThumbnailsDatabase to
- // TopSites?
- bool migration_in_progress_;
-
- // URLs for which we are expecting thumbnails.
- std::set<GURL> migration_pending_urls_;
-
// The map of requests for the top sites list. Can only be
// non-empty at startup. After we read the top sites from the DB, we'll
// always have a cached list.
PendingCallbackSet pending_callbacks_;
- // Are we waiting for the top sites from HistoryService?
- bool waiting_for_results_;
-
// Stores thumbnails for unknown pages. When SetPageThumbnail is
// called, if we don't know about that URL yet and we don't have
// enough Top Sites (new profile), we store it until the next
- // UpdateMostVisitedURLs call.
- std::map<GURL, Images> temp_thumbnails_map_;
+ // SetTopSites call.
+ TempImages temp_images_;
// Blacklisted and pinned URLs are stored in Preferences.
@@ -356,12 +330,20 @@ class TopSites :
// PrefService.
DictionaryValue* blacklist_;
- // This is a dictionary for the pinned URLs for the the most visited
- // part of the new tab page. Key is the URL, value is
- // index where it is pinned at (may be the same as key). This is
- // owned by the PrefService.
+ // This is a dictionary for the pinned URLs for the the most visited part of
+ // the new tab page. Key is the URL, value is index where it is pinned at (may
+ // be the same as key). This is owned by the PrefService.
DictionaryValue* pinned_urls_;
+ // See description above HistoryLoadState.
+ HistoryLoadState history_state_;
+
+ // See description above TopSitesLoadState.
+ TopSitesLoadState top_sites_state_;
+
+ // Are we loaded?
+ bool loaded_;
+
DISALLOW_COPY_AND_ASSIGN(TopSites);
};
diff --git a/chrome/browser/history/top_sites_backend.cc b/chrome/browser/history/top_sites_backend.cc
new file mode 100644
index 0000000..eb27a5e
--- /dev/null
+++ b/chrome/browser/history/top_sites_backend.cc
@@ -0,0 +1,153 @@
+// 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.
+
+#include "chrome/browser/history/top_sites_backend.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/history/top_sites_database.h"
+
+namespace history {
+
+TopSitesBackend::TopSitesBackend()
+ : db_(new TopSitesDatabase()) {
+}
+
+void TopSitesBackend::Init(const FilePath& path) {
+ db_path_ = path;
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this, &TopSitesBackend::InitDBOnDBThread, path));
+}
+
+void TopSitesBackend::Shutdown() {
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this, &TopSitesBackend::ShutdownDBOnDBThread));
+}
+
+TopSitesBackend::Handle TopSitesBackend::GetMostVisitedThumbnails(
+ CancelableRequestConsumerBase* consumer,
+ GetMostVisitedThumbnailsCallback* callback) {
+ GetMostVisitedThumbnailsRequest* request =
+ new GetMostVisitedThumbnailsRequest(callback);
+ request->value = new MostVisitedThumbnails;
+ AddRequest(request, consumer);
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this,
+ &TopSitesBackend::GetMostVisitedThumbnailsOnDBThread,
+ scoped_refptr<GetMostVisitedThumbnailsRequest>(request)));
+ return request->handle();
+}
+
+void TopSitesBackend::UpdateTopSites(const TopSitesDelta& delta) {
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this, &TopSitesBackend::UpdateTopSitesOnDBThread, delta));
+}
+
+void TopSitesBackend::SetPageThumbnail(const MostVisitedURL& url,
+ int url_rank,
+ const Images& thumbnail) {
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this, &TopSitesBackend::SetPageThumbnailOnDBThread, url,
+ url_rank, thumbnail));
+}
+
+void TopSitesBackend::ResetDatabase() {
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this, &TopSitesBackend::ResetDatabaseOnDBThread, db_path_));
+}
+
+TopSitesBackend::Handle TopSitesBackend::DoEmptyRequest(
+ CancelableRequestConsumerBase* consumer,
+ EmptyRequestCallback* callback) {
+ EmptyRequestRequest* request = new EmptyRequestRequest(callback);
+ AddRequest(request, consumer);
+ BrowserThread::PostTask(
+ BrowserThread::DB, FROM_HERE, NewRunnableMethod(
+ this,
+ &TopSitesBackend::DoEmptyRequestOnDBThread,
+ scoped_refptr<EmptyRequestRequest>(request)));
+ return request->handle();
+}
+
+TopSitesBackend::~TopSitesBackend() {
+ DCHECK(!db_.get()); // Shutdown should have happened first (which results in
+ // nulling out db).
+}
+
+void TopSitesBackend::InitDBOnDBThread(const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!db_->Init(path)) {
+ NOTREACHED() << "Failed to initialize database.";
+ db_.reset();
+ }
+}
+
+void TopSitesBackend::ShutdownDBOnDBThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_.reset();
+}
+
+void TopSitesBackend::GetMostVisitedThumbnailsOnDBThread(
+ scoped_refptr<GetMostVisitedThumbnailsRequest> request) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (request->canceled())
+ return;
+
+ bool may_need_history_migration = false;
+ if (db_.get()) {
+ db_->GetPageThumbnails(&(request->value->most_visited),
+ &(request->value->url_to_images_map));
+ may_need_history_migration = db_->may_need_history_migration();
+ }
+ request->ForwardResult(GetMostVisitedThumbnailsRequest::TupleType(
+ request->handle(),
+ request->value,
+ may_need_history_migration));
+}
+
+void TopSitesBackend::UpdateTopSitesOnDBThread(const TopSitesDelta& delta) {
+ if (!db_.get())
+ return;
+
+ for (size_t i = 0; i < delta.deleted.size(); ++i)
+ db_->RemoveURL(delta.deleted[i]);
+
+ for (size_t i = 0; i < delta.added.size(); ++i)
+ db_->SetPageThumbnail(delta.added[i].url, delta.added[i].rank, Images());
+
+ for (size_t i = 0; i < delta.moved.size(); ++i)
+ db_->UpdatePageRank(delta.moved[i].url, delta.moved[i].rank);
+}
+
+void TopSitesBackend::SetPageThumbnailOnDBThread(const MostVisitedURL& url,
+ int url_rank,
+ const Images& thumbnail) {
+ if (!db_.get())
+ return;
+
+ db_->SetPageThumbnail(url, url_rank, thumbnail);
+}
+
+void TopSitesBackend::ResetDatabaseOnDBThread(const FilePath& file_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_.reset(NULL);
+ file_util::Delete(db_path_, false);
+ db_.reset(new TopSitesDatabase());
+ InitDBOnDBThread(db_path_);
+}
+
+void TopSitesBackend::DoEmptyRequestOnDBThread(
+ scoped_refptr<EmptyRequestRequest> request) {
+ request->ForwardResult(EmptyRequestRequest::TupleType(request->handle()));
+}
+
+} // namespace history
diff --git a/chrome/browser/history/top_sites_backend.h b/chrome/browser/history/top_sites_backend.h
new file mode 100644
index 0000000..50d0867
--- /dev/null
+++ b/chrome/browser/history/top_sites_backend.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef CHROME_BROWSER_HISTORY_TOP_SITES_BACKEND_H_
+#define CHROME_BROWSER_HISTORY_TOP_SITES_BACKEND_H_
+#pragma once
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/history/history_types.h"
+
+class FilePath;
+
+namespace history {
+
+class TopSitesDatabase;
+
+// Service used by TopSites to have db interaction happen on the DB thread. All
+// public methods are invoked on the ui thread and get funneled to the DB
+// thread.
+class TopSitesBackend
+ : public base::RefCountedThreadSafe<TopSitesBackend>,
+ public CancelableRequestProvider {
+ public:
+ TopSitesBackend();
+
+ void Init(const FilePath& path);
+
+ // Schedules the db to be shutdown.
+ void Shutdown();
+
+ // The boolean parameter indicates if the DB existed on disk or needs to be
+ // migrated.
+ typedef Callback3<Handle, scoped_refptr<MostVisitedThumbnails>, bool >::Type
+ GetMostVisitedThumbnailsCallback;
+ typedef CancelableRequest1<TopSitesBackend::GetMostVisitedThumbnailsCallback,
+ scoped_refptr<MostVisitedThumbnails> >
+ GetMostVisitedThumbnailsRequest;
+
+ // Fetches MostVisitedThumbnails.
+ Handle GetMostVisitedThumbnails(CancelableRequestConsumerBase* consumer,
+ GetMostVisitedThumbnailsCallback* callback);
+
+ // Updates top sites database from the specified delta.
+ void UpdateTopSites(const TopSitesDelta& delta);
+
+ // Sets the thumbnail.
+ void SetPageThumbnail(const MostVisitedURL& url,
+ int url_rank,
+ const Images& thumbnail);
+
+ // Deletes the database and recreates it.
+ void ResetDatabase();
+
+ typedef Callback1<Handle>::Type EmptyRequestCallback;
+ typedef CancelableRequest<TopSitesBackend::EmptyRequestCallback>
+ EmptyRequestRequest;
+
+ // Schedules a request that does nothing on the DB thread, but then notifies
+ // the callback on the calling thread. This is used to make sure the db has
+ // finished processing a request.
+ Handle DoEmptyRequest(CancelableRequestConsumerBase* consumer,
+ EmptyRequestCallback* callback);
+
+ private:
+ friend class base::RefCountedThreadSafe<TopSitesBackend>;
+
+ ~TopSitesBackend();
+
+ // Invokes Init on the db_.
+ void InitDBOnDBThread(const FilePath& path);
+
+ // Shuts down the db.
+ void ShutdownDBOnDBThread();
+
+ // Does the work of getting the most visted thumbnails.
+ void GetMostVisitedThumbnailsOnDBThread(
+ scoped_refptr<GetMostVisitedThumbnailsRequest> request);
+
+ // Updates top sites.
+ void UpdateTopSitesOnDBThread(const TopSitesDelta& delta);
+
+ // Sets the thumbnail.
+ void SetPageThumbnailOnDBThread(const MostVisitedURL& url,
+ int url_rank,
+ const Images& thumbnail);
+
+ // Resets the database.
+ void ResetDatabaseOnDBThread(const FilePath& file_path);
+
+ // Notifies the request.
+ void DoEmptyRequestOnDBThread(scoped_refptr<EmptyRequestRequest> request);
+
+ FilePath db_path_;
+
+ scoped_ptr<TopSitesDatabase> db_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesBackend);
+};
+
+} // namespace history
+
+#endif // CHROME_BROWSER_HISTORY_TOP_SITES_BACKEND_H_
diff --git a/chrome/browser/history/top_sites_cache.cc b/chrome/browser/history/top_sites_cache.cc
new file mode 100644
index 0000000..828b701
--- /dev/null
+++ b/chrome/browser/history/top_sites_cache.cc
@@ -0,0 +1,110 @@
+// 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.
+
+#include "chrome/browser/history/top_sites_cache.h"
+
+#include "base/logging.h"
+#include "base/ref_counted_memory.h"
+
+namespace history {
+
+TopSitesCache::TopSitesCache() {
+}
+
+TopSitesCache::~TopSitesCache() {
+}
+
+void TopSitesCache::SetTopSites(const MostVisitedURLList& top_sites) {
+ top_sites_ = top_sites;
+ GenerateCanonicalURLs();
+}
+
+void TopSitesCache::SetThumbnails(const URLToImagesMap& images) {
+ images_ = images;
+}
+
+void TopSitesCache::SetPageThumbnail(const GURL& url,
+ RefCountedBytes* thumbnail,
+ const ThumbnailScore& score) {
+ Images& img = images_[GetCanonicalURL(url)];
+ img.thumbnail = thumbnail;
+ img.thumbnail_score = score;
+}
+
+Images* TopSitesCache::GetImage(const GURL& url) {
+ return &images_[GetCanonicalURL(url)];
+}
+
+bool TopSitesCache::GetPageThumbnail(const GURL& url,
+ scoped_refptr<RefCountedBytes>* bytes) {
+ std::map<GURL, Images>::const_iterator found =
+ images_.find(GetCanonicalURL(url));
+ if (found != images_.end()) {
+ *bytes = found->second.thumbnail.get();
+ return true;
+ }
+ return false;
+}
+
+GURL TopSitesCache::GetCanonicalURL(const GURL& url) {
+ CanonicalURLs::iterator i = TopSitesCache::GetCanonicalURLsIterator(url);
+ return i == canonical_urls_.end() ? url : i->first.first->url;
+}
+
+bool TopSitesCache::IsKnownURL(const GURL& url) {
+ return GetCanonicalURLsIterator(url) != canonical_urls_.end();
+}
+
+size_t TopSitesCache::GetURLIndex(const GURL& url) {
+ DCHECK(IsKnownURL(url));
+ return GetCanonicalURLsIterator(url)->second;
+}
+
+void TopSitesCache::RemoveUnreferencedThumbnails() {
+ for (URLToImagesMap::iterator i = images_.begin(); i != images_.end(); ) {
+ if (IsKnownURL(i->first)) {
+ ++i;
+ } else {
+ URLToImagesMap::iterator next_i = i;
+ ++next_i;
+ images_.erase(i);
+ i = next_i;
+ }
+ }
+}
+
+void TopSitesCache::GenerateCanonicalURLs() {
+ canonical_urls_.clear();
+ for (size_t i = 0; i < top_sites_.size(); i++)
+ StoreRedirectChain(top_sites_[i].redirects, i);
+}
+
+void TopSitesCache::StoreRedirectChain(const RedirectList& redirects,
+ size_t destination) {
+ // redirects is empty if the user pinned a site and there are not enough top
+ // sites before the pinned site.
+
+ // Map all the redirected URLs to the destination.
+ for (size_t i = 0; i < redirects.size(); i++) {
+ // If this redirect is already known, don't replace it with a new one.
+ if (!IsKnownURL(redirects[i])) {
+ CanonicalURLEntry entry;
+ entry.first = &(top_sites_[destination]);
+ entry.second = i;
+ canonical_urls_[entry] = destination;
+ }
+ }
+}
+
+TopSitesCache::CanonicalURLs::iterator TopSitesCache::GetCanonicalURLsIterator(
+ const GURL& url) {
+ MostVisitedURL most_visited_url;
+ most_visited_url.redirects.push_back(url);
+ CanonicalURLEntry entry;
+ entry.first = &most_visited_url;
+ entry.second = 0u;
+ return canonical_urls_.find(entry);
+}
+
+} // namespace history
diff --git a/chrome/browser/history/top_sites_cache.h b/chrome/browser/history/top_sites_cache.h
new file mode 100644
index 0000000..4c5d79a
--- /dev/null
+++ b/chrome/browser/history/top_sites_cache.h
@@ -0,0 +1,108 @@
+// 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.
+
+#ifndef CHROME_BROWSER_HISTORY_TOP_SITES_CACHE_H_
+#define CHROME_BROWSER_HISTORY_TOP_SITES_CACHE_H_
+#pragma once
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "base/ref_counted.h"
+#include "chrome/browser/history/history_types.h"
+
+class RefCountedBytes;
+
+namespace history {
+
+// TopSitesCache caches the top sites and thumbnails for TopSites.
+class TopSitesCache {
+ public:
+ TopSitesCache();
+ ~TopSitesCache();
+
+ // The top sites.
+ void SetTopSites(const MostVisitedURLList& top_sites);
+ const MostVisitedURLList& top_sites() const { return top_sites_; }
+
+ // The thumbnails.
+ void SetThumbnails(const URLToImagesMap& images);
+ const URLToImagesMap& images() const { return images_; }
+
+ // Set a thumbnail.
+ void SetPageThumbnail(const GURL& url,
+ RefCountedBytes* thumbnail,
+ const ThumbnailScore& score);
+
+ // Returns the thumbnail as an Image for the specified url. This adds an entry
+ // for |url| if one has not yet been added.
+ Images* GetImage(const GURL& url);
+
+ // Fetches the thumbnail for the specified url. Returns true if there is a
+ // thumbnail for the specified url.
+ bool GetPageThumbnail(const GURL& url,
+ scoped_refptr<RefCountedBytes>* bytes);
+
+ // Returns the canonical URL for |url|.
+ GURL GetCanonicalURL(const GURL& url);
+
+ // Returns true if |url| is known.
+ bool IsKnownURL(const GURL& url);
+
+ // Returns the index into |top_sites_| for |url|.
+ size_t GetURLIndex(const GURL& url);
+
+ // Removes any thumbnails that are no longer referenced by the top sites.
+ void RemoveUnreferencedThumbnails();
+
+ private:
+ // The entries in CanonicalURLs, see CanonicalURLs for details. The second
+ // argument gives the index of the URL into MostVisitedURLs redirects.
+ typedef std::pair<MostVisitedURL*, size_t> CanonicalURLEntry;
+
+ // Comparator used for CanonicalURLs.
+ class CanonicalURLComparator {
+ public:
+ bool operator()(const CanonicalURLEntry& e1,
+ const CanonicalURLEntry& e2) const {
+ return e1.first->redirects[e1.second] < e2.first->redirects[e2.second];
+ }
+ };
+
+ // This is used to map from redirect url to the MostVisitedURL the redirect is
+ // from. Ideally this would be map<GURL, size_t> (second param indexing into
+ // top_sites_), but this results in duplicating all redirect urls. As some
+ // sites have a lot of redirects, we instead use the MostVisitedURL* and the
+ // index of the redirect as the key, and the index into top_sites_ as the
+ // value. This way we aren't duplicating GURLs. CanonicalURLComparator
+ // enforces the ordering as if we were using GURLs.
+ typedef std::map<CanonicalURLEntry, size_t,
+ CanonicalURLComparator> CanonicalURLs;
+
+ // Generates the set of canonical urls from |top_sites_|.
+ void GenerateCanonicalURLs();
+
+ // Stores a set of redirects. This is used by GenerateCanonicalURLs.
+ void StoreRedirectChain(const RedirectList& redirects, size_t destination);
+
+ // Returns the iterator into canconical_urls_ for the specified url.
+ CanonicalURLs::iterator GetCanonicalURLsIterator(const GURL& url);
+
+ // The top sites.
+ MostVisitedURLList top_sites_;
+
+ // The images. These map from canonical url to image.
+ URLToImagesMap images_;
+
+ // Generated from the redirects to and from the most visited pages. See
+ // description above typedef for details.
+ CanonicalURLs canonical_urls_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesCache);
+};
+
+} // namespace history
+
+#endif // CHROME_BROWSER_HISTORY_TOP_SITES_CACHE_H_
diff --git a/chrome/browser/history/top_sites_database.cc b/chrome/browser/history/top_sites_database.cc
index 05a588a..8bfbcaf 100644
--- a/chrome/browser/history/top_sites_database.cc
+++ b/chrome/browser/history/top_sites_database.cc
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "app/sql/connection.h"
#include "app/sql/transaction.h"
+#include "base/file_util.h"
#include "base/string_util.h"
#include "chrome/browser/diagnostics/sqlite_diagnostics.h"
#include "chrome/browser/history/history_types.h"
@@ -11,56 +13,85 @@
namespace history {
-TopSitesDatabaseImpl::TopSitesDatabaseImpl() {
+static const int kVersionNumber = 1;
+
+TopSitesDatabase::TopSitesDatabase() : may_need_history_migration_(false) {
}
-TopSitesDatabaseImpl::~TopSitesDatabaseImpl() {
+TopSitesDatabase::~TopSitesDatabase() {
}
-bool TopSitesDatabaseImpl::Init(const FilePath& db_name) {
- // Settings copied from ThumbnailDatabase.
- db_.set_error_delegate(GetErrorHandlerForThumbnailDb());
- db_.set_page_size(4096);
- db_.set_cache_size(64);
+bool TopSitesDatabase::Init(const FilePath& db_name) {
+ bool file_existed = file_util::PathExists(db_name);
+
+ if (!file_existed)
+ may_need_history_migration_ = true;
- if (!db_.Open(db_name)) {
- LOG(WARNING) << db_.GetErrorMessage();
+ db_.reset(CreateDB(db_name));
+ if (!db_.get())
return false;
+
+ bool does_meta_exist = sql::MetaTable::DoesTableExist(db_.get());
+ if (!does_meta_exist && file_existed) {
+ may_need_history_migration_ = true;
+
+ // If the meta file doesn't exist, this version is old. We could remove all
+ // the entries as they are no longer applicable, but it's safest to just
+ // remove the file and start over.
+ db_.reset(NULL);
+ if (!file_util::Delete(db_name, false) &&
+ !file_util::Delete(db_name, false)) {
+ // Try to delete twice. If we can't, fail.
+ LOG(ERROR) << "unable to delete old TopSites file";
+ return false;
+ }
+ db_.reset(CreateDB(db_name));
+ if (!db_.get())
+ return false;
}
- return InitThumbnailTable();
+ if (!meta_table_.Init(db_.get(), kVersionNumber, kVersionNumber))
+ return false;
+
+ if (!InitThumbnailTable())
+ return false;
+
+ // Version check.
+ if (meta_table_.GetVersionNumber() != kVersionNumber)
+ return false;
+
+ return true;
}
-bool TopSitesDatabaseImpl::InitThumbnailTable() {
- if (!db_.DoesTableExist("thumbnails")) {
- if (!db_.Execute("CREATE TABLE thumbnails ("
- "url LONGVARCHAR PRIMARY KEY,"
- "url_rank INTEGER ,"
- "title LONGVARCHAR,"
- "thumbnail BLOB,"
- "redirects LONGVARCHAR,"
- "boring_score DOUBLE DEFAULT 1.0, "
- "good_clipping INTEGER DEFAULT 0, "
- "at_top INTEGER DEFAULT 0, "
- "last_updated INTEGER DEFAULT 0) ")) {
- LOG(WARNING) << db_.GetErrorMessage();
+bool TopSitesDatabase::InitThumbnailTable() {
+ if (!db_->DoesTableExist("thumbnails")) {
+ if (!db_->Execute("CREATE TABLE thumbnails ("
+ "url LONGVARCHAR PRIMARY KEY,"
+ "url_rank INTEGER ,"
+ "title LONGVARCHAR,"
+ "thumbnail BLOB,"
+ "redirects LONGVARCHAR,"
+ "boring_score DOUBLE DEFAULT 1.0, "
+ "good_clipping INTEGER DEFAULT 0, "
+ "at_top INTEGER DEFAULT 0, "
+ "last_updated INTEGER DEFAULT 0) ")) {
+ LOG(WARNING) << db_->GetErrorMessage();
return false;
}
}
return true;
}
-void TopSitesDatabaseImpl::GetPageThumbnails(MostVisitedURLList* urls,
- std::map<GURL,
- Images>* thumbnails) {
- sql::Statement statement(db_.GetCachedStatement(
+void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls,
+ URLToImagesMap* thumbnails) {
+ sql::Statement statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT url, url_rank, title, thumbnail, redirects, "
"boring_score, good_clipping, at_top, last_updated "
"FROM thumbnails ORDER BY url_rank "));
if (!statement) {
- LOG(WARNING) << db_.GetErrorMessage();
+ LOG(WARNING) << db_->GetErrorMessage();
return;
}
@@ -92,7 +123,7 @@ void TopSitesDatabaseImpl::GetPageThumbnails(MostVisitedURLList* urls,
}
// static
-std::string TopSitesDatabaseImpl::GetRedirects(const MostVisitedURL& url) {
+std::string TopSitesDatabase::GetRedirects(const MostVisitedURL& url) {
std::vector<std::string> redirects;
for (size_t i = 0; i < url.redirects.size(); i++)
redirects.push_back(url.redirects[i].spec());
@@ -100,18 +131,18 @@ std::string TopSitesDatabaseImpl::GetRedirects(const MostVisitedURL& url) {
}
// static
-void TopSitesDatabaseImpl::SetRedirects(const std::string& redirects,
- MostVisitedURL* url) {
+void TopSitesDatabase::SetRedirects(const std::string& redirects,
+ MostVisitedURL* url) {
std::vector<std::string> redirects_vector;
SplitStringAlongWhitespace(redirects, &redirects_vector);
for (size_t i = 0; i < redirects_vector.size(); i++)
url->redirects.push_back(GURL(redirects_vector[i]));
}
-void TopSitesDatabaseImpl::SetPageThumbnail(const MostVisitedURL& url,
+void TopSitesDatabase::SetPageThumbnail(const MostVisitedURL& url,
int new_rank,
const Images& thumbnail) {
- sql::Transaction transaction(&db_);
+ sql::Transaction transaction(db_.get());
transaction.Begin();
int rank = GetURLRank(url);
@@ -125,9 +156,9 @@ void TopSitesDatabaseImpl::SetPageThumbnail(const MostVisitedURL& url,
transaction.Commit();
}
-void TopSitesDatabaseImpl::UpdatePageThumbnail(
+void TopSitesDatabase::UpdatePageThumbnail(
const MostVisitedURL& url, const Images& thumbnail) {
- sql::Statement statement(db_.GetCachedStatement(
+ sql::Statement statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE thumbnails SET "
"title = ?, thumbnail = ?, redirects = ?, "
@@ -137,9 +168,9 @@ void TopSitesDatabaseImpl::UpdatePageThumbnail(
return;
statement.BindString16(0, url.title);
- if (thumbnail.thumbnail.get()) {
- statement.BindBlob(1, &thumbnail.thumbnail->data.front(),
- static_cast<int>(thumbnail.thumbnail->data.size()));
+ if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
+ statement.BindBlob(1, thumbnail.thumbnail->front(),
+ static_cast<int>(thumbnail.thumbnail->size()));
}
statement.BindString(2, GetRedirects(url));
const ThumbnailScore& score = thumbnail.thumbnail_score;
@@ -149,15 +180,15 @@ void TopSitesDatabaseImpl::UpdatePageThumbnail(
statement.BindInt64(6, score.time_at_snapshot.ToInternalValue());
statement.BindString(7, url.url.spec());
if (!statement.Run())
- NOTREACHED() << db_.GetErrorMessage();
+ NOTREACHED() << db_->GetErrorMessage();
}
-void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url,
+void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url,
int new_rank,
const Images& thumbnail) {
int count = GetRowCount();
- sql::Statement statement(db_.GetCachedStatement(
+ sql::Statement statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"INSERT OR REPLACE INTO thumbnails "
"(url, url_rank, title, thumbnail, redirects, "
@@ -169,9 +200,9 @@ void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url,
statement.BindString(0, url.url.spec());
statement.BindInt(1, count); // Make it the last url.
statement.BindString16(2, url.title);
- if (thumbnail.thumbnail.get()) {
- statement.BindBlob(3, &thumbnail.thumbnail->data.front(),
- static_cast<int>(thumbnail.thumbnail->data.size()));
+ if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
+ statement.BindBlob(3, thumbnail.thumbnail->front(),
+ static_cast<int>(thumbnail.thumbnail->size()));
}
statement.BindString(4, GetRedirects(url));
const ThumbnailScore& score = thumbnail.thumbnail_score;
@@ -180,21 +211,21 @@ void TopSitesDatabaseImpl::AddPageThumbnail(const MostVisitedURL& url,
statement.BindBool(7, score.at_top);
statement.BindInt64(8, score.time_at_snapshot.ToInternalValue());
if (!statement.Run())
- NOTREACHED() << db_.GetErrorMessage();
+ NOTREACHED() << db_->GetErrorMessage();
UpdatePageRankNoTransaction(url, new_rank);
}
-void TopSitesDatabaseImpl::UpdatePageRank(const MostVisitedURL& url,
+void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url,
int new_rank) {
- sql::Transaction transaction(&db_);
+ sql::Transaction transaction(db_.get());
transaction.Begin();
UpdatePageRankNoTransaction(url, new_rank);
transaction.Commit();
}
// Caller should have a transaction open.
-void TopSitesDatabaseImpl::UpdatePageRankNoTransaction(
+void TopSitesDatabase::UpdatePageRankNoTransaction(
const MostVisitedURL& url, int new_rank) {
int prev_rank = GetURLRank(url);
if (prev_rank == -1) {
@@ -205,7 +236,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction(
// Shift the ranks.
if (prev_rank > new_rank) {
// Shift up
- sql::Statement shift_statement(db_.GetCachedStatement(
+ sql::Statement shift_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE thumbnails "
"SET url_rank = url_rank + 1 "
@@ -216,7 +247,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction(
shift_statement.Run();
} else if (prev_rank < new_rank) {
// Shift down
- sql::Statement shift_statement(db_.GetCachedStatement(
+ sql::Statement shift_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE thumbnails "
"SET url_rank = url_rank - 1 "
@@ -228,7 +259,7 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction(
}
// Set the url's rank.
- sql::Statement set_statement(db_.GetCachedStatement(
+ sql::Statement set_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE thumbnails "
"SET url_rank = ? "
@@ -239,15 +270,15 @@ void TopSitesDatabaseImpl::UpdatePageRankNoTransaction(
set_statement.Run();
}
-bool TopSitesDatabaseImpl::GetPageThumbnail(const GURL& url,
+bool TopSitesDatabase::GetPageThumbnail(const GURL& url,
Images* thumbnail) {
- sql::Statement statement(db_.GetCachedStatement(
+ sql::Statement statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT thumbnail, boring_score, good_clipping, at_top, last_updated "
"FROM thumbnails WHERE url=?"));
if (!statement) {
- LOG(WARNING) << db_.GetErrorMessage();
+ LOG(WARNING) << db_->GetErrorMessage();
return false;
}
@@ -266,13 +297,13 @@ bool TopSitesDatabaseImpl::GetPageThumbnail(const GURL& url,
return true;
}
-int TopSitesDatabaseImpl::GetRowCount() {
+int TopSitesDatabase::GetRowCount() {
int result = 0;
- sql::Statement select_statement(db_.GetCachedStatement(
+ sql::Statement select_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT COUNT (url) FROM thumbnails"));
if (!select_statement) {
- LOG(WARNING) << db_.GetErrorMessage();
+ LOG(WARNING) << db_->GetErrorMessage();
return result;
}
@@ -282,14 +313,14 @@ int TopSitesDatabaseImpl::GetRowCount() {
return result;
}
-int TopSitesDatabaseImpl::GetURLRank(const MostVisitedURL& url) {
+int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) {
int result = -1;
- sql::Statement select_statement(db_.GetCachedStatement(
+ sql::Statement select_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT url_rank "
"FROM thumbnails WHERE url=?"));
if (!select_statement) {
- LOG(WARNING) << db_.GetErrorMessage();
+ LOG(WARNING) << db_->GetErrorMessage();
return result;
}
@@ -301,15 +332,15 @@ int TopSitesDatabaseImpl::GetURLRank(const MostVisitedURL& url) {
}
// Remove the record for this URL. Returns true iff removed successfully.
-bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) {
+bool TopSitesDatabase::RemoveURL(const MostVisitedURL& url) {
int old_rank = GetURLRank(url);
if (old_rank < 0)
return false;
- sql::Transaction transaction(&db_);
+ sql::Transaction transaction(db_.get());
transaction.Begin();
// Decrement all following ranks.
- sql::Statement shift_statement(db_.GetCachedStatement(
+ sql::Statement shift_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE thumbnails "
"SET url_rank = url_rank - 1 "
@@ -320,8 +351,8 @@ bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) {
shift_statement.Run();
sql::Statement delete_statement(
- db_.GetCachedStatement(SQL_FROM_HERE,
- "DELETE FROM thumbnails WHERE url = ?"));
+ db_->GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM thumbnails WHERE url = ?"));
if (!delete_statement)
return false;
delete_statement.BindString(0, url.url.spec());
@@ -330,4 +361,19 @@ bool TopSitesDatabaseImpl::RemoveURL(const MostVisitedURL& url) {
return transaction.Commit();
}
+sql::Connection* TopSitesDatabase::CreateDB(const FilePath& db_name) {
+ scoped_ptr<sql::Connection> db(new sql::Connection());
+ // Settings copied from ThumbnailDatabase.
+ db->set_error_delegate(GetErrorHandlerForThumbnailDb());
+ db->set_page_size(4096);
+ db->set_cache_size(32);
+
+ if (!db->Open(db_name)) {
+ LOG(ERROR) << db->GetErrorMessage();
+ return NULL;
+ }
+
+ return db.release();
+}
+
} // namespace history
diff --git a/chrome/browser/history/top_sites_database.h b/chrome/browser/history/top_sites_database.h
index edfb4e5..8450889 100644
--- a/chrome/browser/history/top_sites_database.h
+++ b/chrome/browser/history/top_sites_database.h
@@ -9,94 +9,61 @@
#include <map>
#include <string>
-#include "app/sql/connection.h"
+#include "app/sql/meta_table.h"
#include "base/ref_counted.h"
+#include "chrome/browser/history/history_types.h"
#include "chrome/browser/history/url_database.h" // For DBCloseScoper.
class FilePath;
class RefCountedMemory;
class SkBitmap;
-class Images;
-namespace base {
-class Time;
+namespace app {
+class Connection;
}
namespace history {
-// Interface to be implemented by the real storage layer as well as
-// the mockup database for testing.
class TopSitesDatabase {
public:
- virtual ~TopSitesDatabase() {}
- virtual bool Init(const FilePath& filename) {
- return true;
- }
-
- // Returns a list of all URLs currently in the table.
- virtual void GetPageThumbnails(MostVisitedURLList* urls,
- std::map<GURL,
- Images>* thumbnails) = 0;
-
- // Set a thumbnail for a URL. |url_rank| is the position of the URL
- // in the list of TopURLs, zero-based.
- // If the URL is not in the table, add it. If it is, replace its
- // thumbnail.
- virtual void SetPageThumbnail(const MostVisitedURL& url,
- int url_rank,
- const Images& thumbnail) = 0;
-
- // Update rank of a URL that's already in the database.
- virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank) = 0;
-
- // Convenience wrapper.
- bool GetPageThumbnail(const MostVisitedURL& url,
- Images* thumbnail) {
- return GetPageThumbnail(url.url, thumbnail);
- }
-
- // Get a thumbnail for a given page. Returns true iff we have the thumbnail.
- virtual bool GetPageThumbnail(const GURL& url,
- Images* thumbnail) = 0;
-
- // Remove the record for this URL. Returns true iff removed successfully.
- virtual bool RemoveURL(const MostVisitedURL& url) = 0;
-};
-
-class TopSitesDatabaseImpl : public TopSitesDatabase {
- public:
- TopSitesDatabaseImpl();
- virtual ~TopSitesDatabaseImpl();
+ TopSitesDatabase();
+ ~TopSitesDatabase();
// Must be called after creation but before any other methods are called.
// Returns true on success. If false, no other functions should be called.
- virtual bool Init(const FilePath& db_name);
+ bool Init(const FilePath& db_name);
+
+ // Returns true if migration of top sites from history may be needed. A value
+ // of true means either migration is definitely needed (the top sites file is
+ // old) or doesn't exist (as would happen for a new user).
+ bool may_need_history_migration() const {
+ return may_need_history_migration_;
+ }
// Thumbnails ----------------------------------------------------------------
// Returns a list of all URLs currently in the table.
// WARNING: clears both input arguments.
- virtual void GetPageThumbnails(MostVisitedURLList* urls,
- std::map<GURL, Images>* thumbnails);
+ void GetPageThumbnails(MostVisitedURLList* urls,
+ std::map<GURL, Images>* thumbnails);
// Set a thumbnail for a URL. |url_rank| is the position of the URL
// in the list of TopURLs, zero-based.
// If the URL is not in the table, add it. If it is, replace its
// thumbnail and rank. Shift the ranks of other URLs if necessary.
- virtual void SetPageThumbnail(const MostVisitedURL& url,
- int new_rank,
- const Images& thumbnail);
+ void SetPageThumbnail(const MostVisitedURL& url,
+ int new_rank,
+ const Images& thumbnail);
// Sets the rank for a given URL. The URL must be in the database.
// Use SetPageThumbnail if it's not.
- virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank);
+ void UpdatePageRank(const MostVisitedURL& url, int new_rank);
// Get a thumbnail for a given page. Returns true iff we have the thumbnail.
- virtual bool GetPageThumbnail(const GURL& url,
- Images* thumbnail);
+ bool GetPageThumbnail(const GURL& url, Images* thumbnail);
// Remove the record for this URL. Returns true iff removed successfully.
- virtual bool RemoveURL(const MostVisitedURL& url);
+ bool RemoveURL(const MostVisitedURL& url);
private:
// Creates the thumbnail table, returning true if the table already exists
@@ -121,13 +88,21 @@ class TopSitesDatabaseImpl : public TopSitesDatabase {
// Returns the number of URLs (rows) in the database.
int GetRowCount();
+ sql::Connection* CreateDB(const FilePath& db_name);
+
// Encodes redirects into a string.
static std::string GetRedirects(const MostVisitedURL& url);
// Decodes redirects from a string and sets them for the url.
static void SetRedirects(const std::string& redirects, MostVisitedURL* url);
- sql::Connection db_;
+ scoped_ptr<sql::Connection> db_;
+ sql::MetaTable meta_table_;
+
+ // See description above class.
+ bool may_need_history_migration_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesDatabase);
};
} // namespace history
diff --git a/chrome/browser/history/top_sites_unittest.cc b/chrome/browser/history/top_sites_unittest.cc
index 160b275..f908646 100644
--- a/chrome/browser/history/top_sites_unittest.cc
+++ b/chrome/browser/history/top_sites_unittest.cc
@@ -3,18 +3,26 @@
// found in the LICENSE file.
#include "app/l10n_util.h"
+#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_thread.h"
-#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/dom_ui/most_visited_handler.h"
+#include "chrome/browser/history/history_backend.h"
+#include "chrome/browser/history/history_database.h"
#include "chrome/browser/history/history_marshaling.h"
-#include "chrome/browser/history/top_sites_database.h"
#include "chrome/browser/history/history_notifications.h"
+#include "chrome/browser/history/top_sites.h"
+#include "chrome/browser/history/top_sites_backend.h"
+#include "chrome/browser/history/top_sites_cache.h"
+#include "chrome/browser/history/top_sites_database.h"
+#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/test/testing_profile.h"
#include "chrome/tools/profiles/thumbnail-inl.h"
#include "gfx/codec/jpeg_codec.h"
@@ -25,246 +33,362 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
-
namespace history {
-static const unsigned char kBlob[] =
- "12346102356120394751634516591348710478123649165419234519234512349134";
+namespace {
-class TopSitesTest : public testing::Test {
+// Used by WaitForHistory, see it for details.
+class WaitForHistoryTask : public HistoryDBTask {
public:
- TopSitesTest() : number_of_callbacks_(0) {
+ WaitForHistoryTask() {}
+
+ virtual bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) {
+ return true;
}
- ~TopSitesTest() {
+
+ virtual void DoneRunOnMainThread() {
+ MessageLoop::current()->Quit();
}
- TopSites& top_sites() { return *top_sites_; }
- MostVisitedURLList& urls() { return urls_; }
- Profile& profile() {return *profile_;}
- FilePath& file_name() { return file_name_; }
- RefCountedBytes* google_thumbnail() { return google_thumbnail_; }
- RefCountedBytes* random_thumbnail() { return random_thumbnail_; }
- RefCountedBytes* weewar_thumbnail() { return weewar_thumbnail_; }
- CancelableRequestConsumer* consumer() { return &consumer_; }
- size_t number_of_callbacks() {return number_of_callbacks_; }
- // Prepopulated URLs - added at the back of TopSites.
- GURL welcome_url() {
- return GURL(l10n_util::GetStringUTF8(IDS_CHROME_WELCOME_URL));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WaitForHistoryTask);
+};
+
+// Used for querying top sites. Either runs sequentially, or runs a nested
+// nested message loop until the response is complete. The later is used when
+// TopSites is queried before it finishes loading.
+class TopSitesQuerier {
+ public:
+ TopSitesQuerier() : number_of_callbacks_(0), waiting_(false) {}
+
+ // Queries top sites. If |wait| is true a nested message loop is run until the
+ // callback is notified.
+ void QueryTopSites(TopSites* top_sites, bool wait) {
+ int start_number_of_callbacks = number_of_callbacks_;
+ top_sites->GetMostVisitedURLs(
+ &consumer_,
+ NewCallback(this, &TopSitesQuerier::OnTopSitesAvailable));
+ if (wait && start_number_of_callbacks == number_of_callbacks_) {
+ waiting_ = true;
+ MessageLoop::current()->Run();
+ }
}
- GURL themes_url() {
- return GURL(l10n_util::GetStringUTF8(IDS_THEMES_GALLERY_URL));
+
+ void CancelRequest() {
+ consumer_.CancelAllRequests();
+ }
+
+ void set_urls(const MostVisitedURLList& urls) { urls_ = urls; }
+ const MostVisitedURLList& urls() const { return urls_; }
+
+ int number_of_callbacks() const { return number_of_callbacks_; }
+
+ private:
+ // Callback for TopSites::GetMostVisitedURLs.
+ void OnTopSitesAvailable(const history::MostVisitedURLList& data) {
+ urls_ = data;
+ number_of_callbacks_++;
+ if (waiting_) {
+ MessageLoop::current()->Quit();
+ waiting_ = false;
+ }
+ }
+
+ CancelableRequestConsumer consumer_;
+ MostVisitedURLList urls_;
+ int number_of_callbacks_;
+ bool waiting_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesQuerier);
+};
+
+// Extracts the data from |t1| into a SkBitmap. This is intended for usage of
+// thumbnail data, which is stored as jpgs.
+SkBitmap ExtractThumbnail(const RefCountedBytes& t1) {
+ scoped_ptr<SkBitmap> image(gfx::JPEGCodec::Decode(t1.front(),
+ t1.data.size()));
+ return image.get() ? *image : SkBitmap();
+}
+
+// Returns true if t1 and t2 contain the same data.
+bool ThumbnailsAreEqual(RefCountedBytes* t1, RefCountedBytes* t2) {
+ if (!t1 || !t2)
+ return false;
+ if (t1->data.size() != t2->data.size())
+ return false;
+ return std::equal(t1->data.begin(),
+ t1->data.end(),
+ t2->data.begin());
+}
+
+} // namespace
+
+class TopSitesTest : public testing::Test {
+ public:
+ TopSitesTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB, &message_loop_),
+ original_command_line_(*CommandLine::ForCurrentProcess()) {
}
virtual void SetUp() {
+ CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTopSites);
profile_.reset(new TestingProfile);
- top_sites_ = new TopSites(profile_.get());
-
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- file_name_ = temp_dir_.path().AppendASCII("TopSites.db");
- EXPECT_TRUE(file_util::Delete(file_name_, false));
-
- std::vector<unsigned char> random_data(kBlob, kBlob + sizeof(kBlob));
- std::vector<unsigned char> google_data(kGoogleThumbnail,
- kGoogleThumbnail +
- sizeof(kGoogleThumbnail));
- std::vector<unsigned char> weewar_data(kWeewarThumbnail,
- kWeewarThumbnail +
- sizeof(kWeewarThumbnail));
- random_thumbnail_ = new RefCountedBytes(random_data);
- google_thumbnail_ = new RefCountedBytes(google_data);
- weewar_thumbnail_ = new RefCountedBytes(weewar_data);
+ if (CreateHistoryAndTopSites()) {
+ profile_->CreateHistoryService(false, false);
+ profile_->CreateTopSites();
+ profile_->BlockUntilTopSitesLoaded();
+ }
}
virtual void TearDown() {
profile_.reset();
- TopSites::DeleteTopSites(top_sites_);
- EXPECT_TRUE(file_util::Delete(file_name_, false));
+ *CommandLine::ForCurrentProcess() = original_command_line_;
}
- // Callback for TopSites::GetMostVisitedURLs.
- void OnTopSitesAvailable(history::MostVisitedURLList data) {
- urls_ = data;
- number_of_callbacks_++;
+ // Returns true if history and top sites should be created in SetUp.
+ virtual bool CreateHistoryAndTopSites() {
+ return true;
}
- // Wrappers that allow private TopSites functions to be called from the
- // individual tests without making them all be friends.
- GURL GetCanonicalURL(const GURL& url) const {
- AutoLock lock(top_sites_->lock_); // The function asserts it's in the lock.
- return top_sites_->GetCanonicalURL(url);
+ // Gets the thumbnail for |url| from TopSites.
+ SkBitmap GetThumbnail(const GURL& url) {
+ scoped_refptr<RefCountedBytes> data;
+ return top_sites()->GetPageThumbnail(url, &data) ?
+ ExtractThumbnail(*data.get()) : SkBitmap();
}
- void StoreMostVisited(std::vector<MostVisitedURL>* urls) {
- top_sites_->StoreMostVisited(urls);
+ // Creates a bitmap of the specified color.
+ SkBitmap CreateBitmap(SkColor color) {
+ SkBitmap thumbnail;
+ thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
+ thumbnail.allocPixels();
+ thumbnail.eraseColor(color);
+ return thumbnail;
}
- static void DiffMostVisited(const std::vector<MostVisitedURL>& old_list,
- const std::vector<MostVisitedURL>& new_list,
- std::vector<size_t>* added_urls,
- std::vector<size_t>* deleted_urls,
- std::vector<size_t>* moved_urls) {
- TopSites::DiffMostVisited(old_list, new_list,
- added_urls, deleted_urls, moved_urls);
+ // Forces top sites to load top sites from history, then recreates top sites.
+ // Recreating top sites makes sure the changes from history are saved and
+ // loaded from the db.
+ void RefreshTopSitesAndRecreate() {
+ StartQueryForMostVisited();
+ WaitForHistory();
+ RecreateTopSitesAndBlock();
}
- Lock& lock() {
- return top_sites_->lock_;
+ // Blocks the caller until history processes a task. This is useful if you
+ // need to wait until you know history has processed a task.
+ void WaitForHistory() {
+ history_service()->ScheduleDBTask(new WaitForHistoryTask(), &consumer_);
+ MessageLoop::current()->Run();
}
- private:
- scoped_refptr<TopSites> top_sites_;
- MostVisitedURLList urls_;
- size_t number_of_callbacks_;
- scoped_ptr<TestingProfile> profile_;
- ScopedTempDir temp_dir_;
- FilePath file_name_; // Database filename.
- scoped_refptr<RefCountedBytes> google_thumbnail_;
- scoped_refptr<RefCountedBytes> random_thumbnail_;
- scoped_refptr<RefCountedBytes> weewar_thumbnail_;
- MessageLoop message_loop_;
- CancelableRequestConsumer consumer_;
+ // Waits for top sites to finish processing a task. This is useful if you need
+ // to wait until top sites finishes processing a task.
+ void WaitForTopSites() {
+ top_sites()->backend_->DoEmptyRequest(
+ &consumer_,
+ NewCallback(this, &TopSitesTest::QuitCallback));
+ MessageLoop::current()->Run();
+ }
- DISALLOW_COPY_AND_ASSIGN(TopSitesTest);
-};
+ TopSites* top_sites() { return profile_->GetTopSites(); }
+ CancelableRequestConsumer* consumer() { return &consumer_; }
+ TestingProfile* profile() {return profile_.get();}
+ HistoryService* history_service() {
+ return profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ }
+
+ MostVisitedURLList GetPrepopulatePages() {
+ return TopSites::GetPrepopulatePages();
+ }
+ // Returns true if the TopSitesQuerier contains the prepopulate data starting
+ // at |start_index|.
+ void ContainsPrepopulatePages(const TopSitesQuerier& querier,
+ size_t start_index) {
+ MostVisitedURLList prepopulate_urls = GetPrepopulatePages();
+ ASSERT_LE(start_index + prepopulate_urls.size(), querier.urls().size());
+ for (size_t i = 0; i < prepopulate_urls.size(); ++i) {
+ EXPECT_EQ(prepopulate_urls[i].url.spec(),
+ querier.urls()[start_index + i].url.spec()) << " @ index " <<
+ i;
+ }
+ }
-// A mockup of a HistoryService used for testing TopSites.
-class MockHistoryServiceImpl : public TopSites::MockHistoryService {
- public:
- MockHistoryServiceImpl() : num_thumbnail_requests_(0) {}
-
- // Calls the callback directly with the results.
- HistoryService::Handle QueryMostVisitedURLs(
- int result_count, int days_back,
- CancelableRequestConsumerBase* consumer,
- HistoryService::QueryMostVisitedURLsCallback* callback) {
- callback->Run(CancelableRequestProvider::Handle(0), // Handle is unused.
- most_visited_urls_);
- delete callback;
- return 0;
+ // Used for callbacks from history.
+ void EmptyCallback() {
}
- // Add a page to the end of the pages list.
- void AppendMockPage(const GURL& url,
- const string16& title) {
- MostVisitedURL page;
- page.url = url;
- page.title = title;
- page.redirects = RedirectList();
- page.redirects.push_back(url);
- most_visited_urls_.push_back(page);
+ // Quit the current message loop when invoked. Useful when running a nested
+ // message loop.
+ void QuitCallback(TopSitesBackend::Handle handle) {
+ MessageLoop::current()->Quit();
}
- // Removes the last URL in the list.
- void RemoveMostVisitedURL() {
- most_visited_urls_.pop_back();
+ // Adds a page to history.
+ void AddPageToHistory(const GURL& url) {
+ RedirectList redirects;
+ redirects.push_back(url);
+ history_service()->AddPage(
+ url, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED,
+ redirects, history::SOURCE_BROWSED, false);
}
- virtual void GetPageThumbnail(
- const GURL& url,
- CancelableRequestConsumerTSimple<size_t>* consumer,
- HistoryService::ThumbnailDataCallback* callback,
- size_t index) {
- num_thumbnail_requests_++;
- MostVisitedURL mvu;
- mvu.url = url;
- MostVisitedURLList::iterator pos = std::find(most_visited_urls_.begin(),
- most_visited_urls_.end(),
- mvu);
- EXPECT_TRUE(pos != most_visited_urls_.end()) << url.spec();
- scoped_refptr<RefCountedBytes> thumbnail;
- callback->Run(index, thumbnail);
- delete callback;
+ // Adds a page to history.
+ void AddPageToHistory(const GURL& url, const string16& title) {
+ RedirectList redirects;
+ redirects.push_back(url);
+ history_service()->AddPage(
+ url, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED,
+ redirects, history::SOURCE_BROWSED, false);
+ history_service()->SetPageTitle(url, title);
}
- void ResetNumberOfThumbnailRequests() {
- num_thumbnail_requests_ = 0;
+ // Adds a page to history.
+ void AddPageToHistory(const GURL& url,
+ const string16& title,
+ const history::RedirectList& redirects,
+ base::Time time) {
+ history_service()->AddPage(
+ url, time, static_cast<void*>(this), 0, GURL(), PageTransition::TYPED,
+ redirects, history::SOURCE_BROWSED, false);
+ history_service()->SetPageTitle(url, title);
}
- int GetNumberOfThumbnailRequests() {
- return num_thumbnail_requests_;
+ // Delets a url.
+ void DeleteURL(const GURL& url) {
+ history_service()->DeleteURL(url);
}
- private:
- MostVisitedURLList most_visited_urls_;
- int num_thumbnail_requests_; // Number of calls to GetPageThumbnail.
-};
+ // Returns true if the thumbnail equals the specified bytes.
+ bool ThumbnailEqualsBytes(const SkBitmap& image, RefCountedBytes* bytes) {
+ scoped_refptr<RefCountedBytes> encoded_image;
+ EncodeBitmap(image, &encoded_image);
+ return ThumbnailsAreEqual(encoded_image, bytes);
+ }
+ // Recreates top sites. This forces top sites to reread from the db.
+ void RecreateTopSitesAndBlock() {
+ // Recreate TopSites and wait for it to load.
+ profile()->CreateTopSites();
+ // As history already loaded we have to fake this call.
+ profile()->BlockUntilTopSitesLoaded();
+ }
-// A mockup of a TopSitesDatabase used for testing TopSites.
-class MockTopSitesDatabaseImpl : public TopSitesDatabase {
- public:
- virtual void GetPageThumbnails(MostVisitedURLList* urls,
- std::map<GURL, Images>* thumbnails) {
- // Return a copy of the vector.
- *urls = top_sites_list_;
- *thumbnails = thumbnails_map_;
+ // Wrappers that allow private TopSites functions to be called from the
+ // individual tests without making them all be friends.
+ GURL GetCanonicalURL(const GURL& url) {
+ return top_sites()->cache_->GetCanonicalURL(url);
}
- virtual void SetPageThumbnail(const MostVisitedURL& url, int url_rank,
- const Images& thumbnail) {
- SetPageRank(url, url_rank);
- // Update thubmnail
- thumbnails_map_[url.url] = thumbnail;
+ void SetTopSites(const MostVisitedURLList& new_top_sites) {
+ top_sites()->SetTopSites(new_top_sites);
}
- virtual void UpdatePageRank(const MostVisitedURL& url, int new_rank) {
- MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(),
- top_sites_list_.end(),
- url);
- // Is it in the right position?
- int rank = pos - top_sites_list_.begin();
- if (rank != new_rank) {
- // Move the URL to a new position.
- top_sites_list_.erase(pos);
- top_sites_list_.insert(top_sites_list_.begin() + new_rank, url);
- }
+ void StartQueryForMostVisited() {
+ top_sites()->StartQueryForMostVisited();
}
- virtual void SetPageRank(const MostVisitedURL& url, int url_rank) {
- // Check if this url is in the list, and at which position.
- MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(),
- top_sites_list_.end(),
- url);
- if (pos == top_sites_list_.end()) {
- // Add it to the list.
- top_sites_list_.insert(top_sites_list_.begin() + url_rank, url);
- } else {
- UpdatePageRank(url, url_rank);
- }
+ bool EncodeBitmap(const SkBitmap& image,
+ scoped_refptr<RefCountedBytes>* bytes) {
+ return TopSites::EncodeBitmap(image, bytes);
+ }
+
+ void SetLastNumUrlsChanged(size_t value) {
+ top_sites()->last_num_urls_changed_ = value;
}
- // Get a thumbnail for a given page. Returns true iff we have the thumbnail.
- virtual bool GetPageThumbnail(const GURL& url,
- Images* thumbnail) {
- std::map<GURL, Images>::const_iterator found =
- thumbnails_map_.find(url);
- if (found == thumbnails_map_.end())
- return false; // No thumbnail for this URL.
+ size_t last_num_urls_changed() { return top_sites()->last_num_urls_changed_; }
- thumbnail->thumbnail = found->second.thumbnail;
- thumbnail->thumbnail_score = found->second.thumbnail_score;
- return true;
+ base::TimeDelta GetUpdateDelay() {
+ return top_sites()->GetUpdateDelay();
}
- virtual bool RemoveURL(const MostVisitedURL& url) {
- // Comparison by url.
- MostVisitedURLList::iterator pos = std::find(top_sites_list_.begin(),
- top_sites_list_.end(),
- url);
- if (pos == top_sites_list_.end()) {
- return false;
- }
- top_sites_list_.erase(pos);
- thumbnails_map_.erase(url.url);
- return true;
+ bool IsTopSitesLoaded() { return top_sites()->loaded_; }
+
+ bool AddPrepopulatedPages(MostVisitedURLList* urls) {
+ return TopSites::AddPrepopulatedPages(urls);
}
private:
- MostVisitedURLList top_sites_list_; // Keeps the URLs sorted by score (rank).
- std::map<GURL, Images> thumbnails_map_;
-};
+ MessageLoopForUI message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread db_thread_;
+ scoped_ptr<TestingProfile> profile_;
+ CancelableRequestConsumer consumer_;
+ CommandLine original_command_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesTest);
+}; // Class TopSitesTest
+class TopSitesMigrationTest : public TopSitesTest {
+ public:
+ TopSitesMigrationTest() {}
+
+ virtual void SetUp() {
+ TopSitesTest::SetUp();
+
+ FilePath data_path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
+ data_path = data_path.AppendASCII("top_sites");
+
+ // Set up history and thumbnails as they would be before migration.
+ ASSERT_NO_FATAL_FAILURE(
+ ExecuteSQL(data_path.AppendASCII("history.19.sql"),
+ profile()->GetPath().Append(chrome::kHistoryFilename)));
+ ASSERT_NO_FATAL_FAILURE(
+ ExecuteSQL(data_path.AppendASCII("thumbnails.3.sql"),
+ profile()->GetPath().Append(chrome::kThumbnailsFilename)));
+
+ profile()->CreateHistoryService(false, false);
+ profile()->CreateTopSites();
+ profile()->BlockUntilTopSitesLoaded();
+ }
+
+ // Returns true if history and top sites should be created in SetUp.
+ virtual bool CreateHistoryAndTopSites() {
+ return false;
+ }
+
+ protected:
+ // Assertions for the migration test. This is extracted into a standalone
+ // method so that it can be invoked twice.
+ void MigrationAssertions() {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ // We shouldn't have gotten a callback.
+ EXPECT_EQ(1, querier.number_of_callbacks());
+
+ // The data we loaded should contain google and yahoo.
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(GURL("http://google.com/"), querier.urls()[0].url);
+ EXPECT_EQ(GURL("http://yahoo.com/"), querier.urls()[1].url);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
+
+ SkBitmap goog_thumbnail = GetThumbnail(GURL("http://google.com/"));
+ EXPECT_EQ(1, goog_thumbnail.width());
+
+ SkBitmap yahoo_thumbnail = GetThumbnail(GURL("http://yahoo.com/"));
+ EXPECT_EQ(2, yahoo_thumbnail.width());
+
+ // Favicon assertions are handled in ThumbnailDatabase.
+ }
+
+ private:
+ // Executes the sql from the file |sql_path| in the database at |db_path|.
+ void ExecuteSQL(const FilePath& sql_path,
+ const FilePath& db_path) {
+ std::string sql;
+ ASSERT_TRUE(file_util::ReadFileToString(sql_path, &sql));
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(db_path));
+ ASSERT_TRUE(connection.Execute(sql.c_str()));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TopSitesMigrationTest);
+};
// Helper function for appending a URL to a vector of "most visited" URLs,
// using the default values for everything but the URL.
@@ -276,18 +400,6 @@ static void AppendMostVisitedURL(std::vector<MostVisitedURL>* list,
list->push_back(mv);
}
-// Returns true if t1 and t2 contain the same data.
-static bool ThumbnailsAreEqual(RefCountedBytes* t1,
- RefCountedBytes* t2) {
- if (!t1 || !t2)
- return false;
- if (t1->data.size() != t2->data.size())
- return false;
- return std::equal(t1->data.begin(),
- t1->data.end(),
- t2->data.begin());
-}
-
// Same as AppendMostVisitedURL except that it adds a redirect from the first
// URL to the second.
static void AppendMostVisitedURLWithRedirect(
@@ -300,8 +412,8 @@ static void AppendMostVisitedURLWithRedirect(
list->push_back(mv);
}
+// Tests GetCanonicalURL.
TEST_F(TopSitesTest, GetCanonicalURL) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
// Have two chains:
// google.com -> www.google.com
// news.google.com (no redirects)
@@ -312,7 +424,7 @@ TEST_F(TopSitesTest, GetCanonicalURL) {
std::vector<MostVisitedURL> most_visited;
AppendMostVisitedURLWithRedirect(&most_visited, source, dest);
AppendMostVisitedURL(&most_visited, news);
- StoreMostVisited(&most_visited);
+ SetTopSites(most_visited);
// Random URLs not in the database are returned unchanged.
GURL result = GetCanonicalURL(GURL("http://fark.com/"));
@@ -331,6 +443,7 @@ TEST_F(TopSitesTest, GetCanonicalURL) {
EXPECT_EQ(dest, result);
}
+// Tests DiffMostVisited.
TEST_F(TopSitesTest, DiffMostVisited) {
GURL stays_the_same("http://staysthesame/");
GURL gets_added_1("http://getsadded1/");
@@ -349,26 +462,25 @@ TEST_F(TopSitesTest, DiffMostVisited) {
AppendMostVisitedURL(&new_list, gets_added_2); // 2 (added)
AppendMostVisitedURL(&new_list, gets_moved_1); // 3 (moved from 2)
- std::vector<size_t> added;
- std::vector<size_t> deleted;
- std::vector<size_t> moved;
- DiffMostVisited(old_list, new_list, &added, &deleted, &moved);
+ history::TopSitesDelta delta;
+ history::TopSites::DiffMostVisited(old_list, new_list, &delta);
- ASSERT_EQ(2u, added.size());
- ASSERT_EQ(1u, deleted.size());
- ASSERT_EQ(1u, moved.size());
+ ASSERT_EQ(2u, delta.added.size());
+ ASSERT_TRUE(gets_added_1 == delta.added[0].url.url);
+ ASSERT_EQ(1, delta.added[0].rank);
+ ASSERT_TRUE(gets_added_2 == delta.added[1].url.url);
+ ASSERT_EQ(2, delta.added[1].rank);
- // There should be 2 URLs added, we don't assume what order they're in inside
- // the result vector.
- EXPECT_TRUE(added[0] == 1 || added[1] == 1);
- EXPECT_TRUE(added[0] == 2 || added[1] == 2);
+ ASSERT_EQ(1u, delta.deleted.size());
+ ASSERT_TRUE(gets_deleted_1 == delta.deleted[0].url);
- EXPECT_EQ(1u, deleted[0]);
- EXPECT_EQ(3u, moved[0]);
+ ASSERT_EQ(1u, delta.moved.size());
+ ASSERT_TRUE(gets_moved_1 == delta.moved[0].url.url);
+ ASSERT_EQ(3, delta.moved[0].rank);
}
+// Tests SetPageThumbnail.
TEST_F(TopSitesTest, SetPageThumbnail) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
GURL url1a("http://google.com/");
GURL url1b("http://www.google.com/");
GURL url2("http://images.google.com/");
@@ -384,13 +496,10 @@ TEST_F(TopSitesTest, SetPageThumbnail) {
list.push_back(mv);
// Save our most visited data containing that one site.
- StoreMostVisited(&list);
+ SetTopSites(list);
// Create a dummy thumbnail.
- SkBitmap thumbnail;
- thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
- thumbnail.allocPixels();
- thumbnail.eraseRGB(0x00, 0x00, 0x00);
+ SkBitmap thumbnail(CreateBitmap(SK_ColorWHITE));
base::Time now = base::Time::Now();
ThumbnailScore low_score(1.0, true, true, now);
@@ -398,96 +507,117 @@ TEST_F(TopSitesTest, SetPageThumbnail) {
ThumbnailScore high_score(0.0, true, true, now);
// Setting the thumbnail for invalid pages should fail.
- EXPECT_FALSE(top_sites().SetPageThumbnail(invalid_url,
- thumbnail, medium_score));
+ EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url,
+ thumbnail, medium_score));
// Setting the thumbnail for url2 should succeed, lower scores shouldn't
// replace it, higher scores should.
- EXPECT_TRUE(top_sites().SetPageThumbnail(url2, thumbnail, medium_score));
- EXPECT_FALSE(top_sites().SetPageThumbnail(url2, thumbnail, low_score));
- EXPECT_TRUE(top_sites().SetPageThumbnail(url2, thumbnail, high_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, medium_score));
+ EXPECT_FALSE(top_sites()->SetPageThumbnail(url2, thumbnail, low_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url2, thumbnail, high_score));
// Set on the redirect source should succeed. It should be replacable by
// the same score on the redirect destination, which in turn should not
// be replaced by the source again.
- EXPECT_TRUE(top_sites().SetPageThumbnail(url1a, thumbnail, medium_score));
- EXPECT_TRUE(top_sites().SetPageThumbnail(url1b, thumbnail, medium_score));
- EXPECT_FALSE(top_sites().SetPageThumbnail(url1a, thumbnail, medium_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url1b, thumbnail, medium_score));
+ EXPECT_FALSE(top_sites()->SetPageThumbnail(url1a, thumbnail, medium_score));
+}
+
+// Makes sure a thumbnail is correctly removed when the page is removed.
+TEST_F(TopSitesTest, ThumbnailRemoved) {
+ GURL url("http://google.com/");
+
+ // Configure top sites with 'google.com'.
+ std::vector<MostVisitedURL> list;
+ AppendMostVisitedURL(&list, url);
+ SetTopSites(list);
+
+ // Create a dummy thumbnail.
+ SkBitmap thumbnail(CreateBitmap(SK_ColorRED));
+
+ base::Time now = base::Time::Now();
+ ThumbnailScore low_score(1.0, true, true, now);
+ ThumbnailScore medium_score(0.5, true, true, now);
+ ThumbnailScore high_score(0.0, true, true, now);
+
+ // Set the thumbnail.
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url, thumbnail, medium_score));
+
+ // Make sure the thumbnail was actually set.
+ scoped_refptr<RefCountedBytes> result;
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url, &result));
+ EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get()));
+
+ // Reset the thumbnails and make sure we don't get it back.
+ SetTopSites(MostVisitedURLList());
+ EXPECT_FALSE(top_sites()->GetPageThumbnail(url, &result));
}
+// Tests GetPageThumbnail.
TEST_F(TopSitesTest, GetPageThumbnail) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
MostVisitedURLList url_list;
- MostVisitedURL url1(GURL("http://asdf.com"), GURL(), string16());
+ MostVisitedURL url1;
+ url1.url = GURL("http://asdf.com");
url1.redirects.push_back(url1.url);
url_list.push_back(url1);
- MostVisitedURL url2(GURL("http://gmail.com"), GURL(), string16());
+ MostVisitedURL url2;
+ url2.url = GURL("http://gmail.com");
url2.redirects.push_back(url2.url);
url2.redirects.push_back(GURL("http://mail.google.com"));
url_list.push_back(url2);
- top_sites().UpdateMostVisited(url_list);
- MessageLoop::current()->RunAllPending();
+ SetTopSites(url_list);
// Create a dummy thumbnail.
- SkBitmap thumbnail;
- thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
- thumbnail.allocPixels();
- thumbnail.eraseRGB(0x00, 0x00, 0x00);
+ SkBitmap thumbnail(CreateBitmap(SK_ColorWHITE));
ThumbnailScore score(0.5, true, true, base::Time::Now());
- RefCountedBytes* result = NULL;
- EXPECT_TRUE(top_sites().SetPageThumbnail(url1.url, thumbnail, score));
- EXPECT_TRUE(top_sites().GetPageThumbnail(url1.url, &result));
+ scoped_refptr<RefCountedBytes> result;
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(url1.url, thumbnail, score));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url1.url, &result));
- EXPECT_TRUE(top_sites().SetPageThumbnail(GURL("http://gmail.com"),
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://gmail.com"),
thumbnail, score));
- EXPECT_TRUE(top_sites().GetPageThumbnail(GURL("http://gmail.com"),
- &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://gmail.com"),
+ &result));
// Get a thumbnail via a redirect.
- EXPECT_TRUE(top_sites().GetPageThumbnail(GURL("http://mail.google.com"),
- &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(GURL("http://mail.google.com"),
+ &result));
- EXPECT_TRUE(top_sites().SetPageThumbnail(GURL("http://mail.google.com"),
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(GURL("http://mail.google.com"),
thumbnail, score));
- EXPECT_TRUE(top_sites().GetPageThumbnail(url2.url, &result));
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(url2.url, &result));
- scoped_ptr<SkBitmap> out_bitmap(gfx::JPEGCodec::Decode(result->front(),
- result->size()));
- EXPECT_EQ(0, memcmp(thumbnail.getPixels(), out_bitmap->getPixels(),
- thumbnail.getSize()));
+ EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, result.get()));
}
+// Tests GetMostVisitedURLs.
TEST_F(TopSitesTest, GetMostVisited) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
GURL news("http://news.google.com/");
GURL google("http://google.com/");
- MockHistoryServiceImpl hs;
- hs.AppendMockPage(news, ASCIIToUTF16("Google News"));
- hs.AppendMockPage(google, ASCIIToUTF16("Google"));
- top_sites().SetMockHistoryService(&hs);
-
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
+ AddPageToHistory(news);
+ AddPageToHistory(google);
+
+ StartQueryForMostVisited();
+ WaitForHistory();
+
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(1, querier.number_of_callbacks());
+
// 2 extra prepopulated URLs.
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ(news, urls()[0].url);
- EXPECT_EQ(google, urls()[1].url);
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(news, querier.urls()[0].url);
+ EXPECT_EQ(google, querier.urls()[1].url);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
}
-TEST_F(TopSitesTest, MockDatabase) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- MockTopSitesDatabaseImpl* db = new MockTopSitesDatabaseImpl;
- // |db| is destroyed when the top_sites is destroyed in TearDown.
- top_sites().db_.reset(db);
+// Makes sure changes done to top sites get mirrored to the db.
+TEST_F(TopSitesTest, SaveToDB) {
MostVisitedURL url;
GURL asdf_url("http://asdf.com");
string16 asdf_title(ASCIIToUTF16("ASDF"));
@@ -496,155 +626,62 @@ TEST_F(TopSitesTest, MockDatabase) {
GURL news_url("http://news.google.com");
string16 news_title(ASCIIToUTF16("Google News"));
- url.url = asdf_url;
- url.title = asdf_title;
- url.redirects.push_back(url.url);
- Images thumbnail;
- db->SetPageThumbnail(url, 0, thumbnail);
+ // Add asdf_url to history.
+ AddPageToHistory(asdf_url, asdf_title);
+
+ // Make TopSites reread from the db.
+ StartQueryForMostVisited();
+ WaitForHistory();
- top_sites().ReadDatabase();
+ // Add a thumbnail.
+ SkBitmap tmp_bitmap(CreateBitmap(SK_ColorBLUE));
+ ASSERT_TRUE(top_sites()->SetPageThumbnail(asdf_url, tmp_bitmap,
+ ThumbnailScore()));
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(3u, urls().size());
- EXPECT_EQ(asdf_url, urls()[0].url);
- EXPECT_EQ(asdf_title, urls()[0].title);
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
+ RecreateTopSitesAndBlock();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(asdf_url, querier.urls()[0].url);
+ EXPECT_EQ(asdf_title, querier.urls()[0].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
+
+ scoped_refptr<RefCountedBytes> read_data;
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data));
+ EXPECT_TRUE(ThumbnailEqualsBytes(tmp_bitmap, read_data.get()));
+ }
MostVisitedURL url2;
url2.url = google_url;
url2.title = google_title;
url2.redirects.push_back(url2.url);
- // Add new thumbnail at rank 0 and shift the other result to 1.
- db->SetPageThumbnail(url2, 0, thumbnail);
-
- top_sites().ReadDatabase();
-
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ(google_url, urls()[0].url);
- EXPECT_EQ(google_title, urls()[0].title);
- EXPECT_EQ(asdf_url, urls()[1].url);
- EXPECT_EQ(asdf_title, urls()[1].title);
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
-
- MockHistoryServiceImpl hs;
- // Add one old, one new URL to the history.
- hs.AppendMockPage(google_url, google_title);
- hs.AppendMockPage(news_url, news_title);
- top_sites().SetMockHistoryService(&hs);
-
- // This writes the new data to the DB.
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
-
- std::map<GURL, Images> thumbnails;
- MostVisitedURLList result;
- db->GetPageThumbnails(&result, &thumbnails);
- ASSERT_EQ(4u, result.size());
- EXPECT_EQ(google_title, result[0].title);
- EXPECT_EQ(news_title, result[1].title);
-}
-
-// Test TopSitesDatabaseImpl.
-TEST_F(TopSitesTest, TopSitesDB) {
- TopSitesDatabaseImpl db;
+ AddPageToHistory(url2.url, url2.title);
- ASSERT_TRUE(db.Init(file_name()));
+ // Add new thumbnail at rank 0 and shift the other result to 1.
+ ASSERT_TRUE(top_sites()->SetPageThumbnail(google_url,
+ tmp_bitmap,
+ ThumbnailScore()));
- MostVisitedURL url;
- GURL asdf_url("http://asdf.com");
- string16 asdf_title(ASCIIToUTF16("ASDF"));
- GURL google_url("http://google.com");
- string16 google_title(ASCIIToUTF16("Google"));
- GURL news_url("http://news.google.com");
- string16 news_title(ASCIIToUTF16("Google News"));
+ // Make TopSites reread from the db.
+ RefreshTopSitesAndRecreate();
- url.url = asdf_url;
- url.title = asdf_title;
- url.redirects.push_back(url.url);
- Images thumbnail;
- thumbnail.thumbnail = random_thumbnail();
- // Add asdf at rank 0.
- db.SetPageThumbnail(url, 0, thumbnail);
-
- Images result;
- EXPECT_TRUE(db.GetPageThumbnail(url.url, &result));
- EXPECT_EQ(thumbnail.thumbnail->data.size(), result.thumbnail->data.size());
- EXPECT_TRUE(ThumbnailsAreEqual(thumbnail.thumbnail, result.thumbnail));
-
- MostVisitedURLList urls;
- std::map<GURL, Images> thumbnails;
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(1u, urls.size());
- EXPECT_EQ(asdf_url, urls[0].url);
- EXPECT_EQ(asdf_title, urls[0].title);
-
- url.url = google_url;
- url.title = google_title;
-
- // Add google at rank 1 - no rank shifting.
- db.SetPageThumbnail(url, 1, thumbnail);
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(2u, urls.size());
- EXPECT_EQ(asdf_url, urls[0].url);
- EXPECT_EQ(asdf_title, urls[0].title);
- EXPECT_EQ(google_url, urls[1].url);
- EXPECT_EQ(google_title, urls[1].title);
-
- url.url = news_url;
- url.title = news_title;
-
- // Add news at rank 1 - shift google to rank 2.
- db.SetPageThumbnail(url, 1, thumbnail);
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(3u, urls.size());
- EXPECT_EQ(asdf_url, urls[0].url);
- EXPECT_EQ(news_url, urls[1].url);
- EXPECT_EQ(google_url, urls[2].url);
-
- // Move news at rank 0 - shift the rest up.
- db.SetPageThumbnail(url, 0, thumbnail);
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(3u, urls.size());
- EXPECT_EQ(news_url, urls[0].url);
- EXPECT_EQ(asdf_url, urls[1].url);
- EXPECT_EQ(google_url, urls[2].url);
-
- // Move news at rank 2 - shift the rest down.
- db.SetPageThumbnail(url, 2, thumbnail);
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(3u, urls.size());
- EXPECT_EQ(asdf_url, urls[0].url);
- EXPECT_EQ(google_url, urls[1].url);
- EXPECT_EQ(news_url, urls[2].url);
-
- // Delete asdf.
- url.url = asdf_url;
- db.RemoveURL(url);
-
- db.GetPageThumbnails(&urls, &thumbnails);
- ASSERT_EQ(2u, urls.size());
- EXPECT_EQ(google_url, urls[0].url);
- EXPECT_EQ(news_url, urls[1].url);
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(asdf_url, querier.urls()[0].url);
+ EXPECT_EQ(asdf_title, querier.urls()[0].title);
+ EXPECT_EQ(google_url, querier.urls()[1].url);
+ EXPECT_EQ(google_title, querier.urls()[1].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
+ }
}
-// Test TopSites with a real database.
+// More permutations of saving to db.
TEST_F(TopSitesTest, RealDatabase) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- TopSitesDatabaseImpl* db = new TopSitesDatabaseImpl;
-
- ASSERT_TRUE(db->Init(file_name()));
- // |db| is destroyed when the top_sites is destroyed in TearDown.
- top_sites().db_.reset(db);
MostVisitedURL url;
GURL asdf_url("http://asdf.com");
string16 asdf_title(ASCIIToUTF16("ASDF"));
@@ -658,130 +695,106 @@ TEST_F(TopSitesTest, RealDatabase) {
url.url = asdf_url;
url.title = asdf_title;
url.redirects.push_back(url.url);
- Images thumbnail;
- thumbnail.thumbnail = random_thumbnail();
- db->SetPageThumbnail(url, 0, thumbnail);
-
- top_sites().ReadDatabase();
-
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(3u, urls().size());
- EXPECT_EQ(asdf_url, urls()[0].url);
- EXPECT_EQ(asdf_title, urls()[0].title);
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
-
- Images img_result;
- db->GetPageThumbnail(asdf_url, &img_result);
- EXPECT_TRUE(img_result.thumbnail != NULL);
- EXPECT_TRUE(ThumbnailsAreEqual(random_thumbnail(), img_result.thumbnail));
-
- RefCountedBytes* thumbnail_result;
- EXPECT_TRUE(top_sites().GetPageThumbnail(asdf_url, &thumbnail_result));
- EXPECT_TRUE(thumbnail_result != NULL);
- EXPECT_TRUE(ThumbnailsAreEqual(random_thumbnail(), thumbnail_result));
+ SkBitmap asdf_thumbnail(CreateBitmap(SK_ColorRED));
+ ASSERT_TRUE(top_sites()->SetPageThumbnail(
+ asdf_url, asdf_thumbnail, ThumbnailScore()));
+
+ base::Time add_time(base::Time::Now());
+ AddPageToHistory(url.url, url.title, url.redirects, add_time);
+
+ RefreshTopSitesAndRecreate();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(asdf_url, querier.urls()[0].url);
+ EXPECT_EQ(asdf_title, querier.urls()[0].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
+
+ scoped_refptr<RefCountedBytes> read_data;
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(asdf_url, &read_data));
+ EXPECT_TRUE(ThumbnailEqualsBytes(asdf_thumbnail, read_data.get()));
+ }
MostVisitedURL url2;
- url2.url = google1_url;
+ url2.url = google3_url;
url2.title = google_title;
url2.redirects.push_back(google1_url);
url2.redirects.push_back(google2_url);
url2.redirects.push_back(google3_url);
- // Add new thumbnail at rank 0 and shift the other result to 1.
- Images g_thumbnail;
- g_thumbnail.thumbnail = google_thumbnail();
- db->SetPageThumbnail(url2, 0, g_thumbnail);
-
- top_sites().ReadDatabase();
-
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ(google1_url, urls()[0].url);
- EXPECT_EQ(google_title, urls()[0].title);
- EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &thumbnail_result));
- EXPECT_TRUE(ThumbnailsAreEqual(google_thumbnail(), thumbnail_result));
- ASSERT_EQ(3u, urls()[0].redirects.size());
- EXPECT_EQ(google1_url, urls()[0].redirects[0]);
- EXPECT_EQ(google2_url, urls()[0].redirects[1]);
- EXPECT_EQ(google3_url, urls()[0].redirects[2]);
-
- EXPECT_EQ(asdf_url, urls()[1].url);
- EXPECT_EQ(asdf_title, urls()[1].title);
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
-
- MockHistoryServiceImpl hs;
- // Add one old, one new URL to the history.
- hs.AppendMockPage(google1_url, google_title);
- hs.AppendMockPage(news_url, news_title);
- top_sites().SetMockHistoryService(&hs);
-
- // This requests data from History Service and writes it to the DB.
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
-
- std::map<GURL, Images> thumbnails;
- MostVisitedURLList results;
- db->GetPageThumbnails(&results, &thumbnails);
- ASSERT_EQ(4u, results.size());
- EXPECT_EQ(google_title, results[0].title);
- EXPECT_EQ(news_title, results[1].title);
-
- scoped_ptr<SkBitmap> weewar_bitmap(
- gfx::JPEGCodec::Decode(weewar_thumbnail()->front(),
- weewar_thumbnail()->size()));
+ AddPageToHistory(google3_url, url2.title, url2.redirects,
+ add_time - base::TimeDelta::FromMinutes(1));
+ // Add google twice so that it becomes the first visited site.
+ AddPageToHistory(google3_url, url2.title, url2.redirects,
+ add_time - base::TimeDelta::FromMinutes(2));
- base::Time now = base::Time::Now();
- ThumbnailScore low_score(1.0, true, true, now);
- ThumbnailScore medium_score(0.5, true, true, now);
- ThumbnailScore high_score(0.0, true, true, now);
+ SkBitmap google_thumbnail(CreateBitmap(SK_ColorBLUE));
+ ASSERT_TRUE(top_sites()->SetPageThumbnail(
+ url2.url, google_thumbnail, ThumbnailScore()));
- // 1. Set to weewar. (Writes the thumbnail to the DB.)
- EXPECT_TRUE(top_sites().SetPageThumbnail(google1_url,
- *weewar_bitmap,
- medium_score));
- RefCountedBytes* out_1;
- Images out_2;
- EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &out_1));
+ RefreshTopSitesAndRecreate();
- MessageLoop::current()->RunAllPending();
+ {
+ scoped_refptr<RefCountedBytes> read_data;
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(google1_url, querier.urls()[0].url);
+ EXPECT_EQ(google_title, querier.urls()[0].title);
+ ASSERT_EQ(3u, querier.urls()[0].redirects.size());
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_TRUE(ThumbnailEqualsBytes(google_thumbnail, read_data.get()));
+
+ EXPECT_EQ(asdf_url, querier.urls()[1].url);
+ EXPECT_EQ(asdf_title, querier.urls()[1].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
+ }
- db->GetPageThumbnail(url2.url, &out_2);
- EXPECT_TRUE(ThumbnailsAreEqual(out_1, out_2.thumbnail));
+ SkBitmap weewar_bitmap(CreateBitmap(SK_ColorYELLOW));
- scoped_ptr<SkBitmap> google_bitmap(
- gfx::JPEGCodec::Decode(google_thumbnail()->front(),
- google_thumbnail()->size()));
+ base::Time thumbnail_time(base::Time::Now());
+ ThumbnailScore low_score(1.0, true, true, thumbnail_time);
+ ThumbnailScore medium_score(0.5, true, true, thumbnail_time);
+ ThumbnailScore high_score(0.0, true, true, thumbnail_time);
+
+ // 1. Set to weewar. (Writes the thumbnail to the DB.)
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(google3_url,
+ weewar_bitmap,
+ medium_score));
+ RefreshTopSitesAndRecreate();
+ {
+ scoped_refptr<RefCountedBytes> read_data;
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_TRUE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get()));
+ }
+
+ SkBitmap google_bitmap(CreateBitmap(SK_ColorGREEN));
// 2. Set to google - low score.
- EXPECT_FALSE(top_sites().SetPageThumbnail(google1_url,
- *google_bitmap,
- low_score));
+ EXPECT_FALSE(top_sites()->SetPageThumbnail(google3_url,
+ google_bitmap,
+ low_score));
// 3. Set to google - high score.
- EXPECT_TRUE(top_sites().SetPageThumbnail(google1_url,
- *google_bitmap,
- high_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(google1_url,
+ google_bitmap,
+ high_score));
+
// Check that the thumbnail was updated.
- EXPECT_TRUE(top_sites().GetPageThumbnail(google1_url, &out_1));
- EXPECT_FALSE(ThumbnailsAreEqual(out_1, out_2.thumbnail));
- MessageLoop::current()->RunAllPending();
-
- // Read the new thumbnail from the DB - should match what's in TopSites.
- db->GetPageThumbnail(url2.url, &out_2);
- EXPECT_TRUE(ThumbnailsAreEqual(out_1, out_2.thumbnail));
- EXPECT_TRUE(high_score.Equals(out_2.thumbnail_score));
+ RefreshTopSitesAndRecreate();
+ {
+ scoped_refptr<RefCountedBytes> read_data;
+ EXPECT_TRUE(top_sites()->GetPageThumbnail(google3_url, &read_data));
+ EXPECT_FALSE(ThumbnailEqualsBytes(weewar_bitmap, read_data.get()));
+ EXPECT_TRUE(ThumbnailEqualsBytes(google_bitmap, read_data.get()));
+ }
}
TEST_F(TopSitesTest, DeleteNotifications) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
GURL google1_url("http://google.com");
GURL google2_url("http://google.com/redirect");
GURL google3_url("http://www.google.com");
@@ -789,61 +802,70 @@ TEST_F(TopSitesTest, DeleteNotifications) {
GURL news_url("http://news.google.com");
string16 news_title(ASCIIToUTF16("Google News"));
- MockHistoryServiceImpl hs;
+ AddPageToHistory(google1_url, google_title);
+ AddPageToHistory(news_url, news_title);
- top_sites().Init(file_name());
+ RefreshTopSitesAndRecreate();
- hs.AppendMockPage(google1_url, google_title);
- hs.AppendMockPage(news_url, news_title);
- top_sites().SetMockHistoryService(&hs);
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
+ ASSERT_EQ(4u, querier.urls().size());
+ }
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- // 2 extra prepopulated URLs.
- ASSERT_EQ(4u, urls().size());
-
- hs.RemoveMostVisitedURL();
-
- history::URLsDeletedDetails history_details;
- history_details.all_history = false;
- Details<URLsDeletedDetails> details(&history_details);
- top_sites().Observe(NotificationType::HISTORY_URLS_DELETED,
- Source<Profile> (&profile()),
- details);
- MessageLoop::current()->RunAllPending();
-
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(3u, urls().size());
- EXPECT_EQ(google_title, urls()[0].title);
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
-
- hs.RemoveMostVisitedURL();
- history_details.all_history = true;
- details = Details<HistoryDetails>(&history_details);
- top_sites().Observe(NotificationType::HISTORY_URLS_DELETED,
- Source<Profile> (&profile()),
- details);
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(2u, urls().size());
- EXPECT_EQ(welcome_url(), urls()[0].url);
- EXPECT_EQ(themes_url(), urls()[1].url);
+ DeleteURL(news_url);
+
+ // Wait for history to process the deletion.
+ WaitForHistory();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(google_title, querier.urls()[0].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
+ }
+
+ // Now reload. This verifies topsites actually wrote the deletion to disk.
+ RefreshTopSitesAndRecreate();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_EQ(google_title, querier.urls()[0].title);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 1));
+ }
+
+ DeleteURL(google1_url);
+
+ // Wait for history to process the deletion.
+ WaitForHistory();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0));
+ }
+
+ // Now reload. This verifies topsites actually wrote the deletion to disk.
+ RefreshTopSitesAndRecreate();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0));
+ }
}
TEST_F(TopSitesTest, PinnedURLsDeleted) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
GURL google1_url("http://google.com");
GURL google2_url("http://google.com/redirect");
GURL google3_url("http://www.google.com");
@@ -851,132 +873,130 @@ TEST_F(TopSitesTest, PinnedURLsDeleted) {
GURL news_url("http://news.google.com");
string16 news_title(ASCIIToUTF16("Google News"));
- MockHistoryServiceImpl hs;
+ AddPageToHistory(google1_url, google_title);
+ AddPageToHistory(news_url, news_title);
+
+ RefreshTopSitesAndRecreate();
- top_sites().Init(file_name());
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
- hs.AppendMockPage(google1_url, google_title);
- hs.AppendMockPage(news_url, news_title);
- top_sites().SetMockHistoryService(&hs);
+ // 2 extra prepopulated URLs.
+ ASSERT_EQ(4u, querier.urls().size());
+ }
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- MessageLoop::current()->RunAllPending();
- EXPECT_EQ(1u, number_of_callbacks());
- // 2 extra prepopulated URLs.
- ASSERT_EQ(4u, urls().size());
-
- top_sites().AddPinnedURL(news_url, 3);
- EXPECT_TRUE(top_sites().IsURLPinned(news_url));
-
- hs.RemoveMostVisitedURL();
- history::URLsDeletedDetails history_details;
- history_details.all_history = false;
- history_details.urls.insert(news_url);
- Details<URLsDeletedDetails> details(&history_details);
- top_sites().Observe(NotificationType::HISTORY_URLS_DELETED,
- Source<Profile> (&profile()),
- details);
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- MessageLoop::current()->RunAllPending();
- EXPECT_EQ(2u, number_of_callbacks());
- ASSERT_EQ(3u, urls().size());
- EXPECT_FALSE(top_sites().IsURLPinned(news_url));
-
- hs.RemoveMostVisitedURL();
- history_details.all_history = true;
- details = Details<HistoryDetails>(&history_details);
- top_sites().Observe(NotificationType::HISTORY_URLS_DELETED,
- Source<Profile> (&profile()),
- details);
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(2u, urls().size());
- MessageLoop::current()->RunAllPending();
-
- top_sites().StartQueryForMostVisited();
- MessageLoop::current()->RunAllPending();
- top_sites().GetMostVisitedURLs(
- consumer(),
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(2u, urls().size());
- EXPECT_EQ(welcome_url(), urls()[0].url);
- EXPECT_EQ(themes_url(), urls()[1].url);
+ top_sites()->AddPinnedURL(news_url, 3);
+ EXPECT_TRUE(top_sites()->IsURLPinned(news_url));
+
+ DeleteURL(news_url);
+ WaitForHistory();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ // 2 extra prepopulated URLs.
+ ASSERT_EQ(3u, querier.urls().size());
+ EXPECT_FALSE(top_sites()->IsURLPinned(news_url));
+ }
+
+ history_service()->ExpireHistoryBetween(
+ std::set<GURL>(), base::Time(), base::Time(),
+ consumer(), NewCallback(static_cast<TopSitesTest*>(this),
+ &TopSitesTest::EmptyCallback)),
+ WaitForHistory();
+
+ {
+ TopSitesQuerier querier;
+ querier.QueryTopSites(top_sites(), false);
+
+ // 2 extra prepopulated URLs.
+ ASSERT_EQ(GetPrepopulatePages().size(), querier.urls().size());
+ EXPECT_FALSE(top_sites()->IsURLPinned(google1_url));
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 0));
+ }
}
+// Makes sure GetUpdateDelay is updated appropriately.
TEST_F(TopSitesTest, GetUpdateDelay) {
- top_sites().last_num_urls_changed_ = 0;
- EXPECT_EQ(30, top_sites().GetUpdateDelay().InSeconds());
+ SetLastNumUrlsChanged(0);
+ EXPECT_EQ(30, GetUpdateDelay().InSeconds());
- top_sites().top_sites_.resize(20);
- top_sites().last_num_urls_changed_ = 0;
- EXPECT_EQ(60, top_sites().GetUpdateDelay().InMinutes());
+ MostVisitedURLList url_list;
+ url_list.resize(20);
+ GURL tmp_url(GURL("http://x"));
+ for (size_t i = 0; i < url_list.size(); ++i) {
+ url_list[i].url = tmp_url;
+ url_list[i].redirects.push_back(tmp_url);
+ }
+ SetTopSites(url_list);
+ EXPECT_EQ(20u, last_num_urls_changed());
+ SetLastNumUrlsChanged(0);
+ EXPECT_EQ(60, GetUpdateDelay().InMinutes());
- top_sites().last_num_urls_changed_ = 3;
- EXPECT_EQ(52, top_sites().GetUpdateDelay().InMinutes());
+ SetLastNumUrlsChanged(3);
+ EXPECT_EQ(52, GetUpdateDelay().InMinutes());
- top_sites().last_num_urls_changed_ = 20;
- EXPECT_EQ(1, top_sites().GetUpdateDelay().InMinutes());
+ SetLastNumUrlsChanged(20);
+ EXPECT_EQ(1, GetUpdateDelay().InMinutes());
}
-TEST_F(TopSitesTest, Migration) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- GURL google1_url("http://google.com");
- string16 google_title(ASCIIToUTF16("Google"));
- GURL news_url("http://news.google.com");
- string16 news_title(ASCIIToUTF16("Google News"));
+TEST_F(TopSitesMigrationTest, Migrate) {
+ EXPECT_TRUE(IsTopSitesLoaded());
- MockHistoryServiceImpl hs;
+ // Make sure the data was migrated to top sites.
+ ASSERT_NO_FATAL_FAILURE(MigrationAssertions());
- top_sites().Init(file_name());
+ // We need to wait for top sites and history to finish processing requests.
+ WaitForTopSites();
+ WaitForHistory();
- hs.AppendMockPage(google1_url, google_title);
- hs.AppendMockPage(news_url, news_title);
- top_sites().SetMockHistoryService(&hs);
- MessageLoop::current()->RunAllPending();
+ // Make sure there is no longer a Thumbnails file on disk.
+ ASSERT_FALSE(file_util::PathExists(
+ profile()->GetPath().Append(chrome::kThumbnailsFilename)));
- top_sites().StartMigration();
- EXPECT_TRUE(top_sites().migration_in_progress_);
- MessageLoop::current()->RunAllPending();
- EXPECT_EQ(2, hs.GetNumberOfThumbnailRequests());
- EXPECT_FALSE(top_sites().migration_in_progress_);
+ // Recreate top sites and make sure everything is still there.
+ profile()->CreateHistoryService(false, false);
+ RecreateTopSitesAndBlock();
+
+ ASSERT_NO_FATAL_FAILURE(MigrationAssertions());
}
-TEST_F(TopSitesTest, QueueingRequestsForTopSites) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- CancelableRequestConsumer c1;
- CancelableRequestConsumer c2;
- CancelableRequestConsumer c3;
- top_sites().GetMostVisitedURLs(
- &c1,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- top_sites().GetMostVisitedURLs(
- &c2,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- top_sites().GetMostVisitedURLs(
- &c3,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- EXPECT_EQ(0u, number_of_callbacks());
- EXPECT_EQ(0u, urls().size());
+// Verifies that callbacks are notified correctly if requested before top sites
+// has loaded.
+TEST_F(TopSitesTest, NotifyCallbacksWhenLoaded) {
+ // Recreate top sites. It won't be loaded now.
+ profile()->CreateTopSites();
+
+ EXPECT_FALSE(IsTopSitesLoaded());
+
+ TopSitesQuerier querier1;
+ TopSitesQuerier querier2;
+ TopSitesQuerier querier3;
+
+ // Starts the queries.
+ querier1.QueryTopSites(top_sites(), false);
+ querier2.QueryTopSites(top_sites(), false);
+ querier3.QueryTopSites(top_sites(), false);
+
+ // We shouldn't have gotten a callback.
+ EXPECT_EQ(0, querier1.number_of_callbacks());
+ EXPECT_EQ(0, querier2.number_of_callbacks());
+ EXPECT_EQ(0, querier3.number_of_callbacks());
+ // Wait for loading to complete.
+ profile()->BlockUntilTopSitesLoaded();
+
+ // Now we should have gotten the callbacks.
+ EXPECT_EQ(1, querier1.number_of_callbacks());
+ EXPECT_EQ(GetPrepopulatePages().size(), querier1.urls().size());
+ EXPECT_EQ(1, querier2.number_of_callbacks());
+ EXPECT_EQ(GetPrepopulatePages().size(), querier2.urls().size());
+ EXPECT_EQ(1, querier3.number_of_callbacks());
+ EXPECT_EQ(GetPrepopulatePages().size(), querier3.urls().size());
+
+ // Reset the top sites.
MostVisitedURLList pages;
MostVisitedURL url;
url.url = GURL("http://1.com/");
@@ -985,109 +1005,107 @@ TEST_F(TopSitesTest, QueueingRequestsForTopSites) {
url.url = GURL("http://2.com/");
url.redirects.push_back(url.url);
pages.push_back(url);
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
+ SetTopSites(pages);
+
+ // Recreate top sites. It won't be loaded now.
+ profile()->CreateTopSites();
+
+ EXPECT_FALSE(IsTopSitesLoaded());
- EXPECT_EQ(3u, number_of_callbacks());
+ TopSitesQuerier querier4;
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://1.com/", urls()[0].url.spec());
- EXPECT_EQ("http://2.com/", urls()[1].url.spec());
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
+ // Query again.
+ querier4.QueryTopSites(top_sites(), false);
+ // We shouldn't have gotten a callback.
+ EXPECT_EQ(0, querier4.number_of_callbacks());
+ // Wait for loading to complete.
+ profile()->BlockUntilTopSitesLoaded();
+
+ // Now we should have gotten the callbacks.
+ EXPECT_EQ(1, querier4.number_of_callbacks());
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), querier4.urls().size());
+
+ EXPECT_EQ("http://1.com/", querier4.urls()[0].url.spec());
+ EXPECT_EQ("http://2.com/", querier4.urls()[1].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier4, 2));
+
+ // Reset the top sites again, this time don't reload.
url.url = GURL("http://3.com/");
url.redirects.push_back(url.url);
pages.push_back(url);
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
+ SetTopSites(pages);
- top_sites().GetMostVisitedURLs(
- &c3,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
+ // Query again.
+ TopSitesQuerier querier5;
+ querier5.QueryTopSites(top_sites(), true);
- EXPECT_EQ(4u, number_of_callbacks());
-
- ASSERT_EQ(5u, urls().size());
- EXPECT_EQ("http://1.com/", urls()[0].url.spec());
- EXPECT_EQ("http://2.com/", urls()[1].url.spec());
- EXPECT_EQ("http://3.com/", urls()[2].url.spec());
- EXPECT_EQ(welcome_url(), urls()[3].url);
- EXPECT_EQ(themes_url(), urls()[4].url);
+ EXPECT_EQ(1, querier5.number_of_callbacks());
+ ASSERT_EQ(3u + GetPrepopulatePages().size(), querier5.urls().size());
+ EXPECT_EQ("http://1.com/", querier5.urls()[0].url.spec());
+ EXPECT_EQ("http://2.com/", querier5.urls()[1].url.spec());
+ EXPECT_EQ("http://3.com/", querier5.urls()[2].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier5, 3));
}
+// Makes sure canceled requests are not notified.
TEST_F(TopSitesTest, CancelingRequestsForTopSites) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- CancelableRequestConsumer c1;
- CancelableRequestConsumer c2;
- top_sites().GetMostVisitedURLs(
- &c1,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- top_sites().GetMostVisitedURLs(
- &c2,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
+ // Recreate top sites. It won't be loaded now.
+ profile()->CreateTopSites();
- {
- CancelableRequestConsumer c3;
- top_sites().GetMostVisitedURLs(
- &c3,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- // c3 is out of scope, and the request should be cancelled.
- }
+ EXPECT_FALSE(IsTopSitesLoaded());
- // No requests until OnTopSitesAvailable is called.
- EXPECT_EQ(0u, number_of_callbacks());
- EXPECT_EQ(0u, urls().size());
+ TopSitesQuerier querier1;
+ TopSitesQuerier querier2;
- MostVisitedURLList pages;
- MostVisitedURL url;
- url.url = GURL("http://1.com/");
- url.redirects.push_back(url.url);
- pages.push_back(url);
- url.url = GURL("http://2.com/");
- pages.push_back(url);
+ // Starts the queries.
+ querier1.QueryTopSites(top_sites(), false);
+ querier2.QueryTopSites(top_sites(), false);
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
+ // We shouldn't have gotten a callback.
+ EXPECT_EQ(0, querier1.number_of_callbacks());
+ EXPECT_EQ(0, querier2.number_of_callbacks());
- // 1 request was canceled.
- EXPECT_EQ(2u, number_of_callbacks());
+ querier2.CancelRequest();
- // 2 extra prepopulated URLs.
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://1.com/", urls()[0].url.spec());
- EXPECT_EQ("http://2.com/", urls()[1].url.spec());
+ // Wait for loading to complete.
+ profile()->BlockUntilTopSitesLoaded();
+
+ // The first callback should succeed.
+ EXPECT_EQ(1, querier1.number_of_callbacks());
+ EXPECT_EQ(GetPrepopulatePages().size(), querier1.urls().size());
+
+ // And the canceled callback should not be notified.
+ EXPECT_EQ(0, querier2.number_of_callbacks());
}
+// Makes sure temporary thumbnails are copied over correctly.
TEST_F(TopSitesTest, AddTemporaryThumbnail) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
GURL unknown_url("http://news.google.com/");
GURL invalid_url("chrome://thumb/http://google.com/");
GURL url1a("http://google.com/");
GURL url1b("http://www.google.com/");
// Create a dummy thumbnail.
- SkBitmap thumbnail;
- thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
- thumbnail.allocPixels();
- thumbnail.eraseRGB(0x00, 0x00, 0x00);
+ SkBitmap thumbnail(CreateBitmap(SK_ColorRED));
ThumbnailScore medium_score(0.5, true, true, base::Time::Now());
// Don't store thumbnails for Javascript URLs.
- EXPECT_FALSE(top_sites().SetPageThumbnail(invalid_url,
- thumbnail, medium_score));
+ EXPECT_FALSE(top_sites()->SetPageThumbnail(invalid_url,
+ thumbnail,
+ medium_score));
// Store thumbnails for unknown (but valid) URLs temporarily - calls
// AddTemporaryThumbnail.
- EXPECT_TRUE(top_sites().SetPageThumbnail(unknown_url,
- thumbnail, medium_score));
+ EXPECT_TRUE(top_sites()->SetPageThumbnail(unknown_url,
+ thumbnail,
+ medium_score));
+
+ // We shouldn't get the thumnail back though (the url isn't in to sites yet).
+ scoped_refptr<RefCountedBytes> out;
+ EXPECT_FALSE(top_sites()->GetPageThumbnail(unknown_url, &out));
std::vector<MostVisitedURL> list;
@@ -1098,19 +1116,15 @@ TEST_F(TopSitesTest, AddTemporaryThumbnail) {
mv.redirects.push_back(url1b);
list.push_back(mv);
- // Update URLs - use temporary thumbnails.
- top_sites().UpdateMostVisited(list);
+ // Update URLs. This should result in using thumbnail.
+ SetTopSites(list);
- RefCountedBytes* out = NULL;
- ASSERT_TRUE(top_sites().GetPageThumbnail(unknown_url, &out));
- scoped_ptr<SkBitmap> out_bitmap(gfx::JPEGCodec::Decode(out->front(),
- out->size()));
- EXPECT_EQ(0, memcmp(thumbnail.getPixels(), out_bitmap->getPixels(),
- thumbnail.getSize()));
+ ASSERT_TRUE(top_sites()->GetPageThumbnail(unknown_url, &out));
+ EXPECT_TRUE(ThumbnailEqualsBytes(thumbnail, out.get()));
}
+// Tests variations of blacklisting.
TEST_F(TopSitesTest, Blacklisting) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
MostVisitedURLList pages;
MostVisitedURL url, url1;
url.url = GURL("http://bbc.com/");
@@ -1120,91 +1134,83 @@ TEST_F(TopSitesTest, Blacklisting) {
url1.redirects.push_back(url1.url);
pages.push_back(url1);
- CancelableRequestConsumer c;
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
+ SetTopSites(pages);
+ EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/")));
+
+ // Blacklist google.com.
+ top_sites()->AddBlacklistedURL(GURL("http://google.com/"));
+
+ GURL prepopulate_url = GetPrepopulatePages()[0].url;
+
+ EXPECT_TRUE(top_sites()->HasBlacklistedItems());
+ EXPECT_TRUE(top_sites()->IsBlacklisted(GURL("http://google.com/")));
+ EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://bbc.com/")));
+ EXPECT_FALSE(top_sites()->IsBlacklisted(prepopulate_url));
+
+ // Make sure the blacklisted site isn't returned in the results.
{
- Lock& l = lock();
- AutoLock lock(l); // IsBlacklisted must be in a lock.
- EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://bbc.com/")));
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1));
}
- EXPECT_EQ(1u, number_of_callbacks());
+ // Recreate top sites and make sure blacklisted url was correctly read.
+ RecreateTopSitesAndBlock();
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(1u + GetPrepopulatePages().size(), q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1));
+ }
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ("http://google.com/", urls()[1].url.spec());
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
- EXPECT_FALSE(top_sites().HasBlacklistedItems());
+ // Blacklist one of the prepopulate urls.
+ top_sites()->AddBlacklistedURL(prepopulate_url);
+ EXPECT_TRUE(top_sites()->HasBlacklistedItems());
- top_sites().AddBlacklistedURL(GURL("http://google.com/"));
- EXPECT_TRUE(top_sites().HasBlacklistedItems());
+ // Make sure the blacklisted prepopulate url isn't returned.
{
- Lock& l = lock();
- AutoLock lock(l); // IsBlacklisted must be in a lock.
- EXPECT_TRUE(top_sites().IsBlacklisted(GURL("http://google.com/")));
- EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://bbc.com/")));
- EXPECT_FALSE(top_sites().IsBlacklisted(welcome_url()));
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(1u + GetPrepopulatePages().size() - 1, q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ for (size_t i = 1; i < q.urls().size(); ++i)
+ EXPECT_NE(prepopulate_url.spec(), q.urls()[i].url.spec());
}
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- MessageLoop::current()->RunAllPending();
- EXPECT_EQ(2u, number_of_callbacks());
- ASSERT_EQ(3u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
-
- top_sites().AddBlacklistedURL(welcome_url());
- EXPECT_TRUE(top_sites().HasBlacklistedItems());
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(2u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ(themes_url(), urls()[1].url);
-
- top_sites().RemoveBlacklistedURL(GURL("http://google.com/"));
- EXPECT_TRUE(top_sites().HasBlacklistedItems());
+ // Mark google as no longer blacklisted.
+ top_sites()->RemoveBlacklistedURL(GURL("http://google.com/"));
+ EXPECT_TRUE(top_sites()->HasBlacklistedItems());
+ EXPECT_FALSE(top_sites()->IsBlacklisted(GURL("http://google.com/")));
+
+ // Make sure google is returned now.
{
- Lock& l = lock();
- AutoLock lock(l); // IsBlacklisted must be in a lock.
- EXPECT_FALSE(top_sites().IsBlacklisted(GURL("http://google.com/")));
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(2u + GetPrepopulatePages().size() - 1, q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[1].url.spec());
+ EXPECT_NE(prepopulate_url.spec(), q.urls()[2].url.spec());
}
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(3u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ("http://google.com/", urls()[1].url.spec());
- EXPECT_EQ(themes_url(), urls()[2].url);
-
- top_sites().ClearBlacklistedURLs();
- EXPECT_FALSE(top_sites().HasBlacklistedItems());
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ("http://google.com/", urls()[1].url.spec());
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
+ // Remove all blacklisted sites.
+ top_sites()->ClearBlacklistedURLs();
+ EXPECT_FALSE(top_sites()->HasBlacklistedItems());
+
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[1].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2));
+ }
}
+// Tests variations of pinning/unpinning urls.
TEST_F(TopSitesTest, PinnedURLs) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
MostVisitedURLList pages;
MostVisitedURL url, url1;
url.url = GURL("http://bbc.com/");
@@ -1214,127 +1220,127 @@ TEST_F(TopSitesTest, PinnedURLs) {
url1.redirects.push_back(url1.url);
pages.push_back(url1);
- CancelableRequestConsumer c;
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
- EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://bbc.com/")));
-
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ("http://google.com/", urls()[1].url.spec());
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
-
- top_sites().AddPinnedURL(GURL("http://google.com/"), 3);
- EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://bbc.com/")));
- EXPECT_FALSE(top_sites().IsURLPinned(welcome_url()));
-
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- EXPECT_EQ(2u, number_of_callbacks());
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
- EXPECT_EQ("http://google.com/", urls()[3].url.spec());
-
- top_sites().RemovePinnedURL(GURL("http://google.com/"));
- EXPECT_FALSE(top_sites().IsURLPinned(GURL("http://google.com/")));
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://bbc.com/", urls()[0].url.spec());
- EXPECT_EQ("http://google.com/", urls()[1].url.spec());
- EXPECT_EQ(welcome_url(), urls()[2].url);
- EXPECT_EQ(themes_url(), urls()[3].url);
-
- top_sites().AddPinnedURL(GURL("http://bbc.com"), 1);
- top_sites().AddPinnedURL(themes_url(), 0);
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ(themes_url(), urls()[0].url);
- EXPECT_EQ("http://bbc.com/", urls()[1].url.spec());
- EXPECT_EQ("http://google.com/", urls()[2].url.spec());
- EXPECT_EQ(welcome_url(), urls()[3].url);
-
- top_sites().RemovePinnedURL(GURL("http://bbc.com"));
- top_sites().RemovePinnedURL(themes_url());
-
- top_sites().AddPinnedURL(welcome_url(), 1);
- top_sites().AddPinnedURL(GURL("http://bbc.com"), 3);
-
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- ASSERT_EQ(4u, urls().size());
- EXPECT_EQ("http://google.com/", urls()[0].url.spec());
- EXPECT_EQ(welcome_url(), urls()[1].url);
- EXPECT_EQ(themes_url(), urls()[2].url);
- EXPECT_EQ("http://bbc.com/", urls()[3].url.spec());
+ SetTopSites(pages);
+
+ EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://bbc.com/")));
+
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[1].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2));
+ }
+
+ top_sites()->AddPinnedURL(GURL("http://google.com/"), 3);
+ EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://bbc.com/")));
+ EXPECT_FALSE(top_sites()->IsURLPinned(GetPrepopulatePages()[0].url));
+
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ ASSERT_EQ(4u, q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 1));
+ EXPECT_EQ("http://google.com/", q.urls()[3].url.spec());
+ }
+
+ top_sites()->RemovePinnedURL(GURL("http://google.com/"));
+ EXPECT_FALSE(top_sites()->IsURLPinned(GURL("http://google.com/")));
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+
+ ASSERT_EQ(2u + GetPrepopulatePages().size(), q.urls().size());
+ EXPECT_EQ("http://bbc.com/", q.urls()[0].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[1].url.spec());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 2));
+ }
+
+ GURL prepopulate_url = GetPrepopulatePages()[0].url;
+ top_sites()->AddPinnedURL(GURL("http://bbc.com"), 1);
+ top_sites()->AddPinnedURL(prepopulate_url, 0);
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+
+ ASSERT_EQ(3u + GetPrepopulatePages().size() - 1, q.urls().size());
+ EXPECT_EQ(prepopulate_url, q.urls()[0].url);
+ EXPECT_EQ("http://bbc.com/", q.urls()[1].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[2].url.spec());
+ if (GetPrepopulatePages().size() > 1)
+ EXPECT_EQ(GetPrepopulatePages()[1].url, q.urls()[3].url);
+ }
+
+ // Recreate and make sure state remains the same.
+ RecreateTopSitesAndBlock();
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+
+ ASSERT_EQ(3u + GetPrepopulatePages().size() - 1, q.urls().size());
+ EXPECT_EQ(prepopulate_url, q.urls()[0].url);
+ EXPECT_EQ("http://bbc.com/", q.urls()[1].url.spec());
+ EXPECT_EQ("http://google.com/", q.urls()[2].url.spec());
+ if (GetPrepopulatePages().size() > 1)
+ EXPECT_EQ(GetPrepopulatePages()[1].url, q.urls()[3].url);
+ }
}
+// Tests blacklisting and pinning.
TEST_F(TopSitesTest, BlacklistingAndPinnedURLs) {
- BrowserThread db_loop(BrowserThread::DB, MessageLoop::current());
- MostVisitedURLList pages;
- CancelableRequestConsumer c;
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
- top_sites().OnTopSitesAvailable(0, pages);
- MessageLoop::current()->RunAllPending();
-
- ASSERT_EQ(2u, urls().size());
- EXPECT_EQ(welcome_url(), urls()[0].url);
- EXPECT_EQ(themes_url(), urls()[1].url);
-
- top_sites().AddPinnedURL(themes_url(), 1);
- top_sites().AddBlacklistedURL(welcome_url());
-
- top_sites().GetMostVisitedURLs(
- &c,
- NewCallback(static_cast<TopSitesTest*>(this),
- &TopSitesTest::OnTopSitesAvailable));
-
- ASSERT_EQ(2u, urls().size());
- EXPECT_EQ(GURL(), urls()[0].url);
- EXPECT_EQ(themes_url(), urls()[1].url);
+ MostVisitedURLList prepopulate_urls = GetPrepopulatePages();
+ if (prepopulate_urls.size() < 2)
+ return;
+
+ top_sites()->AddPinnedURL(prepopulate_urls[0].url, 1);
+ top_sites()->AddBlacklistedURL(prepopulate_urls[1].url);
+ {
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+
+ ASSERT_LE(2u, q.urls().size());
+ EXPECT_EQ(GURL(), q.urls()[0].url);
+ EXPECT_EQ(prepopulate_urls[0].url, q.urls()[1].url);
+ }
}
+// Makes sure prepopulated pages exist.
TEST_F(TopSitesTest, AddPrepopulatedPages) {
- MostVisitedURLList pages;
- top_sites().AddPrepopulatedPages(&pages);
- ASSERT_EQ(2u, pages.size());
- EXPECT_EQ(welcome_url(), pages[0].url);
- EXPECT_EQ(themes_url(), pages[1].url);
-
- pages.clear();
+ TopSitesQuerier q;
+ q.QueryTopSites(top_sites(), true);
+ EXPECT_EQ(GetPrepopulatePages().size(), q.urls().size());
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0));
- MostVisitedURL url(themes_url(), GURL(), string16());
- pages.push_back(url);
+ MostVisitedURLList pages = q.urls();
+ EXPECT_FALSE(AddPrepopulatedPages(&pages));
- top_sites().AddPrepopulatedPages(&pages);
+ EXPECT_EQ(GetPrepopulatePages().size(), pages.size());
+ q.set_urls(pages);
+ ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(q, 0));
+}
- // Themes URL is already in pages; should not be added twice.
- ASSERT_EQ(2u, pages.size());
- EXPECT_EQ(themes_url(), pages[0].url);
- EXPECT_EQ(welcome_url(), pages[1].url);
+// Makes sure creating top sites before history is created works.
+TEST_F(TopSitesTest, CreateTopSitesThenHistory) {
+ profile()->DestroyTopSites();
+ profile()->DestroyHistoryService();
+
+ // Remove the TopSites file. This forces TopSites to wait until history loads
+ // before TopSites is considered loaded.
+ file_util::Delete(profile()->GetPath().Append(chrome::kTopSitesFilename),
+ false);
+
+ // Create TopSites, but not History.
+ profile()->CreateTopSites();
+ WaitForTopSites();
+ EXPECT_FALSE(IsTopSitesLoaded());
+
+ // Load history, which should make TopSites finish loading too.
+ profile()->CreateHistoryService(false, false);
+ profile()->BlockUntilTopSitesLoaded();
+ EXPECT_TRUE(IsTopSitesLoaded());
}
} // namespace history
diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc
index b63d24a..3f297bd 100644
--- a/chrome/browser/history/url_database.cc
+++ b/chrome/browser/history/url_database.cc
@@ -247,16 +247,29 @@ bool URLDatabase::IsFavIconUsed(FavIconID favicon_id) {
void URLDatabase::AutocompleteForPrefix(const string16& prefix,
size_t max_results,
+ bool typed_only,
std::vector<history::URLRow>* results) {
// NOTE: this query originally sorted by starred as the second parameter. But
// as bookmarks is no longer part of the db we no longer include the order
// by clause.
results->clear();
- sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
- "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
- "WHERE url >= ? AND url < ? AND hidden = 0 "
- "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC "
- "LIMIT ?"));
+ const char* sql;
+ int line;
+ if (typed_only) {
+ sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
+ "WHERE url >= ? AND url < ? AND hidden = 0 AND typed_count > 0 "
+ "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC "
+ "LIMIT ?";
+ line = __LINE__;
+ } else {
+ sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
+ "WHERE url >= ? AND url < ? AND hidden = 0 "
+ "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC "
+ "LIMIT ?";
+ line = __LINE__;
+ }
+ sql::Statement statement(
+ GetDB().GetCachedStatement(sql::StatementID(__FILE__, line), sql));
if (!statement)
return;
@@ -327,7 +340,10 @@ bool URLDatabase::InitKeywordSearchTermsTable() {
"term LONGVARCHAR NOT NULL)")) // The actual search term.
return false;
}
+ return true;
+}
+void URLDatabase::CreateKeywordSearchTermsIndices() {
// For searching.
GetDB().Execute("CREATE INDEX keyword_search_terms_index1 ON "
"keyword_search_terms (keyword_id, lower_term)");
@@ -335,8 +351,6 @@ bool URLDatabase::InitKeywordSearchTermsTable() {
// For deletion.
GetDB().Execute("CREATE INDEX keyword_search_terms_index2 ON "
"keyword_search_terms (url_id)");
-
- return true;
}
bool URLDatabase::DropKeywordSearchTermsTable() {
diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h
index 36bfebb..49bf5f0 100644
--- a/chrome/browser/history/url_database.h
+++ b/chrome/browser/history/url_database.h
@@ -138,10 +138,12 @@ class URLDatabase {
// Autocomplete --------------------------------------------------------------
// Fills the given array with URLs matching the given prefix. They will be
- // sorted by typed count, then by visit count, then by visit date (most
- // recent first) up to the given maximum number. Called by HistoryURLProvider.
+ // sorted by typed count, then by visit count, then by visit date (most recent
+ // first) up to the given maximum number. If |typed_only| is true, only urls
+ // that have been typed once are returned. Called by HistoryURLProvider.
void AutocompleteForPrefix(const string16& prefix,
size_t max_results,
+ bool typed_only,
std::vector<URLRow>* results);
// Tries to find the shortest URL beginning with |base| that strictly
@@ -218,6 +220,9 @@ class URLDatabase {
// Ensures the keyword search terms table exists.
bool InitKeywordSearchTermsTable();
+ // Creates the indices used for keyword search terms.
+ void CreateKeywordSearchTermsIndices();
+
// Deletes the keyword search terms table.
bool DropKeywordSearchTermsTable();
diff --git a/chrome/browser/history/url_database_unittest.cc b/chrome/browser/history/url_database_unittest.cc
index f567844..c37323f 100644
--- a/chrome/browser/history/url_database_unittest.cc
+++ b/chrome/browser/history/url_database_unittest.cc
@@ -52,6 +52,7 @@ class URLDatabaseTest : public testing::Test,
CreateMainURLIndex();
CreateSupplimentaryURLIndices();
InitKeywordSearchTermsTable();
+ CreateKeywordSearchTermsIndices();
}
void TearDown() {
db_.Close();