123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- // Copyright 2013 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 "components/ntp_tiles/most_visited_sites.h"
- #include <algorithm>
- #include <cctype>
- #include <iterator>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/feature_list.h"
- #include "base/logging.h"
- #include "base/metrics/user_metrics.h"
- #include "base/observer_list.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "base/strings/utf_string_conversions.h"
- #include "build/build_config.h"
- #include "components/ntp_tiles/constants.h"
- #include "components/ntp_tiles/deleted_tile_type.h"
- #include "components/ntp_tiles/features.h"
- #include "components/ntp_tiles/icon_cacher.h"
- #include "components/ntp_tiles/metrics.h"
- #include "components/ntp_tiles/pref_names.h"
- #include "components/ntp_tiles/switches.h"
- #include "components/pref_registry/pref_registry_syncable.h"
- #include "components/prefs/pref_service.h"
- #include "components/search/ntp_features.h"
- #include "components/webapps/common/constants.h"
- #include "extensions/buildflags/buildflags.h"
- #if BUILDFLAG(ENABLE_EXTENSIONS)
- // GN doesn't understand conditional includes, so we need nogncheck here.
- #include "extensions/common/constants.h" // nogncheck
- #endif
- using history::TopSites;
- namespace ntp_tiles {
- namespace {
- // URL host prefixes. Hosts with these prefixes often redirect to each other, or
- // have the same content.
- // Popular sites are excluded if the user has visited a page whose host only
- // differs by one of these prefixes. Even if the URL does not point to the exact
- // same page, the user will have a personalized suggestion that is more likely
- // to be of use for them.
- // A cleaner way could be checking the history for redirects but this requires
- // the page to be visited on the device.
- const char* kKnownGenericPagePrefixes[] = {
- "m.", "mobile.", // Common prefixes among popular sites.
- "edition.", // Used among news papers (CNN, Independent, ...)
- "www.", // Usually no-www domains redirect to www or vice-versa.
- // The following entry MUST REMAIN LAST as it is prefix of every string!
- ""}; // The no-www domain matches domains on same level .
- // Determine whether we need any tiles from PopularSites to fill up a grid of
- // |num_tiles| tiles.
- bool NeedPopularSites(const PrefService* prefs, int num_tiles) {
- return prefs->GetInteger(prefs::kNumPersonalTiles) < num_tiles;
- }
- bool HasHomeTile(const NTPTilesVector& tiles) {
- for (const auto& tile : tiles) {
- if (tile.source == TileSource::HOMEPAGE)
- return true;
- }
- return false;
- }
- std::string StripFirstGenericPrefix(const std::string& host) {
- for (const char* prefix : kKnownGenericPagePrefixes) {
- if (base::StartsWith(host, prefix, base::CompareCase::INSENSITIVE_ASCII)) {
- return std::string(
- base::TrimString(host, prefix, base::TrimPositions::TRIM_LEADING));
- }
- }
- return host;
- }
- bool ShouldShowPopularSites() {
- return base::FeatureList::IsEnabled(kUsePopularSitesSuggestions);
- }
- // Generate a short title for Most Visited items before they're converted to
- // custom links.
- std::u16string GenerateShortTitle(const std::u16string& title) {
- // Empty title only happened in the unittests.
- if (title.empty())
- return std::u16string();
- std::vector<std::u16string> short_title_list = SplitString(
- title, u"-:|;", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- // Make sure it doesn't crash when the title only contains spaces.
- if (short_title_list.empty())
- return std::u16string();
- std::u16string short_title_front = short_title_list.front();
- std::u16string short_title_back = short_title_list.back();
- std::u16string short_title = short_title_front;
- if (short_title_front != short_title_back) {
- int words_in_front =
- SplitString(short_title_front, base::kWhitespaceASCIIAs16,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)
- .size();
- int words_in_back =
- SplitString(short_title_back, base::kWhitespaceASCIIAs16,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)
- .size();
- if (words_in_front >= 3 && words_in_back >= 1 && words_in_back <= 3) {
- short_title = short_title_back;
- }
- }
- return short_title;
- }
- } // namespace
- MostVisitedSites::MostVisitedSites(
- PrefService* prefs,
- scoped_refptr<history::TopSites> top_sites,
- std::unique_ptr<PopularSites> popular_sites,
- std::unique_ptr<CustomLinksManager> custom_links,
- std::unique_ptr<IconCacher> icon_cacher,
- std::unique_ptr<MostVisitedSitesSupervisor> supervisor,
- bool is_default_chrome_app_migrated)
- : prefs_(prefs),
- top_sites_(top_sites),
- popular_sites_(std::move(popular_sites)),
- custom_links_(std::move(custom_links)),
- icon_cacher_(std::move(icon_cacher)),
- supervisor_(std::move(supervisor)),
- is_default_chrome_app_migrated_(is_default_chrome_app_migrated),
- max_num_sites_(0u),
- mv_source_(TileSource::TOP_SITES),
- is_observing_(false) {
- DCHECK(prefs_);
- // top_sites_ can be null in tests.
- // TODO(sfiera): have iOS use a dummy TopSites in its tests.
- if (supervisor_)
- supervisor_->SetObserver(this);
- }
- MostVisitedSites::~MostVisitedSites() {
- if (supervisor_)
- supervisor_->SetObserver(nullptr);
- observers_.Clear();
- }
- // static
- bool MostVisitedSites::IsHostOrMobilePageKnown(
- const std::set<std::string>& hosts_to_skip,
- const std::string& host) {
- std::string no_prefix_host = StripFirstGenericPrefix(host);
- for (const char* prefix : kKnownGenericPagePrefixes) {
- if (hosts_to_skip.count(prefix + no_prefix_host) ||
- hosts_to_skip.count(prefix + host)) {
- return true;
- }
- }
- return false;
- }
- bool MostVisitedSites::DoesSourceExist(TileSource source) const {
- switch (source) {
- case TileSource::TOP_SITES:
- return top_sites_ != nullptr;
- case TileSource::POPULAR_BAKED_IN:
- case TileSource::POPULAR:
- return popular_sites_ != nullptr;
- case TileSource::HOMEPAGE:
- return homepage_client_ != nullptr;
- case TileSource::ALLOWLIST:
- return supervisor_ != nullptr;
- case TileSource::CUSTOM_LINKS:
- return custom_links_ != nullptr;
- case TileSource::EXPLORE:
- return explore_sites_client_ != nullptr;
- }
- NOTREACHED();
- return false;
- }
- void MostVisitedSites::SetHomepageClient(
- std::unique_ptr<HomepageClient> client) {
- DCHECK(client);
- homepage_client_ = std::move(client);
- }
- void MostVisitedSites::SetExploreSitesClient(
- std::unique_ptr<ExploreSitesClient> client) {
- explore_sites_client_ = std::move(client);
- }
- void MostVisitedSites::AddMostVisitedURLsObserver(Observer* observer,
- size_t max_num_sites) {
- observers_.AddObserver(observer);
- // All observer must provide the same |max_num_sites| value.
- DCHECK(max_num_sites_ == 0u || max_num_sites_ == max_num_sites);
- max_num_sites_ = max_num_sites;
- // Starts observing the following sources when the first observer is added.
- if (!is_observing_) {
- is_observing_ = true;
- // The order for this condition is important, ShouldShowPopularSites()
- // should always be called last to keep metrics as relevant as possible.
- if (popular_sites_ && NeedPopularSites(prefs_, GetMaxNumSites()) &&
- ShouldShowPopularSites()) {
- popular_sites_->MaybeStartFetch(
- false, base::BindOnce(&MostVisitedSites::OnPopularSitesDownloaded,
- base::Unretained(this)));
- }
- if (top_sites_) {
- // Register as TopSitesObserver so that we can update ourselves when the
- // TopSites changes.
- top_sites_observation_.Observe(top_sites_.get());
- }
- if (custom_links_) {
- custom_links_subscription_ =
- custom_links_->RegisterCallbackForOnChanged(base::BindRepeating(
- &MostVisitedSites::OnCustomLinksChanged, base::Unretained(this)));
- }
- }
- // Immediately build the current set of tiles, getting suggestions from
- // TopSites.
- BuildCurrentTiles();
- // Also start a request for fresh suggestions.
- Refresh();
- }
- void MostVisitedSites::RemoveMostVisitedURLsObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
- }
- void MostVisitedSites::Refresh() {
- if (top_sites_) {
- // TopSites updates itself after a delay. To ensure up-to-date results,
- // force an update now.
- top_sites_->SyncWithHistory();
- }
- }
- void MostVisitedSites::RefreshTiles() {
- BuildCurrentTiles();
- }
- void MostVisitedSites::InitializeCustomLinks() {
- if (!custom_links_ || !current_tiles_.has_value() || !IsCustomLinksEnabled())
- return;
- if (custom_links_->Initialize(current_tiles_.value()))
- custom_links_action_count_ = 0;
- }
- void MostVisitedSites::UninitializeCustomLinks() {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return;
- custom_links_action_count_ = -1;
- custom_links_->Uninitialize();
- BuildCurrentTiles();
- }
- bool MostVisitedSites::IsCustomLinksInitialized() {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return false;
- return custom_links_->IsInitialized();
- }
- void MostVisitedSites::EnableCustomLinks(bool enable) {
- if (is_custom_links_enabled_ != enable) {
- is_custom_links_enabled_ = enable;
- BuildCurrentTiles();
- }
- }
- bool MostVisitedSites::IsCustomLinksEnabled() const {
- return is_custom_links_enabled_;
- }
- void MostVisitedSites::SetShortcutsVisible(bool visible) {
- if (is_shortcuts_visible_ != visible) {
- is_shortcuts_visible_ = visible;
- BuildCurrentTiles();
- }
- }
- bool MostVisitedSites::IsShortcutsVisible() const {
- return is_shortcuts_visible_;
- }
- bool MostVisitedSites::AddCustomLink(const GURL& url,
- const std::u16string& title) {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return false;
- bool is_first_action = !custom_links_->IsInitialized();
- // Initialize custom links if they have not been initialized yet.
- InitializeCustomLinks();
- bool success = custom_links_->AddLink(url, title);
- if (success) {
- if (custom_links_action_count_ != -1)
- custom_links_action_count_++;
- BuildCurrentTiles();
- } else if (is_first_action) {
- // We don't want to keep custom links initialized if the first action after
- // initialization failed.
- UninitializeCustomLinks();
- }
- return success;
- }
- bool MostVisitedSites::UpdateCustomLink(const GURL& url,
- const GURL& new_url,
- const std::u16string& new_title) {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return false;
- bool is_first_action = !custom_links_->IsInitialized();
- // Initialize custom links if they have not been initialized yet.
- InitializeCustomLinks();
- bool success = custom_links_->UpdateLink(url, new_url, new_title);
- if (success) {
- if (custom_links_action_count_ != -1)
- custom_links_action_count_++;
- BuildCurrentTiles();
- } else if (is_first_action) {
- // We don't want to keep custom links initialized if the first action after
- // initialization failed.
- UninitializeCustomLinks();
- }
- return success;
- }
- bool MostVisitedSites::ReorderCustomLink(const GURL& url, size_t new_pos) {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return false;
- bool is_first_action = !custom_links_->IsInitialized();
- // Initialize custom links if they have not been initialized yet.
- InitializeCustomLinks();
- bool success = custom_links_->ReorderLink(url, new_pos);
- if (success) {
- if (custom_links_action_count_ != -1)
- custom_links_action_count_++;
- BuildCurrentTiles();
- } else if (is_first_action) {
- // We don't want to keep custom links initialized if the first action after
- // initialization failed.
- UninitializeCustomLinks();
- }
- return success;
- }
- bool MostVisitedSites::DeleteCustomLink(const GURL& url) {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return false;
- bool is_first_action = !custom_links_->IsInitialized();
- // Initialize custom links if they have not been initialized yet.
- InitializeCustomLinks();
- bool success = custom_links_->DeleteLink(url);
- if (success) {
- if (custom_links_action_count_ != -1)
- custom_links_action_count_++;
- BuildCurrentTiles();
- } else if (is_first_action) {
- // We don't want to keep custom links initialized if the first action after
- // initialization failed.
- UninitializeCustomLinks();
- }
- return success;
- }
- void MostVisitedSites::UndoCustomLinkAction() {
- if (!custom_links_ || !IsCustomLinksEnabled())
- return;
- // If this is undoing the first action after initialization, uninitialize
- // custom links.
- if (custom_links_action_count_-- == 1)
- UninitializeCustomLinks();
- else if (custom_links_->UndoAction())
- BuildCurrentTiles();
- }
- size_t MostVisitedSites::GetCustomLinkNum() {
- return custom_links_->GetLinks().size();
- }
- void MostVisitedSites::AddOrRemoveBlockedUrl(const GURL& url, bool add_url) {
- if (add_url) {
- base::RecordAction(base::UserMetricsAction("Suggestions.Site.Removed"));
- } else {
- base::RecordAction(
- base::UserMetricsAction("Suggestions.Site.RemovalUndone"));
- }
- if (top_sites_) {
- if (add_url)
- top_sites_->AddBlockedUrl(url);
- else
- top_sites_->RemoveBlockedUrl(url);
- }
- }
- void MostVisitedSites::ClearBlockedUrls() {
- if (top_sites_)
- top_sites_->ClearBlockedUrls();
- }
- void MostVisitedSites::OnBlockedSitesChanged() {
- BuildCurrentTiles();
- }
- // static
- void MostVisitedSites::RegisterProfilePrefs(
- user_prefs::PrefRegistrySyncable* registry) {
- registry->RegisterIntegerPref(prefs::kNumPersonalTiles, 0);
- }
- // static
- void MostVisitedSites::ResetProfilePrefs(PrefService* prefs) {
- prefs->SetInteger(prefs::kNumPersonalTiles, 0);
- }
- size_t MostVisitedSites::GetMaxNumSites() const {
- return max_num_sites_ + (custom_links_ && IsCustomLinksEnabled() ? 1 : 0);
- }
- void MostVisitedSites::InitiateTopSitesQuery() {
- if (!top_sites_)
- return;
- if (top_sites_weak_ptr_factory_.HasWeakPtrs())
- return; // Ongoing query.
- top_sites_->GetMostVisitedURLs(
- base::BindOnce(&MostVisitedSites::OnMostVisitedURLsAvailable,
- top_sites_weak_ptr_factory_.GetWeakPtr()));
- }
- void MostVisitedSites::OnMostVisitedURLsAvailable(
- const history::MostVisitedURLList& visited_list) {
- // Ignore the event if tiles are provided by custom links, which take
- // precedence.
- if (IsCustomLinksInitialized()) {
- return;
- }
- NTPTilesVector tiles;
- size_t num_tiles = std::min(visited_list.size(), GetMaxNumSites());
- for (size_t i = 0; i < num_tiles; ++i) {
- const history::MostVisitedURL& visited = visited_list[i];
- if (visited.url.is_empty())
- break; // This is the signal that there are no more real visited sites.
- if (supervisor_ && supervisor_->IsBlocked(visited.url))
- continue;
- NTPTile tile;
- tile.title =
- custom_links_ ? GenerateShortTitle(visited.title) : visited.title;
- tile.url = visited.url;
- tile.source = TileSource::TOP_SITES;
- // MostVisitedURL.title is either the title or the URL which is treated
- // exactly as the title. Differentiating here is not worth the overhead.
- tile.title_source = TileTitleSource::TITLE_TAG;
- // TODO(crbug.com/773278): Populate |data_generation_time| here in order to
- // log UMA metrics of age.
- tiles.push_back(std::move(tile));
- }
- mv_source_ = TileSource::TOP_SITES;
- InitiateNotificationForNewTiles(std::move(tiles));
- }
- void MostVisitedSites::BuildCurrentTiles() {
- if (IsCustomLinksInitialized()) {
- BuildCustomLinks(custom_links_->GetLinks());
- return;
- }
- mv_source_ = TileSource::TOP_SITES;
- InitiateTopSitesQuery();
- }
- std::map<SectionType, NTPTilesVector>
- MostVisitedSites::CreatePopularSitesSections(
- const std::set<std::string>& used_hosts,
- size_t num_actual_tiles) {
- std::map<SectionType, NTPTilesVector> sections = {
- std::make_pair(SectionType::PERSONALIZED, NTPTilesVector())};
- // For child accounts popular sites tiles will not be added.
- if (supervisor_ && supervisor_->IsChildProfile()) {
- return sections;
- }
- if (!popular_sites_ || !ShouldShowPopularSites()) {
- return sections;
- }
- const std::set<std::string> no_hosts;
- for (const auto& section_type_and_sites : popular_sites()->sections()) {
- SectionType type = section_type_and_sites.first;
- const PopularSites::SitesVector& sites = section_type_and_sites.second;
- if (type == SectionType::PERSONALIZED) {
- size_t num_required_tiles = GetMaxNumSites() - num_actual_tiles;
- sections[type] =
- CreatePopularSitesTiles(/*popular_sites=*/sites,
- /*hosts_to_skip=*/used_hosts,
- /*num_max_tiles=*/num_required_tiles);
- } else {
- sections[type] =
- CreatePopularSitesTiles(/*popular_sites=*/sites,
- /*hosts_to_skip=*/no_hosts,
- /*num_max_tiles=*/GetMaxNumSites());
- }
- }
- return sections;
- }
- NTPTilesVector MostVisitedSites::CreatePopularSitesTiles(
- const PopularSites::SitesVector& sites_vector,
- const std::set<std::string>& hosts_to_skip,
- size_t num_max_tiles) {
- // Collect non-blocked popular suggestions, skipping those already present
- // in the personal suggestions.
- NTPTilesVector popular_sites_tiles;
- for (const PopularSites::Site& popular_site : sites_vector) {
- if (popular_sites_tiles.size() >= num_max_tiles) {
- break;
- }
- // Skip blocked sites.
- if (top_sites_ && top_sites_->IsBlocked(popular_site.url))
- continue;
- const std::string& host = popular_site.url.host();
- if (IsHostOrMobilePageKnown(hosts_to_skip, host)) {
- continue;
- }
- NTPTile tile;
- tile.title = popular_site.title;
- tile.url = GURL(popular_site.url);
- tile.title_source = popular_site.title_source;
- tile.source = popular_site.baked_in ? TileSource::POPULAR_BAKED_IN
- : TileSource::POPULAR;
- popular_sites_tiles.push_back(std::move(tile));
- icon_cacher_->StartFetchPopularSites(
- popular_site,
- base::BindOnce(&MostVisitedSites::OnIconMadeAvailable,
- base::Unretained(this), popular_site.url),
- base::BindOnce(&MostVisitedSites::OnIconMadeAvailable,
- base::Unretained(this), popular_site.url));
- }
- return popular_sites_tiles;
- }
- void MostVisitedSites::OnHomepageTitleDetermined(
- NTPTilesVector tiles,
- const absl::optional<std::u16string>& title) {
- if (!title.has_value())
- return; // If there is no title, the most recent tile was already sent out.
- MergeMostVisitedTiles(InsertHomeTile(std::move(tiles), title.value()));
- }
- NTPTilesVector MostVisitedSites::InsertHomeTile(
- NTPTilesVector tiles,
- const std::u16string& title) const {
- DCHECK(homepage_client_);
- DCHECK_GT(GetMaxNumSites(), 0u);
- const GURL& homepage_url = homepage_client_->GetHomepageUrl();
- NTPTilesVector new_tiles;
- bool homepage_tile_added = false;
- for (auto& tile : tiles) {
- if (new_tiles.size() >= GetMaxNumSites()) {
- break;
- }
- // If there's a tile has the same host name with homepage, insert the tile
- // to the first position of the list. This is also a deduplication.
- if (tile.url.host() == homepage_url.host() && !homepage_tile_added) {
- tile.source = TileSource::HOMEPAGE;
- homepage_tile_added = true;
- new_tiles.insert(new_tiles.begin(), std::move(tile));
- continue;
- }
- new_tiles.push_back(std::move(tile));
- }
- if (!homepage_tile_added) {
- // Make room for the homepage tile.
- if (new_tiles.size() >= GetMaxNumSites()) {
- new_tiles.pop_back();
- }
- NTPTile homepage_tile;
- homepage_tile.url = homepage_url;
- homepage_tile.title = title;
- homepage_tile.source = TileSource::HOMEPAGE;
- homepage_tile.title_source = TileTitleSource::TITLE_TAG;
- // Always insert |homepage_tile| to the front of |new_tiles| to ensure it's
- // the first tile.
- new_tiles.insert(new_tiles.begin(), std::move(homepage_tile));
- }
- return new_tiles;
- }
- absl::optional<NTPTile> MostVisitedSites::CreateExploreSitesTile() {
- if (!explore_sites_client_)
- return absl::nullopt;
- NTPTile explore_sites_tile;
- explore_sites_tile.url = explore_sites_client_->GetExploreSitesUrl();
- explore_sites_tile.title = explore_sites_client_->GetExploreSitesTitle();
- explore_sites_tile.source = TileSource::EXPLORE;
- explore_sites_tile.title_source = TileTitleSource::UNKNOWN;
- return explore_sites_tile;
- }
- void MostVisitedSites::OnCustomLinksChanged() {
- DCHECK(custom_links_);
- if (!IsCustomLinksEnabled())
- return;
- if (custom_links_->IsInitialized()) {
- BuildCustomLinks(custom_links_->GetLinks());
- } else {
- // Since custom links have been uninitialized (e.g. through Chrome sync), we
- // should show the regular Most Visited tiles.
- BuildCurrentTiles();
- }
- }
- void MostVisitedSites::BuildCustomLinks(
- const std::vector<CustomLinksManager::Link>& links) {
- DCHECK(custom_links_);
- NTPTilesVector tiles;
- // The maximum number of custom links that can be shown is independent of the
- // maximum number of Most Visited sites that can be shown.
- size_t num_tiles = std::min(links.size(), kMaxNumCustomLinks);
- for (size_t i = 0; i < num_tiles; ++i) {
- const CustomLinksManager::Link& link = links.at(i);
- if (supervisor_ && supervisor_->IsBlocked(link.url))
- continue;
- NTPTile tile;
- tile.title = link.title;
- tile.url = link.url;
- tile.source = TileSource::CUSTOM_LINKS;
- tile.from_most_visited = link.is_most_visited;
- tiles.push_back(std::move(tile));
- }
- mv_source_ = TileSource::CUSTOM_LINKS;
- SaveTilesAndNotify(std::move(tiles), std::map<SectionType, NTPTilesVector>());
- }
- void MostVisitedSites::InitiateNotificationForNewTiles(
- NTPTilesVector new_tiles) {
- if (ShouldAddHomeTile() && !HasHomeTile(new_tiles)) {
- homepage_client_->QueryHomepageTitle(
- base::BindOnce(&MostVisitedSites::OnHomepageTitleDetermined,
- base::Unretained(this), new_tiles));
- GURL homepage_url = homepage_client_->GetHomepageUrl();
- icon_cacher_->StartFetchMostLikely(
- homepage_url,
- base::BindRepeating(&MostVisitedSites::OnIconMadeAvailable,
- base::Unretained(this), homepage_url));
- // Don't wait for the homepage title from history but immediately serve a
- // copy of new tiles.
- new_tiles = InsertHomeTile(std::move(new_tiles), std::u16string());
- }
- MergeMostVisitedTiles(std::move(new_tiles));
- }
- void MostVisitedSites::MergeMostVisitedTiles(NTPTilesVector personal_tiles) {
- std::set<std::string> used_hosts;
- absl::optional<NTPTile> explore_tile = CreateExploreSitesTile();
- size_t num_actual_tiles = explore_tile ? 1 : 0;
- // The explore sites tile may have taken a space that was utilized by the
- // personal tiles.
- if (!personal_tiles.empty() &&
- personal_tiles.size() + num_actual_tiles > GetMaxNumSites()) {
- personal_tiles.pop_back();
- }
- AddToHostsAndTotalCount(personal_tiles, &used_hosts, &num_actual_tiles);
- std::map<SectionType, NTPTilesVector> sections =
- CreatePopularSitesSections(used_hosts, num_actual_tiles);
- AddToHostsAndTotalCount(sections[SectionType::PERSONALIZED], &used_hosts,
- &num_actual_tiles);
- NTPTilesVector new_tiles =
- MergeTiles(std::move(personal_tiles),
- std::move(sections[SectionType::PERSONALIZED]), explore_tile);
- SaveTilesAndNotify(std::move(new_tiles), std::move(sections));
- }
- void MostVisitedSites::SaveTilesAndNotify(
- NTPTilesVector new_tiles,
- std::map<SectionType, NTPTilesVector> sections) {
- // TODO(https://crbug.com/1266574):
- // Remove this after preinstalled apps are migrated.
- NTPTilesVector fixed_tiles = is_default_chrome_app_migrated_
- ? RemoveInvalidPreinstallApps(new_tiles)
- : new_tiles;
- if (fixed_tiles.size() != new_tiles.size()) {
- metrics::RecordsMigratedDefaultAppDeleted(
- DeletedTileType::kMostVisitedSite);
- }
- if (!current_tiles_.has_value() || (*current_tiles_ != fixed_tiles)) {
- current_tiles_.emplace(std::move(fixed_tiles));
- int num_personal_tiles = 0;
- for (const auto& tile : *current_tiles_) {
- if (tile.source != TileSource::POPULAR &&
- tile.source != TileSource::POPULAR_BAKED_IN) {
- num_personal_tiles++;
- }
- }
- prefs_->SetInteger(prefs::kNumPersonalTiles, num_personal_tiles);
- }
- if (observers_.empty())
- return;
- sections[SectionType::PERSONALIZED] = *current_tiles_;
- for (auto& observer : observers_)
- observer.OnURLsAvailable(sections);
- }
- // static
- bool MostVisitedSites::IsNtpTileFromPreinstalledApp(GURL url) {
- #if BUILDFLAG(ENABLE_EXTENSIONS)
- return url.is_valid() && url.SchemeIs(extensions::kExtensionScheme) &&
- extension_misc::IsPreinstalledAppId(url.host());
- #else
- return false;
- #endif
- }
- // static
- bool MostVisitedSites::WasNtpAppMigratedToWebApp(PrefService* prefs, GURL url) {
- const base::Value::List& migrated_apps =
- prefs->GetValueList(webapps::kWebAppsMigratedPreinstalledApps);
- for (const auto& val : migrated_apps) {
- if (val.is_string() && val.GetString() == url.host())
- return true;
- }
- return false;
- }
- NTPTilesVector MostVisitedSites::RemoveInvalidPreinstallApps(
- NTPTilesVector new_tiles) {
- new_tiles.erase(
- std::remove_if(new_tiles.begin(), new_tiles.end(),
- [this](NTPTile ntp_tile) {
- return MostVisitedSites::IsNtpTileFromPreinstalledApp(
- ntp_tile.url) &&
- MostVisitedSites::WasNtpAppMigratedToWebApp(
- prefs_, ntp_tile.url);
- }),
- new_tiles.end());
- return new_tiles;
- }
- NTPTilesVector MostVisitedSites::MergeTiles(
- NTPTilesVector personal_tiles,
- NTPTilesVector popular_tiles,
- absl::optional<NTPTile> explore_tile) {
- NTPTilesVector merged_tiles;
- std::move(personal_tiles.begin(), personal_tiles.end(),
- std::back_inserter(merged_tiles));
- std::move(popular_tiles.begin(), popular_tiles.end(),
- std::back_inserter(merged_tiles));
- if (explore_tile)
- merged_tiles.push_back(*explore_tile);
- return merged_tiles;
- }
- void MostVisitedSites::OnPopularSitesDownloaded(bool success) {
- if (!success) {
- LOG(WARNING) << "Download of popular sites failed";
- return;
- }
- for (const auto& section : popular_sites_->sections()) {
- for (const PopularSites::Site& site : section.second) {
- // Ignore callback; these icons will be seen on the *next* NTP.
- icon_cacher_->StartFetchPopularSites(site, base::NullCallback(),
- base::NullCallback());
- }
- }
- }
- void MostVisitedSites::OnIconMadeAvailable(const GURL& site_url) {
- for (auto& observer : observers_)
- observer.OnIconMadeAvailable(site_url);
- }
- void MostVisitedSites::TopSitesLoaded(TopSites* top_sites) {}
- void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
- ChangeReason change_reason) {
- if (mv_source_ == TileSource::TOP_SITES) {
- // The displayed tiles are invalidated.
- InitiateTopSitesQuery();
- }
- }
- bool MostVisitedSites::ShouldAddHomeTile() const {
- return GetMaxNumSites() > 0u &&
- homepage_client_ && // No platform-specific implementation - no tile.
- homepage_client_->IsHomepageTileEnabled() &&
- !homepage_client_->GetHomepageUrl().is_empty() &&
- !(top_sites_ &&
- top_sites_->IsBlocked(homepage_client_->GetHomepageUrl()));
- }
- void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
- std::set<std::string>* hosts,
- size_t* total_tile_count) const {
- for (const auto& tile : new_tiles) {
- hosts->insert(tile.url.host());
- }
- *total_tile_count += new_tiles.size();
- DCHECK_LE(*total_tile_count, GetMaxNumSites());
- }
- } // namespace ntp_tiles
|