[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
sfiera | 3ff01c0d | 2016-06-13 15:33:38 | [diff] [blame] | 5 | #include "components/ntp_tiles/most_visited_sites.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 6 | |
sfiera | 3ff01c0d | 2016-06-13 15:33:38 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | #include <set> |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 9 | #include <utility> |
| 10 | |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 11 | #include "base/callback.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 12 | #include "base/command_line.h" |
mastiz | f3468a8 | 2016-06-15 11:05:41 | [diff] [blame^] | 13 | #include "base/feature_list.h" |
zea | 9c7707d | 2015-11-03 21:41:14 | [diff] [blame] | 14 | #include "base/metrics/field_trial.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 15 | #include "base/metrics/histogram.h" |
[email protected] | 1d778e1 | 2014-06-17 02:28:51 | [diff] [blame] | 16 | #include "base/metrics/sparse_histogram.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 17 | #include "base/strings/string_number_conversions.h" |
treib | eba6cee | 2015-09-09 13:25:59 | [diff] [blame] | 18 | #include "base/strings/string_util.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 19 | #include "base/strings/stringprintf.h" |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 20 | #include "base/strings/utf_string_conversions.h" |
[email protected] | 6d21c70 | 2014-05-15 06:10:21 | [diff] [blame] | 21 | #include "base/time/time.h" |
sdefresne | 0da3bc0 | 2015-01-29 18:26:35 | [diff] [blame] | 22 | #include "components/history/core/browser/top_sites.h" |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 23 | #include "components/ntp_tiles/pref_names.h" |
| 24 | #include "components/ntp_tiles/switches.h" |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 25 | #include "components/pref_registry/pref_registry_syncable.h" |
brettw | b1fc1b8 | 2016-02-02 00:19:08 | [diff] [blame] | 26 | #include "components/prefs/pref_service.h" |
zea | 9c7707d | 2015-11-03 21:41:14 | [diff] [blame] | 27 | #include "components/variations/variations_associated_data.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 28 | #include "third_party/skia/include/core/SkBitmap.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 29 | #include "ui/gfx/codec/jpeg_codec.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 30 | #include "url/gurl.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 31 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 32 | using history::TopSites; |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 33 | using suggestions::ChromeSuggestion; |
| 34 | using suggestions::SuggestionsProfile; |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 35 | using suggestions::SuggestionsService; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 36 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 37 | namespace { |
| 38 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 39 | // Identifiers for the various tile sources. |
| 40 | const char kHistogramClientName[] = "client"; |
| 41 | const char kHistogramServerName[] = "server"; |
| 42 | const char kHistogramServerFormat[] = "server%d"; |
| 43 | const char kHistogramPopularName[] = "popular"; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 44 | const char kHistogramWhitelistName[] = "whitelist"; |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 45 | |
treib | 508939a | 2015-08-25 10:07:56 | [diff] [blame] | 46 | const char kPopularSitesFieldTrialName[] = "NTPPopularSites"; |
| 47 | |
mastiz | f3468a8 | 2016-06-15 11:05:41 | [diff] [blame^] | 48 | const base::Feature kDisplaySuggestionsServiceTiles{ |
| 49 | "DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT}; |
| 50 | |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 51 | // The visual type of a most visited tile. |
| 52 | // |
| 53 | // These values must stay in sync with the MostVisitedTileType enum |
| 54 | // in histograms.xml. |
| 55 | // |
| 56 | // A Java counterpart will be generated for this enum. |
| 57 | // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp |
| 58 | enum MostVisitedTileType { |
| 59 | // The icon or thumbnail hasn't loaded yet. |
| 60 | NONE, |
| 61 | // The item displays a site's actual favicon or touch icon. |
| 62 | ICON_REAL, |
| 63 | // The item displays a color derived from the site's favicon or touch icon. |
| 64 | ICON_COLOR, |
| 65 | // The item displays a default gray box in place of an icon. |
| 66 | ICON_DEFAULT, |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 67 | NUM_TILE_TYPES, |
| 68 | }; |
| 69 | |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 70 | // May only be called from blocking thread pool. |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 71 | std::unique_ptr<SkBitmap> TryFetchLocalThumbnail( |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 72 | const GURL& url, |
| 73 | const scoped_refptr<TopSites>& top_sites) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 74 | scoped_refptr<base::RefCountedMemory> image; |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 75 | std::unique_ptr<SkBitmap> bitmap; |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 76 | if (top_sites->GetPageThumbnail(url, false, &image)) |
dcheng | b587f9a4 | 2016-04-11 19:51:25 | [diff] [blame] | 77 | bitmap = gfx::JPEGCodec::Decode(image->front(), image->size()); |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 78 | return bitmap; |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 79 | } |
| 80 | |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 81 | // Log an event for a given |histogram| at a given element |position|. This |
| 82 | // routine exists because regular histogram macros are cached thus can't be used |
| 83 | // if the name of the histogram will change at a given call site. |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 84 | void LogHistogramEvent(const std::string& histogram, |
| 85 | int position, |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 86 | int num_sites) { |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 87 | base::HistogramBase* counter = base::LinearHistogram::FactoryGet( |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 88 | histogram, |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 89 | 1, |
| 90 | num_sites, |
| 91 | num_sites + 1, |
| 92 | base::Histogram::kUmaTargetedHistogramFlag); |
[email protected] | 6f67448 | 2014-08-11 22:50:49 | [diff] [blame] | 93 | if (counter) |
| 94 | counter->Add(position); |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 95 | } |
| 96 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 97 | bool ShouldShowPopularSites() { |
| 98 | // Note: It's important to query the field trial state first, to ensure that |
| 99 | // UMA reports the correct group. |
| 100 | const std::string group_name = |
treib | 508939a | 2015-08-25 10:07:56 | [diff] [blame] | 101 | base::FieldTrialList::FindFullName(kPopularSitesFieldTrialName); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 102 | base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 103 | if (cmd_line->HasSwitch(ntp_tiles::switches::kDisableNTPPopularSites)) |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 104 | return false; |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 105 | if (cmd_line->HasSwitch(ntp_tiles::switches::kEnableNTPPopularSites)) |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 106 | return true; |
treib | eba6cee | 2015-09-09 13:25:59 | [diff] [blame] | 107 | return base::StartsWith(group_name, "Enabled", |
| 108 | base::CompareCase::INSENSITIVE_ASCII); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 109 | } |
| 110 | |
treib | b28d650 | 2015-09-22 14:28:50 | [diff] [blame] | 111 | std::string GetPopularSitesCountry() { |
| 112 | return variations::GetVariationParamValue(kPopularSitesFieldTrialName, |
| 113 | "country"); |
| 114 | } |
| 115 | |
| 116 | std::string GetPopularSitesVersion() { |
| 117 | return variations::GetVariationParamValue(kPopularSitesFieldTrialName, |
| 118 | "version"); |
| 119 | } |
| 120 | |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 121 | // Determine whether we need any popular suggestions to fill up a grid of |
| 122 | // |num_tiles| tiles. |
| 123 | bool NeedPopularSites(const PrefService* prefs, size_t num_tiles) { |
| 124 | const base::ListValue* source_list = |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 125 | prefs->GetList(ntp_tiles::prefs::kNTPSuggestionsIsPersonal); |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 126 | // If there aren't enough previous suggestions to fill the grid, we need |
| 127 | // popular suggestions. |
| 128 | if (source_list->GetSize() < num_tiles) |
| 129 | return true; |
| 130 | // Otherwise, if any of the previous suggestions is not personal, then also |
| 131 | // get popular suggestions. |
| 132 | for (size_t i = 0; i < num_tiles; ++i) { |
| 133 | bool is_personal = false; |
| 134 | if (source_list->GetBoolean(i, &is_personal) && !is_personal) |
| 135 | return true; |
| 136 | } |
| 137 | // The whole grid is already filled with personal suggestions, no point in |
| 138 | // bothering with popular ones. |
| 139 | return false; |
| 140 | } |
| 141 | |
atanasova | 9c0dda60 | 2016-03-22 17:41:13 | [diff] [blame] | 142 | bool AreURLsEquivalent(const GURL& url1, const GURL& url2) { |
| 143 | return url1.host() == url2.host() && url1.path() == url2.path(); |
| 144 | } |
| 145 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 146 | std::string GetSourceHistogramName( |
| 147 | const MostVisitedSites::Suggestion& suggestion) { |
| 148 | switch (suggestion.source) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 149 | case MostVisitedSites::TOP_SITES: |
| 150 | return kHistogramClientName; |
| 151 | case MostVisitedSites::POPULAR: |
| 152 | return kHistogramPopularName; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 153 | case MostVisitedSites::WHITELIST: |
| 154 | return kHistogramWhitelistName; |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 155 | case MostVisitedSites::SUGGESTIONS_SERVICE: |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 156 | return suggestion.provider_index >= 0 |
| 157 | ? base::StringPrintf(kHistogramServerFormat, |
| 158 | suggestion.provider_index) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 159 | : kHistogramServerName; |
| 160 | } |
| 161 | NOTREACHED(); |
| 162 | return std::string(); |
| 163 | } |
| 164 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 165 | } // namespace |
| 166 | |
| 167 | MostVisitedSites::Suggestion::Suggestion() : provider_index(-1) {} |
| 168 | |
| 169 | MostVisitedSites::Suggestion::~Suggestion() {} |
| 170 | |
| 171 | MostVisitedSites::Suggestion::Suggestion(Suggestion&&) = default; |
| 172 | MostVisitedSites::Suggestion& |
| 173 | MostVisitedSites::Suggestion::operator=(Suggestion&&) = default; |
| 174 | |
sfiera | 9a20152 | 2016-05-02 10:47:58 | [diff] [blame] | 175 | MostVisitedSites::MostVisitedSites( |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 176 | scoped_refptr<base::SequencedWorkerPool> blocking_pool, |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 177 | PrefService* prefs, |
| 178 | const TemplateURLService* template_url_service, |
| 179 | variations::VariationsService* variations_service, |
| 180 | net::URLRequestContextGetter* download_context, |
sfiera | 685a987e | 2016-05-12 17:08:47 | [diff] [blame] | 181 | const base::FilePath& popular_sites_directory, |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 182 | scoped_refptr<history::TopSites> top_sites, |
| 183 | SuggestionsService* suggestions, |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 184 | MostVisitedSitesSupervisor* supervisor) |
| 185 | : prefs_(prefs), |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 186 | template_url_service_(template_url_service), |
| 187 | variations_service_(variations_service), |
sfiera | 685a987e | 2016-05-12 17:08:47 | [diff] [blame] | 188 | download_context_(download_context), |
| 189 | popular_sites_directory_(popular_sites_directory), |
| 190 | top_sites_(top_sites), |
| 191 | suggestions_service_(suggestions), |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 192 | supervisor_(supervisor), |
sfiera | 685a987e | 2016-05-12 17:08:47 | [diff] [blame] | 193 | observer_(nullptr), |
| 194 | num_sites_(0), |
| 195 | received_most_visited_sites_(false), |
| 196 | received_popular_sites_(false), |
| 197 | recorded_uma_(false), |
| 198 | scoped_observer_(this), |
| 199 | mv_source_(SUGGESTIONS_SERVICE), |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 200 | blocking_pool_(std::move(blocking_pool)), |
| 201 | blocking_runner_(blocking_pool_->GetTaskRunnerWithShutdownBehavior( |
| 202 | base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)), |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 203 | weak_ptr_factory_(this) { |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 204 | DCHECK(top_sites_); |
| 205 | DCHECK(suggestions_service_); |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 206 | supervisor_->SetObserver(this); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 207 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 208 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 209 | MostVisitedSites::~MostVisitedSites() { |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 210 | supervisor_->SetObserver(nullptr); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 211 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 212 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 213 | void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer, |
| 214 | int num_sites) { |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 215 | DCHECK(observer); |
sfiera | 929502f3 | 2016-04-21 12:52:48 | [diff] [blame] | 216 | observer_ = observer; |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 217 | num_sites_ = num_sites; |
[email protected] | 852fc79 | 2013-10-04 04:15:12 | [diff] [blame] | 218 | |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 219 | if (ShouldShowPopularSites() && |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 220 | NeedPopularSites(prefs_, num_sites_)) { |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 221 | popular_sites_.reset(new PopularSites( |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 222 | blocking_pool_, prefs_, template_url_service_, variations_service_, |
| 223 | download_context_, popular_sites_directory_, GetPopularSitesCountry(), |
sfiera | 685a987e | 2016-05-12 17:08:47 | [diff] [blame] | 224 | GetPopularSitesVersion(), false, |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 225 | base::Bind(&MostVisitedSites::OnPopularSitesAvailable, |
| 226 | base::Unretained(this)))); |
| 227 | } else { |
| 228 | received_popular_sites_ = true; |
| 229 | } |
| 230 | |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 231 | // TopSites updates itself after a delay. To ensure up-to-date results, |
| 232 | // force an update now. |
| 233 | top_sites_->SyncWithHistory(); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 234 | |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 235 | // Register as TopSitesObserver so that we can update ourselves when the |
| 236 | // TopSites changes. |
| 237 | scoped_observer_.Add(top_sites_.get()); |
treib | cb9c93b | 2016-04-01 13:22:52 | [diff] [blame] | 238 | |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 239 | suggestions_subscription_ = suggestions_service_->AddCallback( |
treib | cb9c93b | 2016-04-01 13:22:52 | [diff] [blame] | 240 | base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable, |
| 241 | base::Unretained(this))); |
| 242 | |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 243 | // Immediately build the current suggestions, getting personal suggestions |
| 244 | // from the SuggestionsService's cache or, if that is empty, from TopSites. |
| 245 | BuildCurrentSuggestions(); |
treib | cb9c93b | 2016-04-01 13:22:52 | [diff] [blame] | 246 | // Also start a request for fresh suggestions. |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 247 | suggestions_service_->FetchSuggestionsData(); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 248 | } |
| 249 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 250 | void MostVisitedSites::GetURLThumbnail(const GURL& url, |
| 251 | const ThumbnailCallback& callback) { |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 252 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 253 | |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 254 | base::PostTaskAndReplyWithResult( |
| 255 | blocking_runner_.get(), FROM_HERE, |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 256 | base::Bind(&TryFetchLocalThumbnail, url, top_sites_), |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 257 | base::Bind(&MostVisitedSites::OnLocalThumbnailFetched, |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 258 | weak_ptr_factory_.GetWeakPtr(), url, callback)); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | void MostVisitedSites::OnLocalThumbnailFetched( |
| 262 | const GURL& url, |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 263 | const ThumbnailCallback& callback, |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 264 | std::unique_ptr<SkBitmap> bitmap) { |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 265 | DCHECK(thread_checker_.CalledOnValidThread()); |
markusheintz | 55ae116 | 2016-05-25 08:20:57 | [diff] [blame] | 266 | if (bitmap.get()) { |
| 267 | callback.Run(true /* is_local_thumbnail */, bitmap.get()); |
| 268 | return; |
| 269 | } |
| 270 | |
| 271 | // A thumbnail is not locally available for |url|. Make sure it is put in |
| 272 | // the list to be fetched at the next visit to this site. |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 273 | top_sites_->AddForcedURL(url, base::Time::Now()); |
markusheintz | 55ae116 | 2016-05-25 08:20:57 | [diff] [blame] | 274 | // Also fetch a remote thumbnail if possible. PopularSites or the |
| 275 | // SuggestionsService can supply a thumbnail download URL. |
| 276 | if (popular_sites_) { |
| 277 | const std::vector<PopularSites::Site>& sites = popular_sites_->sites(); |
| 278 | auto it = std::find_if( |
| 279 | sites.begin(), sites.end(), |
| 280 | [&url](const PopularSites::Site& site) { return site.url == url; }); |
| 281 | if (it != sites.end() && it->thumbnail_url.is_valid()) { |
| 282 | return suggestions_service_->GetPageThumbnailWithURL( |
| 283 | url, it->thumbnail_url, |
| 284 | base::Bind(&MostVisitedSites::OnObtainedThumbnail, |
| 285 | weak_ptr_factory_.GetWeakPtr(), false, callback)); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 286 | } |
| 287 | } |
markusheintz | 55ae116 | 2016-05-25 08:20:57 | [diff] [blame] | 288 | if (mv_source_ == SUGGESTIONS_SERVICE) { |
| 289 | return suggestions_service_->GetPageThumbnail( |
| 290 | url, base::Bind(&MostVisitedSites::OnObtainedThumbnail, |
| 291 | weak_ptr_factory_.GetWeakPtr(), false, callback)); |
| 292 | } |
| 293 | // If no bitmap could be fetched and neither PopularSites nor the |
| 294 | // SuggestionsService is available then a nullptr is passed to the callback. |
| 295 | callback.Run(true /* is_local_thumbnail */, nullptr); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 296 | } |
| 297 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 298 | void MostVisitedSites::OnObtainedThumbnail(bool is_local_thumbnail, |
| 299 | const ThumbnailCallback& callback, |
| 300 | const GURL& url, |
markusheintz | 55ae116 | 2016-05-25 08:20:57 | [diff] [blame] | 301 | const gfx::Image& image) { |
sfiera | 3e0b88b | 2016-05-27 12:28:38 | [diff] [blame] | 302 | DCHECK(thread_checker_.CalledOnValidThread()); |
markusheintz | 55ae116 | 2016-05-25 08:20:57 | [diff] [blame] | 303 | const SkBitmap* bitmap = nullptr; |
| 304 | if (!image.IsEmpty()) |
| 305 | bitmap = image.ToSkBitmap(); |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 306 | callback.Run(is_local_thumbnail, bitmap); |
| 307 | } |
| 308 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 309 | void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url, |
| 310 | bool add_url) { |
treib | fac7519 | 2015-08-17 13:21:24 | [diff] [blame] | 311 | // Always blacklist in the local TopSites. |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 312 | if (add_url) |
| 313 | top_sites_->AddBlacklistedURL(url); |
| 314 | else |
| 315 | top_sites_->RemoveBlacklistedURL(url); |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 316 | |
treib | fac7519 | 2015-08-17 13:21:24 | [diff] [blame] | 317 | // Only blacklist in the server-side suggestions service if it's active. |
| 318 | if (mv_source_ == SUGGESTIONS_SERVICE) { |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 319 | if (add_url) |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 320 | suggestions_service_->BlacklistURL(url); |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 321 | else |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 322 | suggestions_service_->UndoBlacklistURL(url); |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 323 | } |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 324 | } |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 325 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 326 | void MostVisitedSites::RecordTileTypeMetrics( |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 327 | const std::vector<int>& tile_types) { |
sfiera | 929502f3 | 2016-04-21 12:52:48 | [diff] [blame] | 328 | DCHECK_EQ(current_suggestions_.size(), tile_types.size()); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 329 | int counts_per_type[NUM_TILE_TYPES] = {0}; |
| 330 | for (size_t i = 0; i < tile_types.size(); ++i) { |
| 331 | int tile_type = tile_types[i]; |
| 332 | ++counts_per_type[tile_type]; |
| 333 | std::string histogram = base::StringPrintf( |
| 334 | "NewTabPage.TileType.%s", |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 335 | GetSourceHistogramName(current_suggestions_[i]).c_str()); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 336 | LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES); |
| 337 | } |
| 338 | |
Newton Allen | ea5f0c0 | 2015-12-04 03:02:41 | [diff] [blame] | 339 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal", |
| 340 | counts_per_type[ICON_REAL]); |
| 341 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor", |
| 342 | counts_per_type[ICON_COLOR]); |
| 343 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray", |
| 344 | counts_per_type[ICON_DEFAULT]); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 345 | } |
| 346 | |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 347 | void MostVisitedSites::RecordOpenedMostVisitedItem(int index, int tile_type) { |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 348 | DCHECK_GE(index, 0); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 349 | DCHECK_LT(index, static_cast<int>(current_suggestions_.size())); |
| 350 | std::string histogram = base::StringPrintf( |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 351 | "NewTabPage.MostVisited.%s", |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 352 | GetSourceHistogramName(current_suggestions_[index]).c_str()); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 353 | LogHistogramEvent(histogram, index, num_sites_); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 354 | |
| 355 | histogram = base::StringPrintf( |
| 356 | "NewTabPage.TileTypeClicked.%s", |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 357 | GetSourceHistogramName(current_suggestions_[index]).c_str()); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 358 | LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES); |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 359 | } |
| 360 | |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 361 | void MostVisitedSites::OnBlockedSitesChanged() { |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 362 | BuildCurrentSuggestions(); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 363 | } |
| 364 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 365 | // static |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 366 | void MostVisitedSites::RegisterProfilePrefs( |
| 367 | user_prefs::PrefRegistrySyncable* registry) { |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 368 | // TODO(treib): Remove this, it's unused. Do we need migration code to clean |
| 369 | // up existing entries? |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 370 | registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsURL); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 371 | // TODO(treib): Remove this. It's only used to determine if we need |
| 372 | // PopularSites at all. Find a way to do that without prefs, or failing that, |
| 373 | // replace this list pref by a simple bool. |
sfiera | 0dc4da5 | 2016-04-29 09:23:05 | [diff] [blame] | 374 | registry->RegisterListPref(ntp_tiles::prefs::kNTPSuggestionsIsPersonal); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 375 | } |
| 376 | |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 377 | void MostVisitedSites::BuildCurrentSuggestions() { |
| 378 | // Get the current suggestions from cache. If the cache is empty, this will |
| 379 | // fall back to TopSites. |
treib | ba0dee7 | 2016-03-31 13:25:59 | [diff] [blame] | 380 | OnSuggestionsProfileAvailable( |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 381 | suggestions_service_->GetSuggestionsDataFromCache()); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 382 | } |
| 383 | |
| 384 | void MostVisitedSites::InitiateTopSitesQuery() { |
sfiera | 728962f | 2016-04-29 11:04:02 | [diff] [blame] | 385 | top_sites_->GetMostVisitedURLs( |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 386 | base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable, |
| 387 | weak_ptr_factory_.GetWeakPtr()), |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 388 | false); |
| 389 | } |
| 390 | |
atanasova | 9c0dda60 | 2016-03-22 17:41:13 | [diff] [blame] | 391 | base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) { |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 392 | for (const auto& whitelist : supervisor_->whitelists()) { |
| 393 | if (AreURLsEquivalent(whitelist.entry_point, url)) |
| 394 | return whitelist.large_icon_path; |
atanasova | 9c0dda60 | 2016-03-22 17:41:13 | [diff] [blame] | 395 | } |
| 396 | return base::FilePath(); |
| 397 | } |
| 398 | |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 399 | void MostVisitedSites::OnMostVisitedURLsAvailable( |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 400 | const history::MostVisitedURLList& visited_list) { |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 401 | SuggestionsPtrVector suggestions; |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 402 | size_t num_tiles = |
| 403 | std::min(visited_list.size(), static_cast<size_t>(num_sites_)); |
| 404 | for (size_t i = 0; i < num_tiles; ++i) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 405 | const history::MostVisitedURL& visited = visited_list[i]; |
| 406 | if (visited.url.is_empty()) { |
| 407 | num_tiles = i; |
| 408 | break; // This is the signal that there are no more real visited sites. |
| 409 | } |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 410 | if (supervisor_->IsBlocked(visited.url)) |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 411 | continue; |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 412 | |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 413 | std::unique_ptr<Suggestion> suggestion(new Suggestion()); |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 414 | suggestion->title = visited.title; |
| 415 | suggestion->url = visited.url; |
| 416 | suggestion->source = TOP_SITES; |
atanasova | 9c0dda60 | 2016-03-22 17:41:13 | [diff] [blame] | 417 | suggestion->whitelist_icon_path = GetWhitelistLargeIconPath(visited.url); |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 418 | |
| 419 | suggestions.push_back(std::move(suggestion)); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 420 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 421 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 422 | received_most_visited_sites_ = true; |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 423 | mv_source_ = TOP_SITES; |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 424 | SaveNewSuggestions(&suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 425 | NotifyMostVisitedURLsObserver(); |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 428 | void MostVisitedSites::OnSuggestionsProfileAvailable( |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 429 | const SuggestionsProfile& suggestions_profile) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 430 | int num_tiles = suggestions_profile.suggestions_size(); |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 431 | // With no server suggestions, fall back to local TopSites. |
mastiz | f3468a8 | 2016-06-15 11:05:41 | [diff] [blame^] | 432 | if (num_tiles == 0 || |
| 433 | !base::FeatureList::IsEnabled(kDisplaySuggestionsServiceTiles)) { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 434 | InitiateTopSitesQuery(); |
| 435 | return; |
| 436 | } |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 437 | if (num_sites_ < num_tiles) |
| 438 | num_tiles = num_sites_; |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 439 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 440 | SuggestionsPtrVector suggestions; |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 441 | for (int i = 0; i < num_tiles; ++i) { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 442 | const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i); |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 443 | if (supervisor_->IsBlocked(GURL(suggestion.url()))) |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 444 | continue; |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 445 | |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 446 | std::unique_ptr<Suggestion> generated_suggestion(new Suggestion()); |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 447 | generated_suggestion->title = base::UTF8ToUTF16(suggestion.title()); |
| 448 | generated_suggestion->url = GURL(suggestion.url()); |
| 449 | generated_suggestion->source = SUGGESTIONS_SERVICE; |
atanasova | 9c0dda60 | 2016-03-22 17:41:13 | [diff] [blame] | 450 | generated_suggestion->whitelist_icon_path = GetWhitelistLargeIconPath( |
| 451 | GURL(suggestion.url())); |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 452 | if (suggestion.providers_size() > 0) |
| 453 | generated_suggestion->provider_index = suggestion.providers(0); |
| 454 | |
| 455 | suggestions.push_back(std::move(generated_suggestion)); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 456 | } |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 457 | |
| 458 | received_most_visited_sites_ = true; |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 459 | mv_source_ = SUGGESTIONS_SERVICE; |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 460 | SaveNewSuggestions(&suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 461 | NotifyMostVisitedURLsObserver(); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 462 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 463 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 464 | MostVisitedSites::SuggestionsPtrVector |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 465 | MostVisitedSites::CreateWhitelistEntryPointSuggestions( |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 466 | const SuggestionsPtrVector& personal_suggestions) { |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 467 | size_t num_personal_suggestions = personal_suggestions.size(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 468 | DCHECK_LE(num_personal_suggestions, static_cast<size_t>(num_sites_)); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 469 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 470 | size_t num_whitelist_suggestions = num_sites_ - num_personal_suggestions; |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 471 | SuggestionsPtrVector whitelist_suggestions; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 472 | |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 473 | std::set<std::string> personal_hosts; |
| 474 | for (const auto& suggestion : personal_suggestions) |
| 475 | personal_hosts.insert(suggestion->url.host()); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 476 | |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 477 | for (const auto& whitelist : supervisor_->whitelists()) { |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 478 | // Skip blacklisted sites. |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 479 | if (top_sites_->IsBlacklisted(whitelist.entry_point)) |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 480 | continue; |
| 481 | |
| 482 | // Skip suggestions already present. |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 483 | if (personal_hosts.find(whitelist.entry_point.host()) != |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 484 | personal_hosts.end()) |
| 485 | continue; |
| 486 | |
| 487 | // Skip whitelist entry points that are manually blocked. |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 488 | if (supervisor_->IsBlocked(whitelist.entry_point)) |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 489 | continue; |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 490 | |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 491 | std::unique_ptr<Suggestion> suggestion(new Suggestion()); |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 492 | suggestion->title = whitelist.title; |
| 493 | suggestion->url = whitelist.entry_point; |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 494 | suggestion->source = WHITELIST; |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 495 | suggestion->whitelist_icon_path = whitelist.large_icon_path; |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 496 | |
| 497 | whitelist_suggestions.push_back(std::move(suggestion)); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 498 | if (whitelist_suggestions.size() >= num_whitelist_suggestions) |
| 499 | break; |
| 500 | } |
| 501 | |
| 502 | return whitelist_suggestions; |
| 503 | } |
| 504 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 505 | MostVisitedSites::SuggestionsPtrVector |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 506 | MostVisitedSites::CreatePopularSitesSuggestions( |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 507 | const SuggestionsPtrVector& personal_suggestions, |
| 508 | const SuggestionsPtrVector& whitelist_suggestions) { |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 509 | // For child accounts popular sites suggestions will not be added. |
sfiera | 79e643b | 2016-05-13 16:52:49 | [diff] [blame] | 510 | if (supervisor_->IsChildProfile()) |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 511 | return SuggestionsPtrVector(); |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 512 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 513 | size_t num_suggestions = |
| 514 | personal_suggestions.size() + whitelist_suggestions.size(); |
| 515 | DCHECK_LE(num_suggestions, static_cast<size_t>(num_sites_)); |
| 516 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 517 | // Collect non-blacklisted popular suggestions, skipping those already present |
| 518 | // in the personal suggestions. |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 519 | size_t num_popular_sites_suggestions = num_sites_ - num_suggestions; |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 520 | SuggestionsPtrVector popular_sites_suggestions; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 521 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 522 | if (num_popular_sites_suggestions > 0 && popular_sites_) { |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 523 | std::set<std::string> hosts; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 524 | for (const auto& suggestion : personal_suggestions) |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 525 | hosts.insert(suggestion->url.host()); |
| 526 | for (const auto& suggestion : whitelist_suggestions) |
| 527 | hosts.insert(suggestion->url.host()); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 528 | for (const PopularSites::Site& popular_site : popular_sites_->sites()) { |
| 529 | // Skip blacklisted sites. |
mastiz | c60d74e | 2016-06-14 14:02:49 | [diff] [blame] | 530 | if (top_sites_->IsBlacklisted(popular_site.url)) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 531 | continue; |
| 532 | std::string host = popular_site.url.host(); |
atanasova | 9514b30 | 2016-02-26 23:09:28 | [diff] [blame] | 533 | // Skip suggestions already present in personal or whitelists. |
| 534 | if (hosts.find(host) != hosts.end()) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 535 | continue; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 536 | |
dcheng | aeceb05e | 2016-04-07 23:41:10 | [diff] [blame] | 537 | std::unique_ptr<Suggestion> suggestion(new Suggestion()); |
atanasova | c3c25d1 | 2016-03-17 18:33:57 | [diff] [blame] | 538 | suggestion->title = popular_site.title; |
| 539 | suggestion->url = GURL(popular_site.url); |
| 540 | suggestion->source = POPULAR; |
| 541 | |
| 542 | popular_sites_suggestions.push_back(std::move(suggestion)); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 543 | if (popular_sites_suggestions.size() >= num_popular_sites_suggestions) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 544 | break; |
| 545 | } |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 546 | } |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 547 | return popular_sites_suggestions; |
| 548 | } |
| 549 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 550 | void MostVisitedSites::SaveNewSuggestions( |
| 551 | SuggestionsPtrVector* personal_suggestions) { |
| 552 | SuggestionsPtrVector whitelist_suggestions = |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 553 | CreateWhitelistEntryPointSuggestions(*personal_suggestions); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 554 | SuggestionsPtrVector popular_sites_suggestions = |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 555 | CreatePopularSitesSuggestions(*personal_suggestions, |
| 556 | whitelist_suggestions); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 557 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 558 | size_t num_actual_tiles = personal_suggestions->size() + |
| 559 | whitelist_suggestions.size() + |
| 560 | popular_sites_suggestions.size(); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 561 | DCHECK_LE(num_actual_tiles, static_cast<size_t>(num_sites_)); |
| 562 | |
| 563 | SuggestionsPtrVector merged_suggestions = MergeSuggestions( |
| 564 | personal_suggestions, &whitelist_suggestions, &popular_sites_suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 565 | DCHECK_EQ(num_actual_tiles, merged_suggestions.size()); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 566 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 567 | current_suggestions_.resize(merged_suggestions.size()); |
| 568 | for (size_t i = 0; i < merged_suggestions.size(); ++i) |
| 569 | std::swap(*merged_suggestions[i], current_suggestions_[i]); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 570 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 571 | if (received_popular_sites_) |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 572 | SaveCurrentSuggestionsToPrefs(); |
treib | cf674e6 | 2015-08-17 15:43:01 | [diff] [blame] | 573 | } |
| 574 | |
| 575 | // static |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 576 | MostVisitedSites::SuggestionsPtrVector MostVisitedSites::MergeSuggestions( |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 577 | SuggestionsPtrVector* personal_suggestions, |
| 578 | SuggestionsPtrVector* whitelist_suggestions, |
| 579 | SuggestionsPtrVector* popular_suggestions) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 580 | size_t num_personal_suggestions = personal_suggestions->size(); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 581 | size_t num_whitelist_suggestions = whitelist_suggestions->size(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 582 | size_t num_popular_suggestions = popular_suggestions->size(); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 583 | size_t num_tiles = num_popular_suggestions + num_whitelist_suggestions + |
| 584 | num_personal_suggestions; |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 585 | SuggestionsPtrVector merged_suggestions; |
| 586 | AppendSuggestions(personal_suggestions, &merged_suggestions); |
| 587 | AppendSuggestions(whitelist_suggestions, &merged_suggestions); |
| 588 | AppendSuggestions(popular_suggestions, &merged_suggestions); |
| 589 | DCHECK_EQ(num_tiles, merged_suggestions.size()); |
treib | cf674e6 | 2015-08-17 15:43:01 | [diff] [blame] | 590 | |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 591 | return merged_suggestions; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 592 | } |
| 593 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 594 | // TODO(treib): Once we use SuggestionsVector (non-Ptr) everywhere, move this |
| 595 | // into an anonymous namespace. |
| 596 | // static |
| 597 | void MostVisitedSites::AppendSuggestions(SuggestionsPtrVector* src, |
| 598 | SuggestionsPtrVector* dst) { |
| 599 | dst->insert(dst->end(), |
| 600 | std::make_move_iterator(src->begin()), |
| 601 | std::make_move_iterator(src->end())); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 602 | } |
| 603 | |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 604 | void MostVisitedSites::SaveCurrentSuggestionsToPrefs() { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 605 | base::ListValue url_list; |
| 606 | base::ListValue source_list; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame] | 607 | for (const auto& suggestion : current_suggestions_) { |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 608 | url_list.AppendString(suggestion.url.spec()); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 609 | source_list.AppendBoolean(suggestion.source != POPULAR); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 610 | } |
sfiera | 7e952d4 | 2016-05-04 17:56:33 | [diff] [blame] | 611 | prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsIsPersonal, source_list); |
| 612 | prefs_->Set(ntp_tiles::prefs::kNTPSuggestionsURL, url_list); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 613 | } |
| 614 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 615 | void MostVisitedSites::NotifyMostVisitedURLsObserver() { |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 616 | if (received_most_visited_sites_ && received_popular_sites_ && |
| 617 | !recorded_uma_) { |
| 618 | RecordImpressionUMAMetrics(); |
treib | 5a59f54 | 2016-05-18 12:17:29 | [diff] [blame] | 619 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", |
| 620 | current_suggestions_.size()); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 621 | recorded_uma_ = true; |
| 622 | } |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 623 | |
sfiera | 1e7993c6 | 2016-04-18 09:43:26 | [diff] [blame] | 624 | if (!observer_) |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 625 | return; |
| 626 | |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 627 | observer_->OnMostVisitedURLsAvailable(current_suggestions_); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 628 | } |
| 629 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 630 | void MostVisitedSites::OnPopularSitesAvailable(bool success) { |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 631 | received_popular_sites_ = true; |
| 632 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 633 | if (!success) { |
| 634 | LOG(WARNING) << "Download of popular sites failed"; |
| 635 | return; |
| 636 | } |
| 637 | |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 638 | // Pass the popular sites to the observer. This will cause it to fetch any |
| 639 | // missing icons, but will *not* cause it to display the popular sites. |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 640 | observer_->OnPopularURLsAvailable(popular_sites_->sites()); |
treib | c1bd1f1 | 2016-04-29 15:06:05 | [diff] [blame] | 641 | |
| 642 | // Re-build the suggestions list. Once done, this will notify the observer. |
| 643 | BuildCurrentSuggestions(); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 644 | } |
| 645 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 646 | void MostVisitedSites::RecordImpressionUMAMetrics() { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 647 | for (size_t i = 0; i < current_suggestions_.size(); i++) { |
| 648 | std::string histogram = base::StringPrintf( |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 649 | "NewTabPage.SuggestionsImpression.%s", |
sfiera | 8df7b38 | 2016-04-26 14:06:33 | [diff] [blame] | 650 | GetSourceHistogramName(current_suggestions_[i]).c_str()); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 651 | LogHistogramEvent(histogram, static_cast<int>(i), num_sites_); |
| 652 | } |
| 653 | } |
| 654 | |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 655 | void MostVisitedSites::TopSitesLoaded(TopSites* top_sites) {} |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 656 | |
treib | 16070ce | 2016-03-11 11:57:40 | [diff] [blame] | 657 | void MostVisitedSites::TopSitesChanged(TopSites* top_sites, |
fserb | db57511 | 2015-06-29 21:31:59 | [diff] [blame] | 658 | ChangeReason change_reason) { |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 659 | if (mv_source_ == TOP_SITES) { |
| 660 | // The displayed suggestions are invalidated. |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 661 | InitiateTopSitesQuery(); |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 662 | } |
| 663 | } |