Index: components/ntp_tiles/most_visited_sites_unittest.cc |
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc |
index 029968dfa138b8e924036cd006e8badde08cd2a1..bc7f877a2a8137c8dac90eb3e786ca09afb9868b 100644 |
--- a/components/ntp_tiles/most_visited_sites_unittest.cc |
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc |
@@ -10,9 +10,17 @@ |
#include <ostream> |
#include <vector> |
+#include "base/callback_list.h" |
+#include "base/command_line.h" |
#include "base/macros.h" |
#include "base/memory/ptr_util.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "components/history/core/browser/top_sites.h" |
+#include "components/ntp_tiles/icon_cacher.h" |
+#include "components/ntp_tiles/switches.h" |
+#include "components/sync_preferences/testing_pref_service_syncable.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -26,7 +34,23 @@ void PrintTo(const NTPTile& tile, std::ostream* os) { |
namespace { |
+using history::MostVisitedURL; |
+using history::MostVisitedURLList; |
+using history::TopSites; |
+using suggestions::ChromeSuggestion; |
+using suggestions::SuggestionsProfile; |
+using suggestions::SuggestionsService; |
+using testing::AtLeast; |
+using testing::ByMove; |
using testing::ElementsAre; |
+using testing::InSequence; |
+using testing::Invoke; |
+using testing::Mock; |
+using testing::Return; |
+using testing::ReturnRef; |
+using testing::SizeIs; |
+using testing::StrictMock; |
+using testing::_; |
MATCHER_P3(MatchesTile, |
title, |
@@ -50,6 +74,392 @@ NTPTile MakeTile(const std::string& title, |
return tile; |
} |
+ChromeSuggestion MakeSuggestion(const std::string& title, |
+ const std::string& url) { |
+ ChromeSuggestion suggestion; |
+ suggestion.set_title(title); |
+ suggestion.set_url(url); |
+ return suggestion; |
+} |
+ |
+SuggestionsProfile MakeProfile( |
+ const std::vector<ChromeSuggestion>& suggestions) { |
+ SuggestionsProfile profile; |
+ for (const ChromeSuggestion& suggestion : suggestions) { |
+ *profile.add_suggestions() = suggestion; |
+ } |
+ return profile; |
+} |
+ |
+MostVisitedURL MakeMostVisitedURL(const std::string& title, |
+ const std::string& url) { |
+ MostVisitedURL result; |
+ result.title = base::ASCIIToUTF16(title); |
+ result.url = GURL(url); |
+ return result; |
+} |
+ |
+class MockTopSites : public TopSites { |
+ public: |
+ MOCK_METHOD0(ShutdownOnUIThread, void()); |
+ MOCK_METHOD3(SetPageThumbnail, |
+ bool(const GURL& url, |
+ const gfx::Image& thumbnail, |
+ const ThumbnailScore& score)); |
+ MOCK_METHOD3(SetPageThumbnailToJPEGBytes, |
+ bool(const GURL& url, |
+ const base::RefCountedMemory* memory, |
+ const ThumbnailScore& score)); |
+ MOCK_METHOD2(GetMostVisitedURLs, |
+ void(const GetMostVisitedURLsCallback& callback, |
+ bool include_forced_urls)); |
+ MOCK_METHOD3(GetPageThumbnail, |
+ bool(const GURL& url, |
+ bool prefix_match, |
+ scoped_refptr<base::RefCountedMemory>* bytes)); |
+ MOCK_METHOD2(GetPageThumbnailScore, |
+ bool(const GURL& url, ThumbnailScore* score)); |
+ MOCK_METHOD2(GetTemporaryPageThumbnailScore, |
+ bool(const GURL& url, ThumbnailScore* score)); |
+ MOCK_METHOD0(SyncWithHistory, void()); |
+ MOCK_CONST_METHOD0(HasBlacklistedItems, bool()); |
+ MOCK_METHOD1(AddBlacklistedURL, void(const GURL& url)); |
+ MOCK_METHOD1(RemoveBlacklistedURL, void(const GURL& url)); |
+ MOCK_METHOD1(IsBlacklisted, bool(const GURL& url)); |
+ MOCK_METHOD0(ClearBlacklistedURLs, void()); |
+ MOCK_METHOD0(StartQueryForMostVisited, base::CancelableTaskTracker::TaskId()); |
+ MOCK_METHOD1(IsKnownURL, bool(const GURL& url)); |
+ MOCK_CONST_METHOD1(GetCanonicalURLString, |
+ const std::string&(const GURL& url)); |
+ MOCK_METHOD0(IsNonForcedFull, bool()); |
+ MOCK_METHOD0(IsForcedFull, bool()); |
+ MOCK_CONST_METHOD0(loaded, bool()); |
+ MOCK_METHOD0(GetPrepopulatedPages, history::PrepopulatedPageList()); |
+ MOCK_METHOD2(AddForcedURL, bool(const GURL& url, const base::Time& time)); |
+ MOCK_METHOD1(OnNavigationCommitted, void(const GURL& url)); |
+ |
+ protected: |
+ ~MockTopSites() override = default; |
+}; |
+ |
+class MockSuggestionsService : public SuggestionsService { |
+ public: |
+ MOCK_METHOD0(FetchSuggestionsData, bool()); |
+ MOCK_CONST_METHOD0(GetSuggestionsDataFromCache, |
+ base::Optional<SuggestionsProfile>()); |
+ MOCK_METHOD1(AddCallback, |
+ std::unique_ptr<ResponseCallbackList::Subscription>( |
+ const ResponseCallback& callback)); |
+ MOCK_METHOD2(GetPageThumbnail, |
+ void(const GURL& url, const BitmapCallback& callback)); |
+ MOCK_METHOD3(GetPageThumbnailWithURL, |
+ void(const GURL& url, |
+ const GURL& thumbnail_url, |
+ const BitmapCallback& callback)); |
+ MOCK_METHOD1(BlacklistURL, bool(const GURL& candidate_url)); |
+ MOCK_METHOD1(UndoBlacklistURL, bool(const GURL& url)); |
+ MOCK_METHOD0(ClearBlacklist, void()); |
+}; |
+ |
+class MockPopularSites : public PopularSites { |
+ public: |
+ MOCK_METHOD2(StartFetch, |
+ void(bool force_download, const FinishedCallback& callback)); |
+ MOCK_CONST_METHOD0(sites, const SitesVector&()); |
+ MOCK_CONST_METHOD0(GetLastURLFetched, GURL()); |
+ MOCK_CONST_METHOD0(local_path, const base::FilePath&()); |
+ MOCK_METHOD0(GetURLToFetch, GURL()); |
+ MOCK_METHOD0(GetCountryToFetch, std::string()); |
+ MOCK_METHOD0(GetVersionToFetch, std::string()); |
+}; |
+ |
+class MockMostVisitedSitesObserver : public MostVisitedSites::Observer { |
+ public: |
+ MOCK_METHOD1(OnMostVisitedURLsAvailable, void(const NTPTilesVector& tiles)); |
+ MOCK_METHOD1(OnIconMadeAvailable, void(const GURL& site_url)); |
+}; |
+ |
+// CallbackList-like container without Subscription, mimic-ing the |
+// implementation in TopSites (which doesn't use base::CallbackList). |
+class TopSitesCallbackList { |
+ public: |
+ // Second argument declared to match the signature of GetMostVisitedURLs(). |
+ void Add(const TopSites::GetMostVisitedURLsCallback& callback, |
+ testing::Unused) { |
+ callbacks_.push_back(callback); |
+ } |
+ |
+ void ClearAndNotify(const MostVisitedURLList& list) { |
+ std::vector<TopSites::GetMostVisitedURLsCallback> callbacks; |
+ callbacks.swap(callbacks_); |
+ for (const auto& callback : callbacks) { |
+ callback.Run(list); |
+ } |
+ } |
+ |
+ bool empty() const { return callbacks_.empty(); } |
+ |
+ private: |
+ std::vector<TopSites::GetMostVisitedURLsCallback> callbacks_; |
+}; |
+ |
+class MostVisitedSitesTest : public ::testing::Test { |
+ protected: |
+ MostVisitedSitesTest() |
+ : mock_top_sites_(new StrictMock<MockTopSites>()), |
+ mock_popular_sites_(new StrictMock<MockPopularSites>()), |
+ most_visited_sites_(&pref_service_, |
+ mock_top_sites_, |
+ &mock_suggestions_service_, |
+ base::WrapUnique(mock_popular_sites_), |
+ /*icon_cacher=*/nullptr, |
+ /*supervisor=*/nullptr) { |
+ MostVisitedSites::RegisterProfilePrefs(pref_service_.registry()); |
+ // TODO(mastiz): Add test coverage including Popular Sites. |
+ base::CommandLine::ForCurrentProcess()->AppendSwitch( |
+ switches::kDisableNTPPopularSites); |
+ // PopularSites::sites() might be called even if the feature is disabled. |
+ // An empty vector is returned because there was no actual fetch. |
+ EXPECT_CALL(*mock_popular_sites_, sites()) |
+ .Times(AtLeast(0)) |
+ .WillRepeatedly(ReturnRef(empty_popular_sites_vector_)); |
+ } |
+ |
+ bool VerifyAndClearExpectations() { |
+ base::RunLoop().RunUntilIdle(); |
+ // Note that we don't verify or clear mock_popular_sites_, since there's |
+ // no interaction expected except sites() possibly being called, which is |
+ // verified during teardown. |
+ return Mock::VerifyAndClearExpectations(mock_top_sites_.get()) && |
+ Mock::VerifyAndClearExpectations(&mock_suggestions_service_) && |
+ Mock::VerifyAndClearExpectations(&mock_observer_); |
+ } |
+ |
+ base::CallbackList<SuggestionsService::ResponseCallback::RunType> |
+ suggestions_service_callbacks_; |
+ TopSitesCallbackList top_sites_callbacks_; |
+ |
+ base::MessageLoop message_loop_; |
+ sync_preferences::TestingPrefServiceSyncable pref_service_; |
+ scoped_refptr<StrictMock<MockTopSites>> mock_top_sites_; |
+ StrictMock<MockSuggestionsService> mock_suggestions_service_; |
+ StrictMock<MockPopularSites>* const mock_popular_sites_; |
+ StrictMock<MockMostVisitedSitesObserver> mock_observer_; |
+ MostVisitedSites most_visited_sites_; |
+ const PopularSites::SitesVector empty_popular_sites_vector_; |
+}; |
+ |
+TEST_F(MostVisitedSitesTest, ShouldStartNoCallInConstructor) { |
+ // No call to mocks expected by the mere fact of instantiating |
+ // MostVisitedSites. |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+class MostVisitedSitesWithCacheHitTest : public MostVisitedSitesTest { |
+ public: |
+ // Constructor sets the common expectations for the case where suggestions |
+ // service has cached results when the observer is registered. |
+ MostVisitedSitesWithCacheHitTest() { |
+ InSequence seq; |
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); |
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_)) |
+ .WillOnce(Invoke(&suggestions_service_callbacks_, |
+ &SuggestionsService::ResponseCallbackList::Add)); |
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache()) |
+ .WillOnce(Return(MakeProfile({ |
+ MakeSuggestion("Site 1", "http://site1/"), |
+ MakeSuggestion("Site 2", "http://site2/"), |
+ MakeSuggestion("Site 3", "http://site3/"), |
+ MakeSuggestion("Site 4", "http://site4/"), |
+ }))); |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre( |
+ MatchesTile("Site 1", "http://site1/", |
+ NTPTileSource::SUGGESTIONS_SERVICE), |
+ MatchesTile("Site 2", "http://site2/", |
+ NTPTileSource::SUGGESTIONS_SERVICE), |
+ MatchesTile("Site 3", "http://site3/", |
+ NTPTileSource::SUGGESTIONS_SERVICE)))); |
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData()) |
+ .WillOnce(Return(true)); |
+ most_visited_sites_.SetMostVisitedURLsObserver(&mock_observer_, |
+ /*num_sites=*/3); |
+ VerifyAndClearExpectations(); |
+ |
+ EXPECT_FALSE(suggestions_service_callbacks_.empty()); |
+ EXPECT_TRUE(top_sites_callbacks_.empty()); |
+ } |
+}; |
+ |
+TEST_F(MostVisitedSitesWithCacheHitTest, ShouldFavorSuggestionsServiceCache) { |
+ // Constructor sets basic expectations for a suggestions service cache hit. |
+} |
+ |
+TEST_F(MostVisitedSitesWithCacheHitTest, |
+ ShouldPropagateUpdateBySuggestionsService) { |
+ EXPECT_CALL( |
+ mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 5", "http://site5/", NTPTileSource::SUGGESTIONS_SERVICE)))); |
+ suggestions_service_callbacks_.Notify( |
+ MakeProfile({MakeSuggestion("Site 5", "http://site5/")})); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(MostVisitedSitesWithCacheHitTest, |
+ ShouldSwitchToTopSitesIfEmptyUpdateBySuggestionsService) { |
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) |
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); |
+ suggestions_service_callbacks_.Notify(SuggestionsProfile()); |
+ VerifyAndClearExpectations(); |
+ |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 5", "http://site5/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 5", "http://site5/")}); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+class MostVisitedSitesWithEmptyCacheTest : public MostVisitedSitesTest { |
+ public: |
+ // Constructor sets the common expectations for the case where suggestions |
+ // service doesn't have cached results when the observer is registered. |
+ MostVisitedSitesWithEmptyCacheTest() { |
+ InSequence seq; |
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory()); |
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_)) |
+ .WillOnce(Invoke(&suggestions_service_callbacks_, |
+ &SuggestionsService::ResponseCallbackList::Add)); |
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache()) |
+ .WillOnce(Return(SuggestionsProfile())); // Empty cache. |
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) |
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); |
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData()) |
+ .WillOnce(Return(true)); |
+ most_visited_sites_.SetMostVisitedURLsObserver(&mock_observer_, |
+ /*num_sites=*/3); |
+ VerifyAndClearExpectations(); |
+ |
+ EXPECT_FALSE(suggestions_service_callbacks_.empty()); |
+ EXPECT_FALSE(top_sites_callbacks_.empty()); |
+ } |
+}; |
+ |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, |
+ ShouldQueryTopSitesAndSuggestionsService) { |
+ // Constructor sets basic expectations for a suggestions service cache miss. |
+} |
+ |
+// TODO(mastiz): This describes the current behavior but it actually is a bug. |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, |
+ DoesNotIgnoreTopSitesIfSuggestionsServiceFaster) { |
+ // Reply from suggestions service triggers and update to our observer. |
+ EXPECT_CALL( |
+ mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 1", "http://site1/", NTPTileSource::SUGGESTIONS_SERVICE)))); |
+ suggestions_service_callbacks_.Notify( |
+ MakeProfile({MakeSuggestion("Site 1", "http://site1/")})); |
+ VerifyAndClearExpectations(); |
+ |
+ // Reply from top sites is currently not ignored (i.e. is actually reported to |
+ // observer). |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 2", "http://site2/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 2", "http://site2/")}); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, |
+ ShouldExposeTopSitesIfSuggestionsServiceFasterButEmpty) { |
+ // Empty reply from suggestions service causes no update to our observer. |
+ // However, the current implementation issues a redundant query to TopSites. |
+ // TODO(mastiz): Avoid this redundant call. |
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) |
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); |
+ suggestions_service_callbacks_.Notify(SuggestionsProfile()); |
+ VerifyAndClearExpectations(); |
+ |
+ // Reply from top sites is propagated to observer. |
+ // TODO(mastiz): Avoid a second redundant call to the observer. |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 1", "http://site1/", NTPTileSource::TOP_SITES)))) |
+ .Times(2); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 1", "http://site1/")}); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, |
+ ShouldFavorSuggestionsServiceAlthoughSlower) { |
+ // Reply from top sites is propagated to observer. |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 1", "http://site1/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 1", "http://site1/")}); |
+ VerifyAndClearExpectations(); |
+ |
+ // Reply from suggestions service overrides top sites. |
+ InSequence seq; |
+ EXPECT_CALL( |
+ mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 2", "http://site2/", NTPTileSource::SUGGESTIONS_SERVICE)))); |
+ suggestions_service_callbacks_.Notify( |
+ MakeProfile({MakeSuggestion("Site 2", "http://site2/")})); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, |
+ ShouldIgnoreSuggestionsServiceIfSlowerAndEmpty) { |
+ // Reply from top sites is propagated to observer. |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 1", "http://site1/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 1", "http://site1/")}); |
+ VerifyAndClearExpectations(); |
+ |
+ // Reply from suggestions service is empty and thus ignored. However, the |
+ // current implementation issues a redundant query to TopSites. |
+ // TODO(mastiz): Avoid this redundant call. |
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) |
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); |
+ suggestions_service_callbacks_.Notify(SuggestionsProfile()); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(MostVisitedSitesWithEmptyCacheTest, ShouldPropagateUpdateByTopSites) { |
+ // Reply from top sites is propagated to observer. |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 1", "http://site1/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 1", "http://site1/")}); |
+ VerifyAndClearExpectations(); |
+ |
+ // Reply from suggestions service is empty and thus ignored. However, the |
+ // current implementation issues a redundant query to TopSites. |
+ // TODO(mastiz): Avoid this redundant call. |
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false)) |
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add)); |
+ suggestions_service_callbacks_.Notify(SuggestionsProfile()); |
+ VerifyAndClearExpectations(); |
+ |
+ // Update from top sites is propagated to observer. |
+ EXPECT_CALL(mock_observer_, |
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile( |
+ "Site 2", "http://site2/", NTPTileSource::TOP_SITES)))); |
+ top_sites_callbacks_.ClearAndNotify( |
+ {MakeMostVisitedURL("Site 2", "http://site2/")}); |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
// This a test for MostVisitedSites::MergeTiles(...) method, and thus has the |
// same scope as the method itself. This tests merging popular sites with |
// personal tiles. |
@@ -57,9 +467,7 @@ NTPTile MakeTile(const std::string& title, |
// - Removing blacklisted tiles. |
// - Correct host extraction from the URL. |
// - Ensuring personal tiles are not duplicated in popular tiles. |
-// |
-// TODO(mastiz): Add tests for the functionality listed above. |
-TEST(MostVisitedSitesTest, ShouldMergeTilesWithPersonalOnly) { |
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesWithPersonalOnly) { |
std::vector<NTPTile> personal_tiles{ |
MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::TOP_SITES), |
MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::TOP_SITES), |
@@ -81,7 +489,7 @@ TEST(MostVisitedSitesTest, ShouldMergeTilesWithPersonalOnly) { |
NTPTileSource::TOP_SITES))); |
} |
-TEST(MostVisitedSitesTest, ShouldMergeTilesWithPopularOnly) { |
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesWithPopularOnly) { |
std::vector<NTPTile> popular_tiles{ |
MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::POPULAR), |
MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::POPULAR), |
@@ -104,7 +512,7 @@ TEST(MostVisitedSitesTest, ShouldMergeTilesWithPopularOnly) { |
NTPTileSource::POPULAR))); |
} |
-TEST(MostVisitedSitesTest, ShouldMergeTilesFavoringPersonalOverPopular) { |
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesFavoringPersonalOverPopular) { |
std::vector<NTPTile> popular_tiles{ |
MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::POPULAR), |
MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::POPULAR), |