diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-08 16:19:24 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-08 16:19:24 +0000 |
commit | 5f104d769c8c1302eaffec131d201eae8849570f (patch) | |
tree | 8775de80988147495a6dd8654fbdf0bb854daccd /chrome | |
parent | 59c50a33c4961750f3d47cce47f67f243527390d (diff) | |
download | chromium_src-5f104d769c8c1302eaffec131d201eae8849570f.zip chromium_src-5f104d769c8c1302eaffec131d201eae8849570f.tar.gz chromium_src-5f104d769c8c1302eaffec131d201eae8849570f.tar.bz2 |
Lands http://code.google.com/p/chromium/issues/detail?id=71571 for
Moved the page and icon mapping from URLDatabase to
ThumbnailDatabase.
a. Created a new table icon_mappings in ThumbnailDatabase.
b. Migrated the mapping data during the upgrade.
c. Provided the new interface to access the icon_mapping data.
d. Removed the API to access the faviconID field in url database.
Support IconType.
a. Added additional column icon_type in favicons table.
b. Provided the APIs to access the icon_type field.
c. Currently hard-code all icon_type as FAV_ICON in history backend.
BUG=71571
TEST=new testcases in thumbnail_database_unittest.cc, and the existent
tests in history_backend_unittest.cc, history_unitest.cc,
bookmark_html_writer_unittest.cc etc.
Review URL: http://codereview.chromium.org/6620001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77288 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
26 files changed, 1347 insertions, 244 deletions
diff --git a/chrome/browser/history/expire_history_backend.cc b/chrome/browser/history/expire_history_backend.cc index 62aa7e5..e160635 100644 --- a/chrome/browser/history/expire_history_backend.cc +++ b/chrome/browser/history/expire_history_backend.cc @@ -317,12 +317,12 @@ void ExpireHistoryBackend::StartArchivingOldStuff( void ExpireHistoryBackend::DeleteFaviconsIfPossible( const std::set<FavIconID>& favicon_set) { - if (!main_db_ || !thumb_db_) + if (!thumb_db_) return; for (std::set<FavIconID>::const_iterator i = favicon_set.begin(); i != favicon_set.end(); ++i) { - if (!main_db_->IsFavIconUsed(*i)) + if (!thumb_db_->HasMappingFor(*i)) thumb_db_->DeleteFavIcon(*i); } } @@ -408,13 +408,20 @@ void ExpireHistoryBackend::DeleteOneURL( dependencies->deleted_urls.push_back(url_row); // Delete stuff that references this URL. - if (thumb_db_) + if (thumb_db_) { thumb_db_->DeleteThumbnail(url_row.id()); - // Collect shared information. - if (url_row.favicon_id()) - dependencies->affected_favicons.insert(url_row.favicon_id()); - + // Collect shared information. + std::vector<IconMapping> icon_mappings; + if (thumb_db_->GetIconMappingsForPageURL(url_row.url(), &icon_mappings)) { + for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); + m != icon_mappings.end(); ++m) { + dependencies->affected_favicons.insert(m->icon_id); + } + // Delete the mapping entries for the url. + thumb_db_->DeleteIconMappings(url_row.url()); + } + } // Last, delete the URL entry. main_db_->DeleteURLRow(url_row.id()); } diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc index cf080a8..5e53484 100644 --- a/chrome/browser/history/expire_history_backend_unittest.cc +++ b/chrome/browser/history/expire_history_backend_unittest.cc @@ -68,6 +68,8 @@ class ExpireHistoryTest : public testing::Test, bool HasFavIcon(FavIconID favicon_id); bool HasThumbnail(URLID url_id); + FavIconID GetFavIcon(const GURL& page_url, IconType icon_type); + // Returns the number of text matches for the given URL in the example data // added by AddExampleData. int CountTextMatchesForURL(const GURL& url); @@ -137,7 +139,7 @@ class ExpireHistoryTest : public testing::Test, FilePath thumb_name = path().Append(kThumbnailFile); thumb_db_.reset(new ThumbnailDatabase); - if (thumb_db_->Init(thumb_name, NULL) != sql::INIT_OK) + if (thumb_db_->Init(thumb_name, NULL, main_db_.get()) != sql::INIT_OK) thumb_db_.reset(); text_db_.reset(new TextDatabaseManager(path(), @@ -198,28 +200,30 @@ void ExpireHistoryTest::AddExampleData(URLID url_ids[3], Time visit_times[4]) { // Two favicons. The first two URLs will share the same one, while the last // one will have a unique favicon. - FavIconID favicon1 = thumb_db_->AddFavIcon(GURL("http://favicon/url1")); - FavIconID favicon2 = thumb_db_->AddFavIcon(GURL("http://favicon/url2")); + FavIconID favicon1 = thumb_db_->AddFavIcon(GURL("http://favicon/url1"), + FAV_ICON); + FavIconID favicon2 = thumb_db_->AddFavIcon(GURL("http://favicon/url2"), + FAV_ICON); // Three URLs. URLRow url_row1(GURL("http://www.google.com/1")); url_row1.set_last_visit(visit_times[0]); - url_row1.set_favicon_id(favicon1); url_row1.set_visit_count(1); url_ids[0] = main_db_->AddURL(url_row1); + thumb_db_->AddIconMapping(url_row1.url(), favicon1); URLRow url_row2(GURL("http://www.google.com/2")); url_row2.set_last_visit(visit_times[2]); - url_row2.set_favicon_id(favicon1); url_row2.set_visit_count(2); url_row2.set_typed_count(1); url_ids[1] = main_db_->AddURL(url_row2); + thumb_db_->AddIconMapping(url_row2.url(), favicon1); URLRow url_row3(GURL("http://www.google.com/3")); url_row3.set_last_visit(visit_times[3]); - url_row3.set_favicon_id(favicon2); url_row3.set_visit_count(1); url_ids[2] = main_db_->AddURL(url_row3); + thumb_db_->AddIconMapping(url_row3.url(), favicon2); // Thumbnails for each URL. scoped_ptr<SkBitmap> thumbnail( @@ -307,7 +311,7 @@ void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) { } bool ExpireHistoryTest::HasFavIcon(FavIconID favicon_id) { - if (!thumb_db_.get()) + if (!thumb_db_.get() || favicon_id == 0) return false; Time last_updated; std::vector<unsigned char> icon_data_unused; @@ -316,6 +320,13 @@ bool ExpireHistoryTest::HasFavIcon(FavIconID favicon_id) { &icon_url); } +FavIconID ExpireHistoryTest::GetFavIcon(const GURL& page_url, + IconType icon_type) { + IconMapping icon_mapping; + thumb_db_->GetIconMappingForPageURL(page_url, icon_type, &icon_mapping); + return icon_mapping.icon_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. @@ -402,7 +413,7 @@ void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row) { TEST_F(ExpireHistoryTest, DeleteFaviconsIfPossible) { // Add a favicon record. const GURL favicon_url("http://www.google.com/favicon.ico"); - FavIconID icon_id = thumb_db_->AddFavIcon(favicon_url); + FavIconID icon_id = thumb_db_->AddFavIcon(favicon_url, FAV_ICON); EXPECT_TRUE(icon_id); EXPECT_TRUE(HasFavIcon(icon_id)); @@ -413,15 +424,15 @@ TEST_F(ExpireHistoryTest, DeleteFaviconsIfPossible) { EXPECT_FALSE(HasFavIcon(icon_id)); // Add back the favicon. - icon_id = thumb_db_->AddFavIcon(favicon_url); + icon_id = thumb_db_->AddFavIcon(favicon_url, TOUCH_ICON); EXPECT_TRUE(icon_id); EXPECT_TRUE(HasFavIcon(icon_id)); // Add a page that references the favicon. URLRow row(GURL("http://www.google.com/2")); row.set_visit_count(1); - row.set_favicon_id(icon_id); EXPECT_TRUE(main_db_->AddURL(row)); + thumb_db_->AddIconMapping(row.url(), icon_id); // Favicon should not be deletable. favicon_set.clear(); @@ -449,7 +460,8 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { // Verify things are the way we expect with a URL row, favicon, thumbnail. URLRow last_row; ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row)); - EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); + FavIconID fav_icon_id = GetFavIcon(last_row.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(url_ids[2])); @@ -498,7 +510,8 @@ TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { // All the normal data + the favicon should be gone. EnsureURLInfoGone(last_row); - EXPECT_FALSE(HasFavIcon(last_row.favicon_id())); + EXPECT_FALSE(GetFavIcon(last_row.url(), FAV_ICON)); + EXPECT_FALSE(HasFavIcon(fav_icon_id)); } // Deletes a URL with a favicon that other URLs reference, so that the favicon @@ -511,7 +524,8 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) { // Verify things are the way we expect with a URL row, favicon, thumbnail. URLRow last_row; ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row)); - EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); + FavIconID fav_icon_id = GetFavIcon(last_row.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(url_ids[1])); @@ -525,7 +539,7 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) { // All the normal data + the favicon should be gone. EnsureURLInfoGone(last_row); - EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); } // DeleteURL should not delete starred urls. @@ -548,7 +562,8 @@ TEST_F(ExpireHistoryTest, DontDeleteStarredURL) { ASSERT_TRUE(main_db_->GetRowForURL(url, &url_row)); // And the favicon should exist. - EXPECT_TRUE(HasFavIcon(url_row.favicon_id())); + FavIconID fav_icon_id = GetFavIcon(url_row.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // But there should be no fts. ASSERT_EQ(0, CountTextMatchesForURL(url_row.url())); @@ -617,13 +632,15 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) { EXPECT_EQ(0, temp_row.typed_count()); // Verify that the middle URL's favicon and thumbnail is still there. - EXPECT_TRUE(HasFavIcon(url_row1.favicon_id())); + FavIconID fav_icon_id = GetFavIcon(url_row1.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(url_row1.id())); // Verify that the last URL was deleted. + FavIconID fav_icon_id2 = GetFavIcon(url_row2.url(), FAV_ICON); EnsureURLInfoGone(url_row2); - EXPECT_FALSE(HasFavIcon(url_row2.favicon_id())); + EXPECT_FALSE(HasFavIcon(fav_icon_id2)); } // Expires only a specific URLs more recent than a given time, with no starred @@ -674,13 +691,14 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) { EXPECT_EQ(0, temp_row.typed_count()); // Verify that the middle URL's favicon and thumbnail is still there. - EXPECT_TRUE(HasFavIcon(url_row1.favicon_id())); + FavIconID fav_icon_id = GetFavIcon(url_row1.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_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(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(url_row2.id())); } @@ -722,10 +740,12 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) { // that may have been updated since the time threshold. Since the URL still // 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())); + FavIconID fav_icon_id = GetFavIcon(url_row1.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(new_url_row1.id())); - EXPECT_TRUE(HasFavIcon(new_url_row2.favicon_id())); + fav_icon_id = GetFavIcon(url_row1.url(), FAV_ICON); + EXPECT_TRUE(HasFavIcon(fav_icon_id)); // TODO(sky): fix this, see comment in HasThumbnail. // EXPECT_TRUE(HasThumbnail(new_url_row2.id())); } diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc index 4bbf6a8..d74e889 100644 --- a/chrome/browser/history/history.cc +++ b/chrome/browser/history/history.cc @@ -451,7 +451,7 @@ HistoryService::Handle HistoryService::GetPageThumbnail( void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request, const GURL& icon_url) { Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIcon, NULL, request, - icon_url); + icon_url, history::FAV_ICON); } void HistoryService::UpdateFaviconMappingAndFetch( @@ -459,14 +459,14 @@ void HistoryService::UpdateFaviconMappingAndFetch( const GURL& page_url, const GURL& icon_url) { Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFavIconMappingAndFetch, NULL, - request, page_url, icon_url); + request, page_url, icon_url, history::FAV_ICON); } void HistoryService::GetFaviconForURL( FaviconService::GetFaviconRequest* request, const GURL& page_url) { Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIconForURL, NULL, request, - page_url); + page_url, history::FAV_ICON); } void HistoryService::SetFavicon(const GURL& page_url, @@ -477,7 +477,8 @@ void HistoryService::SetFavicon(const GURL& page_url, ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavIcon, page_url, icon_url, - scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data))); + scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)), + history::FAV_ICON); } void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) { diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index dd7bb12..015991a 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -588,7 +588,8 @@ void HistoryBackend::InitImpl(const std::string& languages) { thumbnail_name = GetFaviconsFileName(); } if (thumbnail_db_->Init(thumbnail_name, - history_publisher_.get()) != sql::INIT_OK) { + history_publisher_.get(), + db_.get()) != sql::INIT_OK) { // Unlike the main database, we don't error out when the database is too // new because this error is much less severe. Generally, this shouldn't // happen since the thumbnail and main datbase versions should be in sync. @@ -1600,27 +1601,31 @@ bool HistoryBackend::GetThumbnailFromOlderRedirect( } void HistoryBackend::GetFavIcon(scoped_refptr<GetFavIconRequest> request, - const GURL& icon_url) { - UpdateFavIconMappingAndFetchImpl(NULL, icon_url, request); + const GURL& icon_url, + int icon_types) { + UpdateFavIconMappingAndFetchImpl(NULL, icon_url, request, icon_types); } void HistoryBackend::UpdateFavIconMappingAndFetch( scoped_refptr<GetFavIconRequest> request, const GURL& page_url, - const GURL& icon_url) { - UpdateFavIconMappingAndFetchImpl(&page_url, icon_url, request); + const GURL& icon_url, + IconType icon_type) { + UpdateFavIconMappingAndFetchImpl(&page_url, icon_url, request, icon_type); } void HistoryBackend::SetFavIconOutOfDateForPage(const GURL& page_url) { - if (!thumbnail_db_.get() || !db_.get()) - return; + std::vector<IconMapping> icon_mappings; - URLRow url_row; - URLID url_id = db_->GetRowForURL(page_url, &url_row); - if (!url_id || !url_row.favicon_id()) + if (!thumbnail_db_.get() || + !thumbnail_db_->GetIconMappingsForPageURL(page_url, + &icon_mappings)) return; - thumbnail_db_->SetFavIconLastUpdateTime(url_row.favicon_id(), Time()); + for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); + m != icon_mappings.end(); ++m) { + thumbnail_db_->SetFavIconLastUpdateTime(m->icon_id, Time()); + } ScheduleCommit(); } @@ -1636,10 +1641,11 @@ void HistoryBackend::SetImportedFavicons( for (size_t i = 0; i < favicon_usage.size(); i++) { FavIconID favicon_id = thumbnail_db_->GetFavIconIDForFavIconURL( - favicon_usage[i].favicon_url); + favicon_usage[i].favicon_url, history::FAV_ICON, NULL); if (!favicon_id) { // This favicon doesn't exist yet, so we create it using the given data. - favicon_id = thumbnail_db_->AddFavIcon(favicon_usage[i].favicon_url); + favicon_id = thumbnail_db_->AddFavIcon(favicon_usage[i].favicon_url, + history::FAV_ICON); if (!favicon_id) continue; // Unable to add the favicon. thumbnail_db_->SetFavIcon(favicon_id, @@ -1663,16 +1669,17 @@ void HistoryBackend::SetImportedFavicons( url_info.set_typed_count(0); url_info.set_last_visit(base::Time()); url_info.set_hidden(false); - url_info.set_favicon_id(favicon_id); db_->AddURL(url_info); + thumbnail_db_->AddIconMapping(*url, favicon_id); + favicons_changed.insert(*url); + } + } else { + if (!thumbnail_db_->GetIconMappingForPageURL(*url, FAV_ICON, NULL)) { + // URL is present in history, update the favicon *only* if it is not + // set already. + thumbnail_db_->AddIconMapping(*url, favicon_id); favicons_changed.insert(*url); } - } else if (url_row.favicon_id() == 0) { - // URL is present in history, update the favicon *only* if it - // is not set already. - url_row.set_favicon_id(favicon_id); - db_->UpdateURLRow(url_row.id(), url_row); - favicons_changed.insert(*url); } } } @@ -1688,7 +1695,12 @@ void HistoryBackend::SetImportedFavicons( void HistoryBackend::UpdateFavIconMappingAndFetchImpl( const GURL* page_url, const GURL& icon_url, - scoped_refptr<GetFavIconRequest> request) { + scoped_refptr<GetFavIconRequest> request, + int icon_types) { + // Check only a single type was given when the page_url was specified. + DCHECK(!page_url || (page_url && (icon_types == FAV_ICON || + icon_types == TOUCH_ICON || icon_types == TOUCH_PRECOMPOSED_ICON))); + if (request->canceled()) return; @@ -1697,8 +1709,10 @@ void HistoryBackend::UpdateFavIconMappingAndFetchImpl( scoped_refptr<RefCountedBytes> data; if (thumbnail_db_.get()) { + IconType returned_icon_type; const FavIconID favicon_id = - thumbnail_db_->GetFavIconIDForFavIconURL(icon_url); + thumbnail_db_->GetFavIconIDForFavIconURL( + icon_url, icon_types, &returned_icon_type); if (favicon_id) { data = new RefCountedBytes; know_favicon = true; @@ -1710,7 +1724,7 @@ void HistoryBackend::UpdateFavIconMappingAndFetchImpl( } if (page_url) - SetFavIconMapping(*page_url, favicon_id); + SetFavIconMapping(*page_url, favicon_id, returned_icon_type); } // else case, haven't cached entry yet. Caller is responsible for // downloading the favicon and invoking SetFavIcon. @@ -1722,7 +1736,8 @@ void HistoryBackend::UpdateFavIconMappingAndFetchImpl( void HistoryBackend::GetFavIconForURL( scoped_refptr<GetFavIconRequest> request, - const GURL& page_url) { + const GURL& page_url, + int icon_types) { if (request->canceled()) return; @@ -1736,11 +1751,12 @@ void HistoryBackend::GetFavIconForURL( // Time the query. TimeTicks beginning_time = TimeTicks::Now(); - URLRow url_info; + std::vector<IconMapping> icon_mappings; data = new RefCountedBytes; Time last_updated; - if (db_->GetRowForURL(page_url, &url_info) && url_info.favicon_id() && - thumbnail_db_->GetFavIcon(url_info.favicon_id(), &last_updated, + if (thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings) && + (icon_mappings.front().icon_type & icon_types) && + thumbnail_db_->GetFavIcon(icon_mappings.front().icon_id, &last_updated, &data->data, &icon_url)) { know_favicon = true; expired = (Time::Now() - last_updated) > @@ -1759,23 +1775,29 @@ void HistoryBackend::GetFavIconForURL( void HistoryBackend::SetFavIcon( const GURL& page_url, const GURL& icon_url, - scoped_refptr<RefCountedMemory> data) { + scoped_refptr<RefCountedMemory> data, + IconType icon_type) { DCHECK(data.get()); if (!thumbnail_db_.get() || !db_.get()) return; - FavIconID id = thumbnail_db_->GetFavIconIDForFavIconURL(icon_url); + FavIconID id = thumbnail_db_->GetFavIconIDForFavIconURL( + icon_url, icon_type, NULL); if (!id) - id = thumbnail_db_->AddFavIcon(icon_url); + id = thumbnail_db_->AddFavIcon(icon_url, icon_type); // Set the image data. thumbnail_db_->SetFavIcon(id, data, Time::Now()); - SetFavIconMapping(page_url, id); + SetFavIconMapping(page_url, id, icon_type); } void HistoryBackend::SetFavIconMapping(const GURL& page_url, - FavIconID id) { + FavIconID id, + IconType icon_type) { + if (!thumbnail_db_.get()) + return; + // Find all the pages whose favicons we should set, we want to set it for // all the pages in the redirect chain if it redirected. history::RedirectList dummy_list; @@ -1799,27 +1821,18 @@ void HistoryBackend::SetFavIconMapping(const GURL& page_url, // Save page <-> favicon association. for (history::RedirectList::const_iterator i(redirects->begin()); i != redirects->end(); ++i) { - URLRow row; - if (!db_->GetRowForURL(*i, &row) || row.favicon_id() == id) - continue; - - FavIconID old_id = row.favicon_id(); - if (old_id == id) - continue; - row.set_favicon_id(id); - db_->UpdateURLRow(row.id(), row); - - if (old_id) { + FavIconID replaced_id; + if (AddOrUpdateIconMapping(*i, id, icon_type, &replaced_id)) { // The page's favicon ID changed. This means that the one we just // changed from could have been orphaned, and we need to re-check it. // This is not super fast, but this case will get triggered rarely, // since normally a page will always map to the same favicon ID. It // will mostly happen for favicons we import. - if (!db_->IsFavIconUsed(old_id) && thumbnail_db_.get()) - thumbnail_db_->DeleteFavIcon(old_id); - } + if (replaced_id && !thumbnail_db_->HasMappingFor(replaced_id)) + thumbnail_db_->DeleteFavIcon(replaced_id); - favicons_changed.insert(row.url()); + favicons_changed.insert(*i); + } } // Send the notification about the changed favicons. @@ -1830,6 +1843,43 @@ void HistoryBackend::SetFavIconMapping(const GURL& page_url, ScheduleCommit(); } +bool HistoryBackend::AddOrUpdateIconMapping(const GURL& page_url, + FavIconID id, + IconType icon_type, + FavIconID* replaced_icon) { + *replaced_icon = 0; + std::vector<IconMapping> icon_mappings; + if (!thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings)) { + // There is no mapping add it directly. + thumbnail_db_->AddIconMapping(page_url, id); + return true; + } + // Iterate all matched icon mappings, + // a. If the given icon id and matched icon id are same, return. + // b. If the given icon type and matched icon type are same, but icon id + // are not, update the IconMapping. + // c. If the given icon_type and matched icon type are not same, but + // either of them is ICON_TOUCH or ICON_PRECOMPOSED_TOUCH, update the + // IconMapping. + // d. Otherwise add a icon mapping. + for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); + m != icon_mappings.end(); ++m) { + if (m->icon_id == id) + // The mapping is already there. + return false; + + if ((icon_type == TOUCH_ICON && m->icon_type == TOUCH_PRECOMPOSED_ICON) || + (icon_type == TOUCH_PRECOMPOSED_ICON && m->icon_type == TOUCH_ICON) || + (icon_type == m->icon_type)) { + thumbnail_db_->UpdateIconMapping(m->mapping_id, id); + *replaced_icon = m->icon_id; + return true; + } + } + thumbnail_db_->AddIconMapping(page_url, id); + return true; +} + void HistoryBackend::Commit() { if (!db_.get()) return; @@ -2120,6 +2170,9 @@ bool HistoryBackend::ClearAllThumbnailHistory( if (!thumbnail_db_->InitTemporaryFavIconsTable()) return false; + if (!thumbnail_db_->InitTemporaryIconMappingTable()) + return false; + // This maps existing favicon IDs to the ones in the temporary table. typedef std::map<FavIconID, FavIconID> FavIconMap; FavIconMap copied_favicons; @@ -2128,26 +2181,33 @@ bool HistoryBackend::ClearAllThumbnailHistory( // URLs to have the new IDs. for (std::vector<URLRow>::iterator i = kept_urls->begin(); i != kept_urls->end(); ++i) { - FavIconID old_id = i->favicon_id(); - if (!old_id) - continue; // URL has no favicon. - FavIconID new_id; - - FavIconMap::const_iterator found = copied_favicons.find(old_id); - if (found == copied_favicons.end()) { - new_id = thumbnail_db_->CopyToTemporaryFavIconTable(old_id); - copied_favicons[old_id] = new_id; - } else { - // We already encountered a URL that used this favicon, use the ID we - // previously got. - new_id = found->second; + std::vector<IconMapping> icon_mappings; + if (!thumbnail_db_->GetIconMappingsForPageURL(i->url(), &icon_mappings)) + continue; + + for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); + m != icon_mappings.end(); ++m) { + FavIconID old_id = m->icon_id; + FavIconID new_id; + FavIconMap::const_iterator found = copied_favicons.find(old_id); + if (found == copied_favicons.end()) { + new_id = thumbnail_db_->CopyToTemporaryFavIconTable(old_id); + copied_favicons[old_id] = new_id; + } else { + // We already encountered a URL that used this favicon, use the ID we + // previously got. + new_id = found->second; + } + // Add Icon mapping, and we don't care wheteher it suceeded or not. + thumbnail_db_->AddToTemporaryIconMappingTable(i->url(), new_id); } - i->set_favicon_id(new_id); } - // Rename the duplicate favicon table back and recreate the other tables. - // This will make the database consistent again. + // Rename the duplicate favicon and icon_mapping back table and recreate the + // other tables. This will make the database consistent again. thumbnail_db_->CommitTemporaryFavIconTable(); + thumbnail_db_->CommitTemporaryIconMappingTable(); + thumbnail_db_->RecreateThumbnailTable(); // Vacuum to remove all the pages associated with the dropped tables. There diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index 4ce7aea..06be843 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -210,16 +210,25 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, // Favicon ------------------------------------------------------------------- void GetFavIcon(scoped_refptr<GetFavIconRequest> request, - const GURL& icon_url); + const GURL& icon_url, + int icon_types); + void GetFavIconForURL(scoped_refptr<GetFavIconRequest> request, - const GURL& page_url); + const GURL& page_url, + int icon_types); + void SetFavIcon(const GURL& page_url, const GURL& icon_url, - scoped_refptr<RefCountedMemory> data); + scoped_refptr<RefCountedMemory> data, + IconType icon_type); + void UpdateFavIconMappingAndFetch(scoped_refptr<GetFavIconRequest> request, const GURL& page_url, - const GURL& icon_url); + const GURL& icon_url, + IconType icon_type); + void SetFavIconOutOfDateForPage(const GURL& page_url); + void SetImportedFavicons( const std::vector<ImportedFavIconUsage>& favicon_usage); @@ -320,6 +329,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, private: friend class base::RefCountedThreadSafe<HistoryBackend>; friend class CommitLaterTask; // The commit task needs to call Commit(). + friend class HistoryBackendTest; friend class HistoryTest; // So the unit tests can poke our innards. FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, DeleteAll); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, ImportedFaviconsTest); @@ -331,6 +341,10 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddVisitsSource); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, RemoveVisitsSource); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, MigrationVisitSource); + FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, MigrationIconMapping); + FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFavIconMapping); + FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, AddOrUpdateIconMapping); + friend class ::TestingProfile; // Computes the name of the specified database on disk. @@ -433,14 +447,28 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, // If page_url is non-null and SetFavIcon has previously been invoked for // icon_url the favicon url for page_url (and all redirects) is set to // icon_url. + // Only a single type can be given in icon_type when page_url is specified. void UpdateFavIconMappingAndFetchImpl( const GURL* page_url, const GURL& icon_url, - scoped_refptr<GetFavIconRequest> request); + scoped_refptr<GetFavIconRequest> request, + int icon_type); // Sets the favicon url id for page_url to id. This will also broadcast // notifications as necessary. - void SetFavIconMapping(const GURL& page_url, FavIconID id); + void SetFavIconMapping(const GURL& page_url, + FavIconID id, + IconType icon_type); + + // Updates the FavIconID associated with the url of a page. If there is an + // existing mapping between |page_url| and |id| this does nothing and returns + // false. If the mapping needs to be added or updated, true is returned. If + // there is an existing mapping but it does not map to |id|, then the |id| of + // the replaced FavIconID is set in |replaced_icon_id|. + bool AddOrUpdateIconMapping(const GURL& page_url, + FavIconID id, + IconType icon_type, + FavIconID* replaced_icon_id); // Generic stuff ------------------------------------------------------------- diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc index c930558a..b72ed5a 100644 --- a/chrome/browser/history/history_backend_unittest.cc +++ b/chrome/browser/history/history_backend_unittest.cc @@ -35,6 +35,14 @@ using base::Time; // history unit test. Because of the elaborate callbacks involved, this is no // harder than calling it directly for many things. +namespace { + +// data we'll put into the thumbnail database +static const unsigned char blob1[] = + "12346102356120394751634516591348710478123649165419234519234512349134"; + +} // namepace + namespace history { class HistoryBackendTest; @@ -122,6 +130,15 @@ class HistoryBackendTest : public testing::Test { return test_dir_; } + FavIconID GetFavIcon(const GURL& url, IconType icon_type) { + IconMapping icon_mapping; + if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type, + &icon_mapping)) + return icon_mapping.icon_id; + else + return 0; + } + BookmarkModel bookmark_model_; protected: @@ -199,8 +216,10 @@ TEST_F(HistoryBackendTest, DeleteAll) { // deleted. This way we can test that updating works properly. GURL favicon_url1("http://www.google.com/favicon.ico"); GURL favicon_url2("http://news.google.com/favicon.ico"); - FavIconID favicon2 = backend_->thumbnail_db_->AddFavIcon(favicon_url2); - FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1); + FavIconID favicon2 = backend_->thumbnail_db_->AddFavIcon(favicon_url2, + FAV_ICON); + FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1, + FAV_ICON); std::vector<unsigned char> data; data.push_back('1'); @@ -216,12 +235,12 @@ TEST_F(HistoryBackendTest, DeleteAll) { row1.set_visit_count(2); row1.set_typed_count(1); row1.set_last_visit(Time::Now()); - row1.set_favicon_id(favicon1); + backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1); URLRow row2(GURL("http://news.google.com/")); row2.set_visit_count(1); row2.set_last_visit(Time::Now()); - row2.set_favicon_id(favicon2); + backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2); std::vector<URLRow> rows; rows.push_back(row2); // Reversed order for the same reason as favicons. @@ -281,6 +300,7 @@ TEST_F(HistoryBackendTest, DeleteAll) { // The first URL should be preserved but the time should be cleared. EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1)); + EXPECT_EQ(row1.url(), outrow1.url()); EXPECT_EQ(0, outrow1.visit_count()); EXPECT_EQ(0, outrow1.typed_count()); EXPECT_TRUE(Time() == outrow1.last_visit()); @@ -303,15 +323,15 @@ TEST_F(HistoryBackendTest, DeleteAll) { // We should have a favicon for the first URL only. We look them up by favicon // URL since the IDs may hav changed. FavIconID out_favicon1 = backend_->thumbnail_db_-> - GetFavIconIDForFavIconURL(favicon_url1); + GetFavIconIDForFavIconURL(favicon_url1, FAV_ICON, NULL); EXPECT_TRUE(out_favicon1); FavIconID out_favicon2 = backend_->thumbnail_db_-> - GetFavIconIDForFavIconURL(favicon_url2); + GetFavIconIDForFavIconURL(favicon_url2, FAV_ICON, NULL); EXPECT_FALSE(out_favicon2) << "Favicon not deleted"; // The remaining URL should still reference the same favicon, even if its // ID has changed. - EXPECT_EQ(out_favicon1, outrow1.favicon_id()); + EXPECT_EQ(out_favicon1, GetFavIcon(outrow1.url(), FAV_ICON)); // The first URL should still be bookmarked. EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url())); @@ -329,8 +349,10 @@ TEST_F(HistoryBackendTest, DeleteAll) { TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { GURL favicon_url1("http://www.google.com/favicon.ico"); GURL favicon_url2("http://news.google.com/favicon.ico"); - FavIconID favicon2 = backend_->thumbnail_db_->AddFavIcon(favicon_url2); - FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1); + FavIconID favicon2 = backend_->thumbnail_db_->AddFavIcon(favicon_url2, + FAV_ICON); + FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1, + FAV_ICON); std::vector<unsigned char> data; data.push_back('1'); @@ -346,12 +368,12 @@ TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { row1.set_visit_count(2); row1.set_typed_count(1); row1.set_last_visit(Time::Now()); - row1.set_favicon_id(favicon1); + EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); URLRow row2(GURL("http://news.google.com/")); row2.set_visit_count(1); row2.set_last_visit(Time::Now()); - row2.set_favicon_id(favicon2); + EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2)); std::vector<URLRow> rows; rows.push_back(row2); // Reversed order for the same reason as favicons. @@ -377,7 +399,9 @@ TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { EXPECT_EQ(0U, visits.size()); // The favicon should still be valid. EXPECT_EQ(favicon2, - backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url2)); + backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url2, + FAV_ICON, + NULL)); // Unstar row2. bookmark_model_.SetURLStarred(row2.url(), string16(), false); @@ -391,7 +415,9 @@ TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row)); // And the favicon should be deleted. EXPECT_EQ(0, - backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url2)); + backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url2, + FAV_ICON, + NULL)); // Unstar row 1. bookmark_model_.SetURLStarred(row1.url(), string16(), false); @@ -411,7 +437,9 @@ TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { // The favicon should still be valid. EXPECT_EQ(favicon1, - backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url1)); + backend_->thumbnail_db_->GetFavIconIDForFavIconURL(favicon_url1, + FAV_ICON, + NULL)); } // Tests a handful of assertions for a navigation with a type of @@ -494,15 +522,17 @@ TEST_F(HistoryBackendTest, ImportedFaviconsTest) { // Setup test data - two Urls in the history, one with favicon assigned and // one without. GURL favicon_url1("http://www.google.com/favicon.ico"); - FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1); + FavIconID favicon1 = backend_->thumbnail_db_->AddFavIcon(favicon_url1, + FAV_ICON); std::vector<unsigned char> data; data.push_back('1'); EXPECT_TRUE(backend_->thumbnail_db_->SetFavIcon(favicon1, RefCountedBytes::TakeVector(&data), Time::Now())); URLRow row1(GURL("http://www.google.com/")); - row1.set_favicon_id(favicon1); row1.set_visit_count(1); row1.set_last_visit(Time::Now()); + EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); + URLRow row2(GURL("http://news.google.com/")); row2.set_visit_count(1); row2.set_last_visit(Time::Now()); @@ -513,8 +543,8 @@ TEST_F(HistoryBackendTest, ImportedFaviconsTest) { URLRow url_row1, url_row2; EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); - EXPECT_FALSE(url_row1.favicon_id() == 0); - EXPECT_TRUE(url_row2.favicon_id() == 0); + EXPECT_FALSE(GetFavIcon(row1.url(), FAV_ICON) == 0); + EXPECT_TRUE(GetFavIcon(row2.url(), FAV_ICON) == 0); // Now provide one imported favicon for both URLs already in the registry. // The new favicon should only be used with the URL that doesn't already have @@ -529,9 +559,10 @@ TEST_F(HistoryBackendTest, ImportedFaviconsTest) { backend_->SetImportedFavicons(favicons); EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); - EXPECT_FALSE(url_row1.favicon_id() == 0); - EXPECT_FALSE(url_row2.favicon_id() == 0); - EXPECT_FALSE(url_row1.favicon_id() == url_row2.favicon_id()); + EXPECT_FALSE(GetFavIcon(row1.url(), FAV_ICON) == 0); + EXPECT_FALSE(GetFavIcon(row2.url(), FAV_ICON) == 0); + EXPECT_FALSE(GetFavIcon(row1.url(), FAV_ICON) == + GetFavIcon(row2.url(), FAV_ICON)); // A URL should not be added to history (to store favicon), if // the URL is not bookmarked. @@ -789,4 +820,112 @@ TEST_F(HistoryBackendTest, MigrationVisitSource) { EXPECT_FALSE(s.Step()); } +TEST_F(HistoryBackendTest, SetFavIconMapping) { + // Init recent_redirects_ + const GURL url1("http://www.google.com"); + const GURL url2("http://www.google.com/m"); + URLRow url_info1(url1); + url_info1.set_visit_count(0); + url_info1.set_typed_count(0); + url_info1.set_last_visit(base::Time()); + url_info1.set_hidden(false); + backend_->db_->AddURL(url_info1); + + URLRow url_info2(url2); + url_info2.set_visit_count(0); + url_info2.set_typed_count(0); + url_info2.set_last_visit(base::Time()); + url_info2.set_hidden(false); + backend_->db_->AddURL(url_info2); + + history::RedirectList redirects; + redirects.push_back(url2); + redirects.push_back(url1); + backend_->recent_redirects_.Put(url1, redirects); + + const GURL icon_url("http://www.google.com/icon"); + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + // Add a fav icon + backend_->SetFavIcon( + url1, icon_url, RefCountedBytes::TakeVector(&data), FAV_ICON); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, FAV_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url2, FAV_ICON, NULL)); + + // Add a touch_icon + backend_->SetFavIcon( + url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, TOUCH_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url2, TOUCH_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, FAV_ICON, NULL)); + + // Add a TOUCH_PRECOMPOSED_ICON + backend_->SetFavIcon(url1, + icon_url, + RefCountedBytes::TakeVector(&data), + TOUCH_PRECOMPOSED_ICON); + // The touch_icon was replaced. + EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, TOUCH_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, FAV_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, TOUCH_PRECOMPOSED_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url2, TOUCH_PRECOMPOSED_ICON, NULL)); + + // Add a touch_icon + backend_->SetFavIcon( + url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, TOUCH_ICON, NULL)); + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, FAV_ICON, NULL)); + // The TOUCH_PRECOMPOSED_ICON was replaced. + EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL( + url1, TOUCH_PRECOMPOSED_ICON, NULL)); + + // Add a fav_icon + const GURL icon_url2("http://www.google.com/icon2"); + backend_->SetFavIcon( + url1, icon_url2, RefCountedBytes::TakeVector(&data), FAV_ICON); + FavIconID icon_id = backend_->thumbnail_db_->GetFavIconIDForFavIconURL( + icon_url2, FAV_ICON, NULL); + EXPECT_NE(0, icon_id); + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( + url1, &icon_mapping)); + // The old icon was replaced. + EXPECT_TRUE(icon_mapping.size() > 1); + EXPECT_EQ(icon_id, icon_mapping[1].icon_id); +} + +TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) { + // Test the same icon and page mapping will not be added twice. other case + // should be covered in TEST_F(HistoryBackendTest, SetFavIconMapping) + const GURL url("http://www.google.com/"); + const GURL icon_url("http://www.google.com/icon"); + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + + backend_->SetFavIcon( + url, icon_url, RefCountedBytes::TakeVector(&data), FAV_ICON); + FavIconID icon_id = backend_->thumbnail_db_->GetFavIconIDForFavIconURL( + icon_url, FAV_ICON, NULL); + + // Add the same mapping + FavIconID replaced; + EXPECT_FALSE(backend_->AddOrUpdateIconMapping( + url, icon_id, FAV_ICON, &replaced)); + EXPECT_EQ(0, replaced); + + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( + url, &icon_mapping)); + EXPECT_EQ(1u, icon_mapping.size()); +} + } // namespace history diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc index bdd3cb4..b748e2f 100644 --- a/chrome/browser/history/history_database.cc +++ b/chrome/browser/history/history_database.cc @@ -119,7 +119,6 @@ sql::InitStatus HistoryDatabase::Init(const FilePath& history_name, return sql::INIT_FAILURE; CreateMainURLIndex(); CreateKeywordSearchTermsIndices(); - CreateSupplimentaryURLIndices(); // Version check. sql::InitStatus version_status = EnsureCurrentVersion(bookmarks_path); @@ -168,7 +167,6 @@ bool HistoryDatabase::RecreateAllTablesButURL() { // We also add the supplementary URL indices at this point. This index is // over parts of the URL table that weren't automatically created when the // temporary URL table was - CreateSupplimentaryURLIndices(); CreateKeywordSearchTermsIndices(); return true; } diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h index 1f0c219..6bc53d8 100644 --- a/chrome/browser/history/history_database.h +++ b/chrome/browser/history/history_database.h @@ -145,6 +145,7 @@ class HistoryDatabase : public DownloadDatabase, virtual void UpdateEarlyExpirationThreshold(base::Time threshold); private: + FRIEND_TEST_ALL_PREFIXES(IconMappingMigrationTest, TestIconMappingMigration); // Implemented for URLDatabase. virtual sql::Connection& GetDB(); diff --git a/chrome/browser/history/history_types.cc b/chrome/browser/history/history_types.cc index 9f56ff0..aa83bc8 100644 --- a/chrome/browser/history/history_types.cc +++ b/chrome/browser/history/history_types.cc @@ -40,7 +40,6 @@ URLRow& URLRow::operator=(const URLRow& other) { typed_count_ = other.typed_count_; last_visit_ = other.last_visit_; hidden_ = other.hidden_; - favicon_id_ = other.favicon_id_; return *this; } @@ -52,7 +51,6 @@ void URLRow::Swap(URLRow* other) { std::swap(typed_count_, other->typed_count_); std::swap(last_visit_, other->last_visit_); std::swap(hidden_, other->hidden_); - std::swap(favicon_id_, other->favicon_id_); } void URLRow::Initialize() { @@ -61,7 +59,6 @@ void URLRow::Initialize() { typed_count_ = 0; last_visit_ = base::Time(); hidden_ = false; - favicon_id_ = 0; } // VisitRow -------------------------------------------------------------------- @@ -403,4 +400,14 @@ bool RowQualifiesAsSignificant(const URLRow& row, (row.last_visit() >= real_threshold); } +// IconMapping ---------------------------------------------------------------- + +IconMapping::IconMapping() + : mapping_id(0), + icon_id(0), + icon_type(INVALID_ICON) { +} + +IconMapping::~IconMapping() {} + } // namespace history diff --git a/chrome/browser/history/history_types.h b/chrome/browser/history/history_types.h index ed219a8..a6822cb 100644 --- a/chrome/browser/history/history_types.h +++ b/chrome/browser/history/history_types.h @@ -42,6 +42,7 @@ typedef int64 UIStarID; // Identifier for star entries that come from the UI. typedef int64 DownloadID; // Identifier for a download. typedef int64 FavIconID; // For FavIcons. typedef int64 SegmentID; // URL segments for the most visited view. +typedef int64 IconMappingID; // For page url and icon mapping. // URLRow --------------------------------------------------------------------- @@ -116,12 +117,6 @@ class URLRow { hidden_ = hidden; } - // ID of the favicon. A value of 0 means the favicon isn't known yet. - FavIconID favicon_id() const { return favicon_id_; } - void set_favicon_id(FavIconID favicon_id) { - favicon_id_ = favicon_id; - } - protected: // Swaps the contents of this URLRow with another, which allows it to be // destructively copied without memory allocations. @@ -162,9 +157,6 @@ class URLRow { // is usually for subframes. bool hidden_; - // The ID of the favicon for this url. - FavIconID favicon_id_; - // We support the implicit copy constuctor and operator=. }; @@ -684,6 +676,33 @@ base::Time AutocompleteAgeThreshold(); // AutocompleteAgeThreshold() (or any other desired time in the past). bool RowQualifiesAsSignificant(const URLRow& row, const base::Time& threshold); +// Defines the icon types. They are also stored in icon_type field of favicons +// table. +enum IconType { + INVALID_ICON = 0x0, + FAV_ICON = 1 << 0, + TOUCH_ICON = 1 << 1, + TOUCH_PRECOMPOSED_ICON = 1 << 2 +}; + +// Used for the mapping between the page and icon. +struct IconMapping { + IconMapping(); + ~IconMapping(); + + // The unique id of the mapping. + IconMappingID mapping_id; + + // The url of a web page. + GURL page_url; + + // The unique id of the icon. + FavIconID icon_id; + + // The type of icon. + IconType icon_type; +}; + } // namespace history #endif // CHROME_BROWSER_HISTORY_HISTORY_TYPES_H_ diff --git a/chrome/browser/history/history_unittest_base.cc b/chrome/browser/history/history_unittest_base.cc new file mode 100644 index 0000000..dfd2eaa --- /dev/null +++ b/chrome/browser/history/history_unittest_base.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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 <vector> + +#include "app/sql/connection.h" +#include "base/format_macros.h" +#include "base/string_util.h" +#include "chrome/browser/history/history_unittest_base.h" + +namespace history { + +HistoryUnitTestBase::~HistoryUnitTestBase() { +} + +void HistoryUnitTestBase::ExecuteSQLScript(const FilePath& sql_path, + const FilePath& db_path) { + std::string sql; + ASSERT_TRUE(file_util::ReadFileToString(sql_path, &sql)); + + // Replace the 'last_visit_time', 'visit_time', 'time_slot' values in this + // SQL with the current time. + int64 now = base::Time::Now().ToInternalValue(); + std::vector<std::string> sql_time; + sql_time.push_back(StringPrintf("%" PRId64, now)); // last_visit_time + sql_time.push_back(StringPrintf("%" PRId64, now)); // visit_time + sql_time.push_back(StringPrintf("%" PRId64, now)); // time_slot + sql = ReplaceStringPlaceholders(sql, sql_time, NULL); + + sql::Connection connection; + ASSERT_TRUE(connection.Open(db_path)); + ASSERT_TRUE(connection.Execute(sql.c_str())); +} + +HistoryUnitTestBase::HistoryUnitTestBase() { +} + +} // namespace history diff --git a/chrome/browser/history/history_unittest_base.h b/chrome/browser/history/history_unittest_base.h new file mode 100644 index 0000000..1d9512d --- /dev/null +++ b/chrome/browser/history/history_unittest_base.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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_HISTORY_UNITTEST_BASE_H_ +#define CHROME_BROWSER_HISTORY_HISTORY_UNITTEST_BASE_H_ + +#include "base/file_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace history { +// A base class for a history unit test. It provides the common test methods. +// +class HistoryUnitTestBase : public testing::Test { + public: + virtual ~HistoryUnitTestBase(); + + // Executes the sql from the file |sql_path| in the database at |db_path|. + // |sql_path| is the SQL script file name with full path. + // |db_path| is the db file name with full path. + static void ExecuteSQLScript(const FilePath& sql_path, + const FilePath& db_path); + + protected: + HistoryUnitTestBase(); + + private: + DISALLOW_COPY_AND_ASSIGN(HistoryUnitTestBase); +}; + +} // namespace history + +#endif // CHROME_BROWSER_HISTORY_HISTORY_UNITTEST_BASE_H_ diff --git a/chrome/browser/history/thumbnail_database.cc b/chrome/browser/history/thumbnail_database.cc index 3f863da..28a0d93 100644 --- a/chrome/browser/history/thumbnail_database.cc +++ b/chrome/browser/history/thumbnail_database.cc @@ -27,14 +27,25 @@ #include "base/mac/mac_util.h" #endif +static void FillIconMapping(const sql::Statement& statement, + const GURL& page_url, + history::IconMapping* icon_mapping) { + icon_mapping->mapping_id = statement.ColumnInt64(0); + icon_mapping->icon_id = statement.ColumnInt64(1); + icon_mapping->icon_type = + static_cast<history::IconType>(statement.ColumnInt(2)); + icon_mapping->page_url = page_url; +} + namespace history { // Version number of the database. -static const int kCurrentVersionNumber = 3; -static const int kCompatibleVersionNumber = 3; +static const int kCurrentVersionNumber = 4; +static const int kCompatibleVersionNumber = 4; -ThumbnailDatabase::ThumbnailDatabase() : history_publisher_(NULL), - use_top_sites_(false) { +ThumbnailDatabase::ThumbnailDatabase() + : history_publisher_(NULL), + use_top_sites_(false) { } ThumbnailDatabase::~ThumbnailDatabase() { @@ -43,7 +54,8 @@ ThumbnailDatabase::~ThumbnailDatabase() { sql::InitStatus ThumbnailDatabase::Init( const FilePath& db_name, - const HistoryPublisher* history_publisher) { + const HistoryPublisher* history_publisher, + URLDatabase* url_db) { history_publisher_ = history_publisher; sql::InitStatus status = OpenDatabase(&db_, db_name); if (status != sql::INIT_OK) @@ -66,11 +78,13 @@ sql::InitStatus ThumbnailDatabase::Init( if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber) || !InitThumbnailTable() || - !InitFavIconsTable(&db_, false)) { + !InitFavIconsTable(&db_, false) || + !InitIconMappingTable(&db_, false)) { db_.Close(); return sql::INIT_FAILURE; } InitFavIconsIndex(); + InitIconMappingIndex(); // Version check. We should not encounter a database too old for us to handle // in the wild, so we try to continue in that case. @@ -89,6 +103,15 @@ sql::InitStatus ThumbnailDatabase::Init( ++cur_version; } + if (cur_version == 3) { + if (!UpgradeToVersion4() || !MigrateIconMappingData(url_db)) { + LOG(WARNING) << "Unable to update to thumbnail database to version 4."; + db_.Close(); + return sql::INIT_FAILURE; + } + ++cur_version; + } + LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << "Thumbnail database version " << cur_version << " is too old to handle."; @@ -180,7 +203,11 @@ bool ThumbnailDatabase::InitFavIconsTable(sql::Connection* db, "id INTEGER PRIMARY KEY," "url LONGVARCHAR NOT NULL," "last_updated INTEGER DEFAULT 0," - "image_data BLOB)"); + "image_data BLOB," + "icon_type INTEGER DEFAULT 1)"); // Set the default as FAV_ICON + // to be consistent with table + // upgrade in + // UpgradeToVersion4(). if (!db->Execute(sql.c_str())) return false; } @@ -372,16 +399,22 @@ bool ThumbnailDatabase::SetFavIconLastUpdateTime(FavIconID icon_id, return statement.Run(); } -FavIconID ThumbnailDatabase::GetFavIconIDForFavIconURL(const GURL& icon_url) { +FavIconID ThumbnailDatabase::GetFavIconIDForFavIconURL(const GURL& icon_url, + int required_icon_type, + IconType* icon_type) { sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, - "SELECT id FROM favicons WHERE url=?")); + "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) " + "ORDER BY icon_type DESC")); if (!statement) return 0; statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); + statement.BindInt(1, required_icon_type); if (!statement.Step()) return 0; // not cached + if (icon_type) + *icon_type = static_cast<IconType>(statement.ColumnInt(1)); return statement.ColumnInt64(0); } @@ -411,13 +444,16 @@ bool ThumbnailDatabase::GetFavIcon( return true; } -FavIconID ThumbnailDatabase::AddFavIcon(const GURL& icon_url) { +FavIconID ThumbnailDatabase::AddFavIcon(const GURL& icon_url, + IconType icon_type) { + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, - "INSERT INTO favicons (url) VALUES (?)")); + "INSERT INTO favicons (url, icon_type) VALUES (?, ?)")); if (!statement) return 0; statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); + statement.BindInt(1, icon_type); if (!statement.Run()) return 0; return db_.GetLastInsertRowId(); @@ -428,14 +464,134 @@ bool ThumbnailDatabase::DeleteFavIcon(FavIconID id) { "DELETE FROM favicons WHERE id = ?")); if (!statement) return false; + statement.BindInt64(0, id); return statement.Run(); } +bool ThumbnailDatabase::GetIconMappingForPageURL(const GURL& page_url, + IconType required_icon_type, + IconMapping* icon_mapping) { + std::vector<IconMapping> icon_mappings; + if (!GetIconMappingsForPageURL(page_url, &icon_mappings)) + return false; + + for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); + m != icon_mappings.end(); ++m) { + if (m->icon_type == required_icon_type) { + if (icon_mapping != NULL) + *icon_mapping = *m; + return true; + } + } + + return false; +} + +bool ThumbnailDatabase::GetIconMappingsForPageURL( + const GURL& page_url, + std::vector<IconMapping>* mapping_data) { + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, + "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type " + "FROM icon_mapping " + "INNER JOIN favicons " + "ON icon_mapping.icon_id = favicons.id " + "WHERE icon_mapping.page_url=? " + "ORDER BY favicons.icon_type DESC")); + if (!statement) + return false; + + statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); + + bool result = false; + while (statement.Step()) { + result = true; + if (!mapping_data) + return result; + + IconMapping icon_mapping; + FillIconMapping(statement, page_url, &icon_mapping); + mapping_data->push_back(icon_mapping); + } + return result; +} + +IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, + FavIconID icon_id) { + return AddIconMapping(page_url, icon_id, false); +} + +bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id, + FavIconID icon_id) { + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, + "UPDATE icon_mapping SET icon_id=? WHERE id=?")); + if (!statement) + return 0; + + statement.BindInt64(0, icon_id); + statement.BindInt64(1, mapping_id); + return statement.Run(); +} + +bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) { + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, + "DELETE FROM icon_mapping WHERE page_url = ?")); + if (!statement) + return false; + + statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); + return statement.Run(); +} + +bool ThumbnailDatabase::HasMappingFor(FavIconID id) { + sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, + "SELECT id FROM icon_mapping " + "WHERE icon_id=?")); + if (!statement) + return false; + + statement.BindInt64(0, id); + return statement.Step(); +} + +bool ThumbnailDatabase::MigrateIconMappingData(URLDatabase* url_db) { + URLDatabase::IconMappingEnumerator e; + if (!url_db->InitIconMappingEnumeratorForEverything(&e)) + return false; + + IconMapping info; + while (e.GetNextIconMapping(&info)) { + // TODO: Using bulk insert to improve the performance. + if (!AddIconMapping(info.page_url, info.icon_id)) + return false; + } + return true; +} + +IconMappingID ThumbnailDatabase::AddToTemporaryIconMappingTable( + const GURL& page_url, const FavIconID icon_id) { + return AddIconMapping(page_url, icon_id, true); +} + +bool ThumbnailDatabase::CommitTemporaryIconMappingTable() { + // Delete the old icon_mapping table. + if (!db_.Execute("DROP TABLE icon_mapping")) + return false; + + // Rename the temporary one. + if (!db_.Execute("ALTER TABLE temp_icon_mapping RENAME TO icon_mapping")) + return false; + + // The renamed table needs the index (the temporary table doesn't have one). + InitIconMappingIndex(); + + return true; +} + FavIconID ThumbnailDatabase::CopyToTemporaryFavIconTable(FavIconID source) { sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, - "INSERT INTO temp_favicons (url, last_updated, image_data)" - "SELECT url, last_updated, image_data " + "INSERT INTO temp_favicons (url, last_updated, image_data, icon_type)" + "SELECT url, last_updated, image_data, icon_type " "FROM favicons WHERE id = ?")); if (!statement) return 0; @@ -472,8 +628,9 @@ bool ThumbnailDatabase::RenameAndDropThumbnails(const FilePath& old_db_file, if (OpenDatabase(&favicons, new_db_file) != sql::INIT_OK) return false; - if (!InitFavIconsTable(&favicons, false)) { - NOTREACHED() << "Couldn't init favicons table."; + if (!InitFavIconsTable(&favicons, false) || + !InitIconMappingTable(&favicons, false)) { + NOTREACHED() << "Couldn't init favicons and icon-mapping table."; favicons.Close(); return false; } @@ -538,4 +695,67 @@ bool ThumbnailDatabase::RenameAndDropThumbnails(const FilePath& old_db_file, return true; } +bool ThumbnailDatabase::InitIconMappingTable(sql::Connection* db, + bool is_temporary) { + const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; + if (!db->DoesTableExist(name)) { + std::string sql; + sql.append("CREATE TABLE "); + sql.append(name); + sql.append("(" + "id INTEGER PRIMARY KEY," + "page_url LONGVARCHAR NOT NULL," + "icon_id INTEGER)"); + if (!db->Execute(sql.c_str())) + return false; + } + return true; +} + +void ThumbnailDatabase::InitIconMappingIndex() { + // Add an index on the url column. We ignore errors. Since this is always + // called during startup, the index will normally already exist. + db_.Execute("CREATE INDEX icon_mapping_page_url_idx" + " ON icon_mapping(page_url)"); + db_.Execute("CREATE INDEX icon_mapping_icon_id_idx ON icon_mapping(icon_id)"); +} + +IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, + FavIconID icon_id, + bool is_temporary) { + const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; + const char* statement_name = + is_temporary ? "add_temp_icon_mapping" : "add_icon_mapping"; + + std::string sql; + sql.append("INSERT INTO "); + sql.append(name); + sql.append("(page_url, icon_id) VALUES (?, ?)"); + + sql::Statement statement( + db_.GetCachedStatement(sql::StatementID(statement_name), sql.c_str())); + if (!statement) + return 0; + + statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); + statement.BindInt64(1, icon_id); + + if (!statement.Run()) + return 0; + + return db_.GetLastInsertRowId(); +} + +bool ThumbnailDatabase::UpgradeToVersion4() { + // Set the default icon type as favicon, so the current data are set + // correctly. + if (!db_.Execute("ALTER TABLE favicons ADD icon_type INTEGER DEFAULT 1")) { + NOTREACHED(); + return false; + } + meta_table_.SetVersionNumber(4); + meta_table_.SetCompatibleVersionNumber(std::min(4, kCompatibleVersionNumber)); + return true; +} + } // namespace history diff --git a/chrome/browser/history/thumbnail_database.h b/chrome/browser/history/thumbnail_database.h index 94fb043..b210729 100644 --- a/chrome/browser/history/thumbnail_database.h +++ b/chrome/browser/history/thumbnail_database.h @@ -11,7 +11,9 @@ #include "app/sql/connection.h" #include "app/sql/init_status.h" #include "app/sql/meta_table.h" +#include "base/gtest_prod_util.h" #include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "chrome/browser/history/history_types.h" class FilePath; @@ -43,7 +45,8 @@ class ThumbnailDatabase { // Must be called after creation but before any other methods are called. // When not INIT_OK, no other functions should be called. sql::InitStatus Init(const FilePath& db_name, - const HistoryPublisher* history_publisher); + const HistoryPublisher* history_publisher, + URLDatabase* url_database); // Open database on a given filename. If the file does not exist, // it is created. @@ -102,9 +105,16 @@ class ThumbnailDatabase { // Sets the time the favicon was last updated. bool SetFavIconLastUpdateTime(FavIconID icon_id, base::Time time); - // Returns the id of the entry in the favicon database with the specified url. + // Returns the id of the entry in the favicon database with the specified url + // and icon type. If |required_icon_type| contains multiple icon types and + // there are more than one matched icon in database, only one icon will be + // returned in the priority of TOUCH_PRECOMPOSED_ICON, TOUCH_ICON, and + // FAV_ICON, and the icon type is returned in icon_type parameter if it is not + // NULL. // Returns 0 if no entry exists for the specified url. - FavIconID GetFavIconIDForFavIconURL(const GURL& icon_url); + FavIconID GetFavIconIDForFavIconURL(const GURL& icon_url, + int required_icon_type, + IconType* icon_type); // Gets the png encoded favicon and last updated time for the specified // favicon id. @@ -113,12 +123,70 @@ class ThumbnailDatabase { std::vector<unsigned char>* png_icon_data, GURL* icon_url); - // Adds the favicon URL to the favicon db, returning its id. - FavIconID AddFavIcon(const GURL& icon_url); + // Adds the favicon URL and icon type to the favicon db, returning its id. + FavIconID AddFavIcon(const GURL& icon_url, IconType icon_type); // Delete the favicon with the provided id. Returns false on failure bool DeleteFavIcon(FavIconID id); + // Icon Mapping -------------------------------------------------------------- + // + // Returns true if there is a matched icon mapping for the given page and + // icon type. + // The matched icon mapping is returned in the icon_mapping parameter if it is + // not NULL. + bool GetIconMappingForPageURL(const GURL& page_url, + IconType required_icon_type, + IconMapping* icon_mapping); + + // Returns true if there is any matched icon mapping for the given page. + // All matched icon mappings are returned in descent order of IconType if + // mapping_data is not NULL. + bool GetIconMappingsForPageURL(const GURL& page_url, + std::vector<IconMapping>* mapping_data); + + // Adds a mapping between the given page_url and icon_id. + // Returns the new mapping id if the adding succeeds, otherwise 0 is returned. + IconMappingID AddIconMapping(const GURL& page_url, FavIconID icon_id); + + // Updates the page and icon mapping for the given mapping_id with the given + // icon_id. + // Returns true if the update succeeded. + bool UpdateIconMapping(IconMappingID mapping_id, FavIconID icon_id); + + // Deletes the icon mapping entries for the given page url. + // Returns true if the deletion succeeded. + bool DeleteIconMappings(const GURL& page_url); + + // Checks whether a favicon is used by any URLs in the database. + bool HasMappingFor(FavIconID id); + + // Temporary IconMapping ----------------------------------------------------- + // + // Creates a temporary table to store icon mapping. Icon mapping will be + // copied to this table by AddToTemporaryIconMappingTable() and then the + // original table will be dropped, leaving only those copied mapping + // remaining. This is used to quickly delete most of the icon mapping when + // clearing history. + bool InitTemporaryIconMappingTable() { + return InitIconMappingTable(&db_, true); + } + + // Copies the given icon mapping from the "main" icon_mapping table to the + // temporary one. This is only valid in between calls to + // InitTemporaryIconMappingTable() + // and CommitTemporaryIconMappingTable(). + // + // The ID of the favicon will change when this copy takes place. The new ID + // is returned, or 0 on failure. + IconMappingID AddToTemporaryIconMappingTable(const GURL& page_url, + const FavIconID icon_id); + + // Replaces the main icon mapping table with the temporary table created by + // InitTemporaryIconMappingTable(). This will mean all icon mapping not copied + // over will be deleted. Returns true on success. + bool CommitTemporaryIconMappingTable(); + // Temporary FavIcons -------------------------------------------------------- // Create a temporary table to store favicons. Favicons will be copied to @@ -152,6 +220,8 @@ class ThumbnailDatabase { private: friend class ExpireHistoryBackend; + FRIEND_TEST_ALL_PREFIXES(ThumbnailDatabaseTest, UpgradeToVersion4); + FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, MigrationIconMapping); // Creates the thumbnail table, returning true if the table already exists // or was successfully created. @@ -169,12 +239,37 @@ class ThumbnailDatabase { // Adds support for the new metadata on web page thumbnails. bool UpgradeToVersion3(); + // Adds support for the icon_type in favicon table. + bool UpgradeToVersion4(); + + // Migrates the icon mapping data from URL database to Thumbnail database. + // Return whether the migration succeeds. + bool MigrateIconMappingData(URLDatabase* url_db); + // Creates the index over the favicon table. This will be called during // initialization after the table is created. This is a separate function // because it is used by SwapFaviconTables to create an index over the // newly-renamed favicons table (formerly the temporary table with no index). void InitFavIconsIndex(); + // Creates the icon_map table, return true if the table already exists or was + // successfully created. + bool InitIconMappingTable(sql::Connection* db, bool is_temporary); + + // Creates the index over the icon_mapping table, This will be called during + // initialization after the table is created. This is a separate function + // because it is used by CommitTemporaryIconMappingTable to create an index + // over the newly-renamed icon_mapping table (formerly the temporary table + // with no index). + void InitIconMappingIndex(); + + // Adds a mapping between the given page_url and icon_id; The mapping will be + // added to temp_icon_mapping table if is_temporary is true. + // Returns the new mapping id if the adding succeeds, otherwise 0 is returned. + IconMappingID AddIconMapping(const GURL& page_url, + FavIconID icon_id, + bool is_temporary); + sql::Connection db_; sql::MetaTable meta_table_; diff --git a/chrome/browser/history/thumbnail_database_unittest.cc b/chrome/browser/history/thumbnail_database_unittest.cc index 6483a25..cd40298 100644 --- a/chrome/browser/history/thumbnail_database_unittest.cc +++ b/chrome/browser/history/thumbnail_database_unittest.cc @@ -12,9 +12,13 @@ #include "base/path_service.h" #include "base/ref_counted_memory.h" #include "base/scoped_temp_dir.h" +#include "chrome/browser/history/history_database.h" +#include "chrome/browser/history/history_unittest_base.h" #include "chrome/browser/history/thumbnail_database.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/thumbnail_score.h" +#include "chrome/test/testing_profile.h" #include "chrome/tools/profiles/thumbnail-inl.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,7 +62,7 @@ class ThumbnailDatabaseTest : public testing::Test { file_name_ = temp_dir_.path().AppendASCII("TestThumbnails.db"); new_file_name_ = temp_dir_.path().AppendASCII("TestFavicons.db"); - + history_db_name_ = temp_dir_.path().AppendASCII("TestHistory.db"); google_bitmap_.reset( gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail))); } @@ -68,18 +72,54 @@ class ThumbnailDatabaseTest : public testing::Test { ScopedTempDir temp_dir_; FilePath file_name_; FilePath new_file_name_; + FilePath history_db_name_; +}; + +class IconMappingMigrationTest : public HistoryUnitTestBase { + public: + IconMappingMigrationTest() { + } + ~IconMappingMigrationTest() { + } + + protected: + virtual void SetUp() { + profile_.reset(new TestingProfile); + + FilePath data_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); + data_path = data_path.AppendASCII("History"); + + history_db_name_ = profile_->GetPath().Append(chrome::kHistoryFilename); + // Set up history and thumbnails as they would be before migration. + ASSERT_NO_FATAL_FAILURE( + ExecuteSQLScript(data_path.AppendASCII("history.20.sql"), + history_db_name_)); + thumbnail_db_name_ = + profile_->GetPath().Append(chrome::kThumbnailsFilename); + ASSERT_NO_FATAL_FAILURE( + ExecuteSQLScript(data_path.AppendASCII("thumbnails.3.sql"), + thumbnail_db_name_)); + } + + protected: + FilePath history_db_name_; + FilePath thumbnail_db_name_; + + private: + scoped_ptr<TestingProfile> profile_; }; TEST_F(ThumbnailDatabaseTest, GetFaviconAfterMigrationToTopSites) { ThumbnailDatabase db; - ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL)); + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); db.BeginTransaction(); std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); GURL url("http://google.com"); - FavIconID id = db.AddFavIcon(url); + FavIconID id = db.AddFavIcon(url, FAV_ICON); base::Time time = base::Time::Now(); db.SetFavIcon(id, favicon, time); EXPECT_TRUE(db.RenameAndDropThumbnails(file_name_, new_file_name_)); @@ -96,4 +136,327 @@ TEST_F(ThumbnailDatabaseTest, GetFaviconAfterMigrationToTopSites) { favicon_out.begin())); } +TEST_F(ThumbnailDatabaseTest, AddIconMapping) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, TOUCH_ICON); + EXPECT_NE(0, id); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + + EXPECT_NE(0, db.AddIconMapping(url, id)); + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + EXPECT_EQ(1u, icon_mapping.size()); + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(id, icon_mapping.front().icon_id); +} + +TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, TOUCH_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + + EXPECT_TRUE(0 < db.AddIconMapping(url, id)); + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + ASSERT_EQ(1u, icon_mapping.size()); + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(id, icon_mapping.front().icon_id); + + GURL url1("http://www.google.com/"); + FavIconID new_id = db.AddFavIcon(url1, TOUCH_ICON); + EXPECT_TRUE(db.UpdateIconMapping(icon_mapping.front().mapping_id, new_id)); + + icon_mapping.clear(); + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + ASSERT_EQ(1u, icon_mapping.size()); + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(new_id, icon_mapping.front().icon_id); + EXPECT_NE(id, icon_mapping.front().icon_id); +} + +TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, TOUCH_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + EXPECT_TRUE(0 < db.AddIconMapping(url, id)); + + FavIconID id2 = db.AddFavIcon(url, FAV_ICON); + db.SetFavIcon(id2, favicon, time); + EXPECT_TRUE(0 < db.AddIconMapping(url, id2)); + ASSERT_NE(id, id2); + + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + ASSERT_EQ(2u, icon_mapping.size()); + EXPECT_EQ(icon_mapping.front().icon_type, TOUCH_ICON); + EXPECT_TRUE(db.GetIconMappingForPageURL(url, FAV_ICON, NULL)); + + db.DeleteIconMappings(url); + + EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL)); + EXPECT_FALSE(db.GetIconMappingForPageURL(url, FAV_ICON, NULL)); +} + +TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + + FavIconID id1 = db.AddFavIcon(url, TOUCH_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id1, favicon, time); + EXPECT_TRUE(0 < db.AddIconMapping(url, id1)); + + FavIconID id2 = db.AddFavIcon(url, FAV_ICON); + EXPECT_NE(id1, id2); + db.SetFavIcon(id2, favicon, time); + EXPECT_TRUE(0 < db.AddIconMapping(url, id2)); + + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + ASSERT_EQ(2u, icon_mapping.size()); + EXPECT_NE(icon_mapping[0].icon_id, icon_mapping[1].icon_id); + EXPECT_TRUE(icon_mapping[0].icon_id == id1 && icon_mapping[1].icon_id == id2); +} + +TEST_F(ThumbnailDatabaseTest, UpgradeToVersion4) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + const char* name = "favicons"; + std::string sql; + sql.append("DROP TABLE IF EXISTS "); + sql.append(name); + EXPECT_TRUE(db.db_.Execute(sql.c_str())); + + sql.resize(0); + sql.append("CREATE TABLE "); + sql.append(name); + sql.append("(" + "id INTEGER PRIMARY KEY," + "url LONGVARCHAR NOT NULL," + "last_updated INTEGER DEFAULT 0," + "image_data BLOB)"); + EXPECT_TRUE(db.db_.Execute(sql.c_str())); + + EXPECT_TRUE(db.UpgradeToVersion4()); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, TOUCH_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + + EXPECT_TRUE(0 < db.AddIconMapping(url, id)); + IconMapping icon_mapping; + EXPECT_TRUE(db.GetIconMappingForPageURL(url, TOUCH_ICON, &icon_mapping)); + EXPECT_EQ(url, icon_mapping.page_url); + EXPECT_EQ(id, icon_mapping.icon_id); +} + +TEST_F(ThumbnailDatabaseTest, TemporayIconMapping) { + ThumbnailDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + + db.BeginTransaction(); + + EXPECT_TRUE(db.InitTemporaryIconMappingTable()); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, FAV_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + + db.AddToTemporaryIconMappingTable(url, id); + db.CommitTemporaryIconMappingTable(); + IconMapping icon_mapping; + EXPECT_TRUE(db.GetIconMappingForPageURL(url, FAV_ICON, &icon_mapping)); + EXPECT_EQ(id, icon_mapping.icon_id); + EXPECT_EQ(url, icon_mapping.page_url); +} + +TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + // Add a favicon + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + GURL url("http://google.com"); + FavIconID id = db.AddFavIcon(url, FAV_ICON); + base::Time time = base::Time::Now(); + db.SetFavIcon(id, favicon, time); + + EXPECT_NE(0, db.AddIconMapping(url, id)); + std::vector<IconMapping> icon_mapping; + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(id, icon_mapping.front().icon_id); + EXPECT_EQ(FAV_ICON, icon_mapping.front().icon_type); + + // Add a touch icon + std::vector<unsigned char> data2(blob2, blob2 + sizeof(blob2)); + scoped_refptr<RefCountedBytes> favicon2(new RefCountedBytes(data)); + + FavIconID id2 = db.AddFavIcon(url, TOUCH_ICON); + db.SetFavIcon(id2, favicon2, time); + EXPECT_NE(0, db.AddIconMapping(url, id2)); + + icon_mapping.clear(); + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(id2, icon_mapping.front().icon_id); + EXPECT_EQ(TOUCH_ICON, icon_mapping.front().icon_type); + + // Add a touch precomposed icon + scoped_refptr<RefCountedBytes> favicon3(new RefCountedBytes(data2)); + + FavIconID id3 = db.AddFavIcon(url, TOUCH_PRECOMPOSED_ICON); + db.SetFavIcon(id3, favicon3, time); + EXPECT_NE(0, db.AddIconMapping(url, id3)); + + icon_mapping.clear(); + EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping)); + + EXPECT_EQ(url, icon_mapping.front().page_url); + EXPECT_EQ(id3, icon_mapping.front().icon_id); + EXPECT_EQ(TOUCH_PRECOMPOSED_ICON, icon_mapping.front().icon_type); +} + +TEST_F(ThumbnailDatabaseTest, HasMappingFor) { + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL)); + db.BeginTransaction(); + + std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); + scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data)); + + // Add a favicon which will have icon_mappings + FavIconID id1 = db.AddFavIcon(GURL("http://google.com"), FAV_ICON); + EXPECT_NE(id1, 0); + base::Time time = base::Time::Now(); + db.SetFavIcon(id1, favicon, time); + + // Add another type of favicon + FavIconID id2 = db.AddFavIcon(GURL("http://www.google.com/icon"), TOUCH_ICON); + EXPECT_NE(id2, 0); + time = base::Time::Now(); + db.SetFavIcon(id2, favicon, time); + + // Add 3rd favicon + FavIconID id3 = db.AddFavIcon(GURL("http://www.google.com/icon"), TOUCH_ICON); + EXPECT_NE(id3, 0); + time = base::Time::Now(); + db.SetFavIcon(id3, favicon, time); + + // Add 2 icon mapping + GURL page_url("http://www.google.com"); + EXPECT_TRUE(db.AddIconMapping(page_url, id1)); + EXPECT_TRUE(db.AddIconMapping(page_url, id2)); + + EXPECT_TRUE(db.HasMappingFor(id1)); + EXPECT_TRUE(db.HasMappingFor(id2)); + EXPECT_FALSE(db.HasMappingFor(id3)); + + // Remove all mappings + db.DeleteIconMappings(page_url); + EXPECT_FALSE(db.HasMappingFor(id1)); + EXPECT_FALSE(db.HasMappingFor(id2)); + EXPECT_FALSE(db.HasMappingFor(id3)); +} + +TEST_F(IconMappingMigrationTest, TestIconMappingMigration) { + HistoryDatabase history_db; + ASSERT_TRUE(history_db.db_.Open(history_db_name_)); + history_db.BeginTransaction(); + + const GURL icon1 = GURL("http://www.google.com/favicon.ico"); + const GURL icon2 = GURL("http://www.yahoo.com/favicon.ico"); + + ThumbnailDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(thumbnail_db_name_, NULL, &history_db)); + db.BeginTransaction(); + + // Migration should be done. + // Test one icon_mapping. + GURL page_url1 = GURL("http://google.com/"); + std::vector<IconMapping> icon_mappings; + EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url1, &icon_mappings)); + ASSERT_EQ(1u, icon_mappings.size()); + EXPECT_EQ(FAV_ICON, icon_mappings[0].icon_type); + EXPECT_EQ(page_url1, icon_mappings[0].page_url); + EXPECT_EQ(1, icon_mappings[0].icon_id); + base::Time time; + std::vector<unsigned char> out_data; + GURL out_icon_url; + ASSERT_TRUE(db.GetFavIcon( + icon_mappings[0].icon_id, &time, &out_data, &out_icon_url)); + EXPECT_EQ(icon1, out_icon_url); + + // Test a page which has the same icon. + GURL page_url3 = GURL("http://www.google.com/"); + icon_mappings.clear(); + EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url3, &icon_mappings)); + ASSERT_EQ(1u, icon_mappings.size()); + EXPECT_EQ(FAV_ICON, icon_mappings[0].icon_type); + EXPECT_EQ(page_url3, icon_mappings[0].page_url); + EXPECT_EQ(1, icon_mappings[0].icon_id); + + // Test a icon_mapping with different IconID. + GURL page_url2 = GURL("http://yahoo.com/"); + icon_mappings.clear(); + EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url2, &icon_mappings)); + ASSERT_EQ(1u, icon_mappings.size()); + EXPECT_EQ(FAV_ICON, icon_mappings[0].icon_type); + EXPECT_EQ(page_url2, icon_mappings[0].page_url); + EXPECT_EQ(2, icon_mappings[0].icon_id); + ASSERT_TRUE(db.GetFavIcon( + icon_mappings[0].icon_id, &time, &out_data, &out_icon_url)); + EXPECT_EQ(icon2, out_icon_url); + + // Test a page without icon + GURL page_url4 = GURL("http://www.google.com/blank.html"); + EXPECT_FALSE(db.GetIconMappingsForPageURL(page_url4, NULL)); +} + } // namespace history diff --git a/chrome/browser/history/top_sites_unittest.cc b/chrome/browser/history/top_sites_unittest.cc index 866ba3c..f818bb7 100644 --- a/chrome/browser/history/top_sites_unittest.cc +++ b/chrome/browser/history/top_sites_unittest.cc @@ -14,6 +14,7 @@ #include "chrome/browser/history/history_database.h" #include "chrome/browser/history/history_marshaling.h" #include "chrome/browser/history/history_notifications.h" +#include "chrome/browser/history/history_unittest_base.h" #include "chrome/browser/history/top_sites.h" #include "chrome/browser/history/top_sites_backend.h" #include "chrome/browser/history/top_sites_cache.h" @@ -124,7 +125,7 @@ bool ThumbnailsAreEqual(RefCountedBytes* t1, RefCountedBytes* t2) { } // namespace -class TopSitesTest : public testing::Test { +class TopSitesTest : public HistoryUnitTestBase { public: TopSitesTest() : ui_thread_(BrowserThread::UI, &message_loop_), @@ -334,12 +335,12 @@ class TopSitesMigrationTest : public TopSitesTest { 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))); + ASSERT_NO_FATAL_FAILURE(ExecuteSQLScript( + data_path.AppendASCII("history.19.sql"), + profile()->GetPath().Append(chrome::kHistoryFilename))); + ASSERT_NO_FATAL_FAILURE(ExecuteSQLScript( + data_path.AppendASCII("thumbnails.3.sql"), + profile()->GetPath().Append(chrome::kThumbnailsFilename))); profile()->CreateHistoryService(false, false); profile()->CreateTopSites(); @@ -377,25 +378,6 @@ class TopSitesMigrationTest : public TopSitesTest { } 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)); - - // Replace the 'last_visit_time', 'visit_time', 'time_slot' values in this - // SQL with the current time. - int64 now = base::Time::Now().ToInternalValue(); - std::vector<std::string> sql_time; - sql_time.push_back(StringPrintf("%" PRId64, now)); // last_visit_time - sql_time.push_back(StringPrintf("%" PRId64, now)); // visit_time - sql_time.push_back(StringPrintf("%" PRId64, now)); // time_slot - sql = ReplaceStringPlaceholders(sql, sql_time, NULL); - - sql::Connection connection; - ASSERT_TRUE(connection.Open(db_path)); - ASSERT_TRUE(connection.Execute(sql.c_str())); - } DISALLOW_COPY_AND_ASSIGN(TopSitesMigrationTest); }; diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc index f6c041c..78ca0c5 100644 --- a/chrome/browser/history/url_database.cc +++ b/chrome/browser/history/url_database.cc @@ -20,6 +20,19 @@ namespace history { const char URLDatabase::kURLRowFields[] = HISTORY_URL_ROW_FIELDS; const int URLDatabase::kNumURLRowFields = 9; +URLDatabase::URLEnumeratorBase::URLEnumeratorBase() + : initialized_(false) { +} + +URLDatabase::URLEnumeratorBase::~URLEnumeratorBase() { +} + +URLDatabase::URLEnumerator::URLEnumerator() { +} + +URLDatabase::IconMappingEnumerator::IconMappingEnumerator() { +} + bool URLDatabase::URLEnumerator::GetNextURL(URLRow* r) { if (statement_.Step()) { FillURLRow(statement_, r); @@ -28,7 +41,17 @@ bool URLDatabase::URLEnumerator::GetNextURL(URLRow* r) { return false; } -URLDatabase::URLDatabase() : has_keyword_search_terms_(false) { +bool URLDatabase::IconMappingEnumerator::GetNextIconMapping(IconMapping* r) { + if (!statement_.Step()) + return false; + + r->page_url = GURL(statement_.ColumnString(0)); + r->icon_id = statement_.ColumnInt64(1); + return true; +} + +URLDatabase::URLDatabase() + : has_keyword_search_terms_(false) { } URLDatabase::~URLDatabase() { @@ -57,7 +80,6 @@ void URLDatabase::FillURLRow(sql::Statement& s, history::URLRow* i) { i->typed_count_ = s.ColumnInt(4); i->last_visit_ = base::Time::FromInternalValue(s.ColumnInt64(5)); i->hidden_ = s.ColumnInt(6) != 0; - i->favicon_id_ = s.ColumnInt64(7); } bool URLDatabase::GetURLRow(URLID url_id, URLRow* info) { @@ -112,7 +134,7 @@ bool URLDatabase::UpdateURLRow(URLID url_id, const history::URLRow& info) { sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "UPDATE urls SET title=?,visit_count=?,typed_count=?,last_visit_time=?," - "hidden=?,favicon_id=?" + "hidden=?" "WHERE id=?")); if (!statement) return false; @@ -122,8 +144,7 @@ bool URLDatabase::UpdateURLRow(URLID url_id, statement.BindInt(2, info.typed_count()); statement.BindInt64(3, info.last_visit().ToInternalValue()); statement.BindInt(4, info.hidden() ? 1 : 0); - statement.BindInt64(5, info.favicon_id()); - statement.BindInt64(6, url_id); + statement.BindInt64(5, url_id); return statement.Run(); } @@ -135,8 +156,8 @@ URLID URLDatabase::AddURLInternal(const history::URLRow& info, // invalid in the insert syntax. #define ADDURL_COMMON_SUFFIX \ " (url, title, visit_count, typed_count, "\ - "last_visit_time, hidden, favicon_id) "\ - "VALUES (?,?,?,?,?,?,?)" + "last_visit_time, hidden) "\ + "VALUES (?,?,?,?,?,?)" const char* statement_name; const char* statement_sql; if (is_temporary) { @@ -161,7 +182,6 @@ URLID URLDatabase::AddURLInternal(const history::URLRow& info, statement.BindInt(3, info.typed_count()); statement.BindInt64(4, info.last_visit().ToInternalValue()); statement.BindInt(5, info.hidden() ? 1 : 0); - statement.BindInt64(6, info.favicon_id()); if (!statement.Run()) { VLOG(0) << "Failed to add url " << info.url().possibly_invalid_spec() @@ -257,14 +277,17 @@ bool URLDatabase::InitURLEnumeratorForSignificant(URLEnumerator* enumerator) { return true; } -bool URLDatabase::IsFavIconUsed(FavIconID favicon_id) { - sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, - "SELECT id FROM urls WHERE favicon_id=? LIMIT 1")); - if (!statement) +bool URLDatabase::InitIconMappingEnumeratorForEverything( + IconMappingEnumerator* enumerator) { + DCHECK(!enumerator->initialized_); + enumerator->statement_.Assign(GetDB().GetUniqueStatement( + "SELECT url, favicon_id FROM urls WHERE favicon_id <> 0")); + if (!enumerator->statement_) { + NOTREACHED() << GetDB().GetErrorMessage(); return false; - - statement.BindInt64(0, favicon_id); - return statement.Step(); + } + enumerator->initialized_ = true; + return true; } void URLDatabase::AutocompleteForPrefix(const string16& prefix, @@ -480,15 +503,6 @@ void URLDatabase::GetMostRecentKeywordSearchTerms( } } -bool URLDatabase::MigrateFromVersion11ToVersion12() { - URLRow about_row; - if (GetRowForURL(GURL(chrome::kAboutBlankURL), &about_row)) { - about_row.set_favicon_id(0); - return UpdateURLRow(about_row.id(), about_row); - } - return true; -} - bool URLDatabase::DropStarredIDFromURLs() { if (!GetDB().DoesColumnExist("urls", "starred_id")) return true; // urls is already updated, no need to continue. @@ -512,9 +526,6 @@ bool URLDatabase::DropStarredIDFromURLs() { // Rename/commit the tmp table. CommitTemporaryURLTable(); - // This isn't created by CommitTemporaryURLTable. - CreateSupplimentaryURLIndices(); - return true; } @@ -534,7 +545,7 @@ bool URLDatabase::CreateURLTable(bool is_temporary) { "typed_count INTEGER DEFAULT 0 NOT NULL," "last_visit_time INTEGER NOT NULL," "hidden INTEGER DEFAULT 0 NOT NULL," - "favicon_id INTEGER DEFAULT 0 NOT NULL)"); + "favicon_id INTEGER DEFAULT 0 NOT NULL)"); // favicon_id is not used now. return GetDB().Execute(sql.c_str()); } @@ -545,9 +556,4 @@ void URLDatabase::CreateMainURLIndex() { GetDB().Execute("CREATE INDEX urls_url_index ON urls (url)"); } -void URLDatabase::CreateSupplimentaryURLIndices() { - // Add a favicon index. This is useful when we delete urls. - GetDB().Execute("CREATE INDEX urls_favicon_id_INDEX ON urls (favicon_id)"); -} - } // namespace history diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h index c821ce7..c21d925 100644 --- a/chrome/browser/history/url_database.h +++ b/chrome/browser/history/url_database.h @@ -111,20 +111,44 @@ class URLDatabase { // Enumeration --------------------------------------------------------------- + // A basic enumerator to enumerate urls database. + class URLEnumeratorBase { + public: + URLEnumeratorBase(); + virtual ~URLEnumeratorBase(); + + private: + friend class URLDatabase; + + bool initialized_; + sql::Statement statement_; + + DISALLOW_COPY_AND_ASSIGN(URLEnumeratorBase); + }; + // A basic enumerator to enumerate urls - class URLEnumerator { + class URLEnumerator : public URLEnumeratorBase { public: - URLEnumerator() : initialized_(false) { - } + URLEnumerator(); // Retreives the next url. Returns false if no more urls are available bool GetNextURL(history::URLRow* r); private: - friend class URLDatabase; + DISALLOW_COPY_AND_ASSIGN(URLEnumerator); + }; - bool initialized_; - sql::Statement statement_; + // A basic enumerator to enumerate icon mapping, it is only used for icon + // mapping migration. + class IconMappingEnumerator : public URLEnumeratorBase { + public: + IconMappingEnumerator(); + + // Retreives the next url. Returns false if no more urls are available + bool GetNextIconMapping(IconMapping* r); + + private: + DISALLOW_COPY_AND_ASSIGN(IconMappingEnumerator); }; // Initializes the given enumerator to enumerator all URLs in the database. @@ -138,9 +162,6 @@ class URLDatabase { // Favicons ------------------------------------------------------------------ - // Check whether a favicon is used by any URLs in the database. - bool IsFavIconUsed(FavIconID favicon_id); - // Autocomplete -------------------------------------------------------------- // Fills the given array with URLs matching the given prefix. They will be @@ -194,6 +215,11 @@ class URLDatabase { // the favicon couldn't be updated. bool MigrateFromVersion11ToVersion12(); + // Initializes the given enumerator to enumerator all URL and icon mappings + // in the database. Only used for icon mapping migration. + bool InitIconMappingEnumeratorForEverything( + IconMappingEnumerator* enumerator); + protected: friend class VisitDatabase; @@ -220,12 +246,8 @@ class URLDatabase { // different name but the same schema. bool CreateURLTable(bool is_temporary); // We have two tiers of indices for the URL table. The main tier is used by - // all URL databases, and is an index over the URL itself. The main history - // DB also creates indices over the favicons and bookmark IDs. The archived - // and in-memory databases don't need these supplimentary indices so we can - // save space by not creating them. + // all URL databases, and is an index over the URL itself. void CreateMainURLIndex(); - void CreateSupplimentaryURLIndices(); // Ensures the keyword search terms table exists. bool InitKeywordSearchTermsTable(); @@ -267,7 +289,7 @@ class URLDatabase { // string dynamically anyway, use the constant, it will save space. #define HISTORY_URL_ROW_FIELDS \ " urls.id, urls.url, urls.title, urls.visit_count, urls.typed_count, " \ - "urls.last_visit_time, urls.hidden, urls.favicon_id " + "urls.last_visit_time, urls.hidden " } // history diff --git a/chrome/browser/history/url_database_unittest.cc b/chrome/browser/history/url_database_unittest.cc index c2f22ff6..10e80c7 100644 --- a/chrome/browser/history/url_database_unittest.cc +++ b/chrome/browser/history/url_database_unittest.cc @@ -38,6 +38,12 @@ class URLDatabaseTest : public testing::Test, URLDatabaseTest() { } + protected: + // Provided for URL/VisitDatabase. + virtual sql::Connection& GetDB() { + return db_; + } + private: // Test setup. void SetUp() { @@ -50,7 +56,6 @@ class URLDatabaseTest : public testing::Test, // Initialize the tables for this test. CreateURLTable(false); CreateMainURLIndex(); - CreateSupplimentaryURLIndices(); InitKeywordSearchTermsTable(); CreateKeywordSearchTermsIndices(); } @@ -59,11 +64,6 @@ class URLDatabaseTest : public testing::Test, file_util::Delete(db_file_, false); } - // Provided for URL/VisitDatabase. - virtual sql::Connection& GetDB() { - return db_; - } - FilePath db_file_; sql::Connection db_; }; @@ -222,4 +222,50 @@ TEST_F(URLDatabaseTest, EnumeratorForSignificant) { EXPECT_EQ(3, row_count); } +TEST_F(URLDatabaseTest, IconMappingEnumerator) { + const GURL url1("http://www.google.com/"); + URLRow url_info1(url1); + url_info1.set_title(UTF8ToUTF16("Google")); + url_info1.set_visit_count(4); + url_info1.set_typed_count(2); + url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1)); + url_info1.set_hidden(false); + + // Insert a row with favicon + URLID url_id1 = AddURL(url_info1); + ASSERT_TRUE(url_id1 != 0); + + FavIconID icon_id = 1; + sql::Statement statement(GetDB().GetCachedStatement( + SQL_FROM_HERE, + "UPDATE urls SET favicon_id =? WHERE id=?")); + + ASSERT_TRUE(statement); + + statement.BindInt64(0, icon_id); + statement.BindInt64(1, url_id1); + ASSERT_TRUE(statement.Run()); + + // Insert another row without favicon + const GURL url2("http://www.google.com/no_icon"); + URLRow url_info2(url2); + url_info2.set_title(UTF8ToUTF16("Google")); + url_info2.set_visit_count(4); + url_info2.set_typed_count(2); + url_info2.set_last_visit(Time::Now() - TimeDelta::FromDays(1)); + url_info2.set_hidden(false); + + // Insert a row with favicon + URLID url_id2 = AddURL(url_info2); + ASSERT_TRUE(url_id2 != 0); + + IconMappingEnumerator e; + InitIconMappingEnumeratorForEverything(&e); + IconMapping icon_mapping; + ASSERT_TRUE(e.GetNextIconMapping(&icon_mapping)); + ASSERT_EQ(url1, icon_mapping.page_url); + ASSERT_EQ(icon_id, icon_mapping.icon_id); + ASSERT_FALSE(e.GetNextIconMapping(&icon_mapping)); +} + } // namespace history diff --git a/chrome/browser/history/visit_database_unittest.cc b/chrome/browser/history/visit_database_unittest.cc index 28a0e15..1cf1868 100644 --- a/chrome/browser/history/visit_database_unittest.cc +++ b/chrome/browser/history/visit_database_unittest.cc @@ -52,7 +52,6 @@ class VisitDatabaseTest : public PlatformTest, // Initialize the tables for this test. CreateURLTable(false); CreateMainURLIndex(); - CreateSupplimentaryURLIndices(); InitVisitTable(); } void TearDown() { diff --git a/chrome/browser/importer/importer_messages.h b/chrome/browser/importer/importer_messages.h index 46d6c98..854343a 100644 --- a/chrome/browser/importer/importer_messages.h +++ b/chrome/browser/importer/importer_messages.h @@ -78,7 +78,6 @@ struct ParamTraits<history::URLRow> { WriteParam(m, p.typed_count()); WriteParam(m, p.last_visit()); WriteParam(m, p.hidden()); - WriteParam(m, p.favicon_id()); } static bool Read(const Message* m, void** iter, param_type* p) { history::URLID id; @@ -103,7 +102,6 @@ struct ParamTraits<history::URLRow> { p->set_typed_count(typed_count); p->set_last_visit(last_visit); p->set_hidden(hidden); - p->set_favicon_id(favicon_id); return true; } static void Log(const param_type& p, std::string* l) { @@ -121,8 +119,6 @@ struct ParamTraits<history::URLRow> { LogParam(p.last_visit(), l); l->append(", "); LogParam(p.hidden(), l); - l->append(", "); - LogParam(p.favicon_id(), l); l->append(")"); } }; // ParamTraits<history::URLRow> diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index f849a4f..069b884 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1318,6 +1318,8 @@ 'browser/history/history_querying_unittest.cc', 'browser/history/history_types_unittest.cc', 'browser/history/history_unittest.cc', + 'browser/history/history_unittest_base.cc', + 'browser/history/history_unittest_base.h', 'browser/history/in_memory_url_index_unittest.cc', 'browser/history/query_parser_unittest.cc', 'browser/history/snippet_unittest.cc', diff --git a/chrome/test/data/History/history.20.sql b/chrome/test/data/History/history.20.sql new file mode 100644 index 0000000..2adb73c --- /dev/null +++ b/chrome/test/data/History/history.20.sql @@ -0,0 +1,11 @@ +BEGIN TRANSACTION; +CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,value LONGVARCHAR); +INSERT INTO "meta" VALUES('version','20'); +INSERT INTO "meta" VALUES('last_compatible_version','16'); +CREATE TABLE urls(id INTEGER PRIMARY KEY,url LONGVARCHAR,title LONGVARCHAR,visit_count INTEGER DEFAULT 0 NOT NULL,typed_count INTEGER DEFAULT 0 NOT NULL,last_visit_time INTEGER NOT NULL,hidden INTEGER DEFAULT 0 NOT NULL,favicon_id INTEGER DEFAULT 0 NOT NULL); +INSERT INTO "urls" VALUES(1,'http://google.com/','Google',1,1,$1,0,1); +INSERT INTO "urls" VALUES(2,'http://www.google.com/','Google',1,0,$1,0,1); +INSERT INTO "urls" VALUES(3,'http://www.google.com/blank.html','',1,0,$1,1,0); +INSERT INTO "urls" VALUES(4,'http://yahoo.com/','Yahoo!',1,1,$1,0,2); +INSERT INTO "urls" VALUES(5,'http://www.yahoo.com/','Yahoo!',1,0,$1,0,2); +COMMIT; diff --git a/chrome/test/data/History/thumbnails.3.sql b/chrome/test/data/History/thumbnails.3.sql new file mode 100644 index 0000000..ab7d395 --- /dev/null +++ b/chrome/test/data/History/thumbnails.3.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; +CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY,value LONGVARCHAR); +INSERT INTO "meta" VALUES('version','3'); +INSERT INTO "meta" VALUES('last_compatible_version','3'); +CREATE TABLE favicons(id INTEGER PRIMARY KEY,url LONGVARCHAR NOT NULL,last_updated INTEGER DEFAULT 0,image_data BLOB); +INSERT INTO "favicons" VALUES(1,'http://www.google.com/favicon.ico',1287424416,X'FFD8FFE000104A46494600010100000100010000FFDB0043000302020302020303030304030304050805050404050A070706080C0A0C0C0B0A0B0B0D0E12100D0E110E0B0B1016101113141515150C0F171816141812141514FFDB00430103040405040509050509140D0B0D1414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414FFC00011080002000203012200021101031101FFC4001F0000010501010101010100000000000000000102030405060708090A0BFFC400B5100002010303020403050504040000017D01020300041105122131410613516107227114328191A1082342B1C11552D1F02433627282090A161718191A25262728292A3435363738393A434445464748494A535455565758595A636465666768696A737475767778797A838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE1E2E3E4E5E6E7E8E9EAF1F2F3F4F5F6F7F8F9FAFFC4001F0100030101010101010101010000000000000102030405060708090A0BFFC400B51100020102040403040705040400010277000102031104052131061241510761711322328108144291A1B1C109233352F0156272D10A162434E125F11718191A262728292A35363738393A434445464748494A535455565758595A636465666768696A737475767778797A82838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE2E3E4E5E6E7E8E9EAF2F3F4F5F6F7F8F9FAFFDA000C03010002110311003F00F9D28A28AFC30FF54CFFD9'); +INSERT INTO "favicons" VALUES(2,'http://www.yahoo.com/favicon.ico',1287424428,X'FFD8FFE000104A46494600010100000100010000FFDB0043000302020302020303030304030304050805050404050A070706080C0A0C0C0B0A0B0B0D0E12100D0E110E0B0B1016101113141515150C0F171816141812141514FFDB00430103040405040509050509140D0B0D1414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414FFC00011080003000303012200021101031101FFC4001F0000010501010101010100000000000000000102030405060708090A0BFFC400B5100002010303020403050504040000017D01020300041105122131410613516107227114328191A1082342B1C11552D1F02433627282090A161718191A25262728292A3435363738393A434445464748494A535455565758595A636465666768696A737475767778797A838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE1E2E3E4E5E6E7E8E9EAF1F2F3F4F5F6F7F8F9FAFFC4001F0100030101010101010101010000000000000102030405060708090A0BFFC400B51100020102040403040705040400010277000102031104052131061241510761711322328108144291A1B1C109233352F0156272D10A162434E125F11718191A262728292A35363738393A434445464748494A535455565758595A636465666768696A737475767778797A82838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE2E3E4E5E6E7E8E9EAF2F3F4F5F6F7F8F9FAFFDA000C03010002110311003F00F9D28A28AFC30FF54CFFD9'); +CREATE INDEX favicons_url ON favicons(url); +COMMIT; diff --git a/chrome/test/data/profiles/typical_history/Default/Favicons b/chrome/test/data/profiles/typical_history/Default/Favicons Binary files differindex b42db08..716aea0 100644 --- a/chrome/test/data/profiles/typical_history/Default/Favicons +++ b/chrome/test/data/profiles/typical_history/Default/Favicons diff --git a/chrome/test/data/profiles/typical_history/Default/History b/chrome/test/data/profiles/typical_history/Default/History Binary files differindex f4e176b..00299e3 100644 --- a/chrome/test/data/profiles/typical_history/Default/History +++ b/chrome/test/data/profiles/typical_history/Default/History |