[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 | |
| 5 | #include "chrome/browser/android/most_visited_sites.h" |
| 6 | |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 7 | #include <utility> |
| 8 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 9 | #include "base/android/jni_android.h" |
| 10 | #include "base/android/jni_array.h" |
| 11 | #include "base/android/jni_string.h" |
| 12 | #include "base/android/scoped_java_ref.h" |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 13 | #include "base/callback.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 14 | #include "base/command_line.h" |
zea | 9c7707d | 2015-11-03 21:41:14 | [diff] [blame] | 15 | #include "base/metrics/field_trial.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 16 | #include "base/metrics/histogram.h" |
[email protected] | 1d778e1 | 2014-06-17 02:28:51 | [diff] [blame] | 17 | #include "base/metrics/sparse_histogram.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 18 | #include "base/strings/string_number_conversions.h" |
treib | eba6cee | 2015-09-09 13:25:59 | [diff] [blame] | 19 | #include "base/strings/string_util.h" |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 20 | #include "base/strings/stringprintf.h" |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 21 | #include "base/strings/utf_string_conversions.h" |
[email protected] | 6d21c70 | 2014-05-15 06:10:21 | [diff] [blame] | 22 | #include "base/time/time.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 23 | #include "chrome/browser/android/popular_sites.h" |
jitendra.ks | 30f0339 | 2015-01-28 09:47:18 | [diff] [blame] | 24 | #include "chrome/browser/history/top_sites_factory.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 25 | #include "chrome/browser/profiles/profile.h" |
| 26 | #include "chrome/browser/profiles/profile_android.h" |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 27 | #include "chrome/browser/search/suggestions/suggestions_service_factory.h" |
[email protected] | 81e09e7 | 2014-05-07 13:41:22 | [diff] [blame] | 28 | #include "chrome/browser/search/suggestions/suggestions_source.h" |
treib | b75f636a | 2016-02-12 10:21:12 | [diff] [blame] | 29 | #include "chrome/browser/search/suggestions/suggestions_utils.h" |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 30 | #include "chrome/browser/supervised_user/supervised_user_service.h" |
| 31 | #include "chrome/browser/supervised_user/supervised_user_service_factory.h" |
[email protected] | 1d1ab15 | 2014-08-21 17:14:12 | [diff] [blame] | 32 | #include "chrome/browser/sync/profile_sync_service_factory.h" |
[email protected] | 81e09e7 | 2014-05-07 13:41:22 | [diff] [blame] | 33 | #include "chrome/browser/thumbnails/thumbnail_list_source.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 34 | #include "chrome/common/chrome_switches.h" |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 35 | #include "chrome/common/pref_names.h" |
blundell | 7282b51 | 2015-11-09 07:21:11 | [diff] [blame] | 36 | #include "components/browser_sync/browser/profile_sync_service.h" |
sdefresne | 0da3bc0 | 2015-01-29 18:26:35 | [diff] [blame] | 37 | #include "components/history/core/browser/top_sites.h" |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 38 | #include "components/pref_registry/pref_registry_syncable.h" |
brettw | b1fc1b8 | 2016-02-02 00:19:08 | [diff] [blame] | 39 | #include "components/prefs/pref_service.h" |
[email protected] | bdceb3ba | 2014-07-25 16:47:48 | [diff] [blame] | 40 | #include "components/suggestions/suggestions_service.h" |
zea | 9c7707d | 2015-11-03 21:41:14 | [diff] [blame] | 41 | #include "components/variations/variations_associated_data.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 42 | #include "content/public/browser/browser_thread.h" |
[email protected] | 81e09e7 | 2014-05-07 13:41:22 | [diff] [blame] | 43 | #include "content/public/browser/url_data_source.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 44 | #include "jni/MostVisitedSites_jni.h" |
| 45 | #include "third_party/skia/include/core/SkBitmap.h" |
| 46 | #include "ui/gfx/android/java_bitmap.h" |
| 47 | #include "ui/gfx/codec/jpeg_codec.h" |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 48 | #include "url/gurl.h" |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 49 | |
| 50 | using base::android::AttachCurrentThread; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 51 | using base::android::ConvertJavaStringToUTF8; |
| 52 | using base::android::ScopedJavaGlobalRef; |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 53 | using base::android::ScopedJavaLocalRef; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 54 | using base::android::ToJavaArrayOfStrings; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 55 | using content::BrowserThread; |
| 56 | using history::TopSites; |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 57 | using suggestions::ChromeSuggestion; |
| 58 | using suggestions::SuggestionsProfile; |
| 59 | using suggestions::SuggestionsService; |
| 60 | using suggestions::SuggestionsServiceFactory; |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 61 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 62 | namespace { |
| 63 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 64 | // Identifiers for the various tile sources. |
| 65 | const char kHistogramClientName[] = "client"; |
| 66 | const char kHistogramServerName[] = "server"; |
| 67 | const char kHistogramServerFormat[] = "server%d"; |
| 68 | const char kHistogramPopularName[] = "popular"; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 69 | const char kHistogramWhitelistName[] = "whitelist"; |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 70 | |
treib | 508939a | 2015-08-25 10:07:56 | [diff] [blame] | 71 | const char kPopularSitesFieldTrialName[] = "NTPPopularSites"; |
| 72 | |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 73 | // The visual type of a most visited tile. |
| 74 | // |
| 75 | // These values must stay in sync with the MostVisitedTileType enum |
| 76 | // in histograms.xml. |
| 77 | // |
| 78 | // A Java counterpart will be generated for this enum. |
| 79 | // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp |
| 80 | enum MostVisitedTileType { |
| 81 | // The icon or thumbnail hasn't loaded yet. |
| 82 | NONE, |
| 83 | // The item displays a site's actual favicon or touch icon. |
| 84 | ICON_REAL, |
| 85 | // The item displays a color derived from the site's favicon or touch icon. |
| 86 | ICON_COLOR, |
| 87 | // The item displays a default gray box in place of an icon. |
| 88 | ICON_DEFAULT, |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 89 | NUM_TILE_TYPES, |
| 90 | }; |
| 91 | |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 92 | scoped_ptr<SkBitmap> MaybeFetchLocalThumbnail( |
| 93 | const GURL& url, |
| 94 | const scoped_refptr<TopSites>& top_sites) { |
| 95 | DCHECK_CURRENTLY_ON(BrowserThread::DB); |
| 96 | scoped_refptr<base::RefCountedMemory> image; |
| 97 | scoped_ptr<SkBitmap> bitmap; |
| 98 | if (top_sites && top_sites->GetPageThumbnail(url, false, &image)) |
| 99 | bitmap.reset(gfx::JPEGCodec::Decode(image->front(), image->size())); |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 100 | return bitmap; |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 101 | } |
| 102 | |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 103 | // Log an event for a given |histogram| at a given element |position|. This |
| 104 | // routine exists because regular histogram macros are cached thus can't be used |
| 105 | // if the name of the histogram will change at a given call site. |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 106 | void LogHistogramEvent(const std::string& histogram, |
| 107 | int position, |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 108 | int num_sites) { |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 109 | base::HistogramBase* counter = base::LinearHistogram::FactoryGet( |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 110 | histogram, |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 111 | 1, |
| 112 | num_sites, |
| 113 | num_sites + 1, |
| 114 | base::Histogram::kUmaTargetedHistogramFlag); |
[email protected] | 6f67448 | 2014-08-11 22:50:49 | [diff] [blame] | 115 | if (counter) |
| 116 | counter->Add(position); |
[email protected] | 9ed2f5c | 2014-06-13 14:55:24 | [diff] [blame] | 117 | } |
| 118 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 119 | bool ShouldShowPopularSites() { |
| 120 | // Note: It's important to query the field trial state first, to ensure that |
| 121 | // UMA reports the correct group. |
| 122 | const std::string group_name = |
treib | 508939a | 2015-08-25 10:07:56 | [diff] [blame] | 123 | base::FieldTrialList::FindFullName(kPopularSitesFieldTrialName); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 124 | base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| 125 | if (cmd_line->HasSwitch(switches::kDisableNTPPopularSites)) |
| 126 | return false; |
| 127 | if (cmd_line->HasSwitch(switches::kEnableNTPPopularSites)) |
| 128 | return true; |
treib | eba6cee | 2015-09-09 13:25:59 | [diff] [blame] | 129 | return base::StartsWith(group_name, "Enabled", |
| 130 | base::CompareCase::INSENSITIVE_ASCII); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 131 | } |
| 132 | |
treib | b28d650 | 2015-09-22 14:28:50 | [diff] [blame] | 133 | std::string GetPopularSitesCountry() { |
| 134 | return variations::GetVariationParamValue(kPopularSitesFieldTrialName, |
| 135 | "country"); |
| 136 | } |
| 137 | |
| 138 | std::string GetPopularSitesVersion() { |
| 139 | return variations::GetVariationParamValue(kPopularSitesFieldTrialName, |
| 140 | "version"); |
| 141 | } |
| 142 | |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 143 | // Determine whether we need any popular suggestions to fill up a grid of |
| 144 | // |num_tiles| tiles. |
| 145 | bool NeedPopularSites(const PrefService* prefs, size_t num_tiles) { |
| 146 | const base::ListValue* source_list = |
| 147 | prefs->GetList(prefs::kNTPSuggestionsIsPersonal); |
| 148 | // If there aren't enough previous suggestions to fill the grid, we need |
| 149 | // popular suggestions. |
| 150 | if (source_list->GetSize() < num_tiles) |
| 151 | return true; |
| 152 | // Otherwise, if any of the previous suggestions is not personal, then also |
| 153 | // get popular suggestions. |
| 154 | for (size_t i = 0; i < num_tiles; ++i) { |
| 155 | bool is_personal = false; |
| 156 | if (source_list->GetBoolean(i, &is_personal) && !is_personal) |
| 157 | return true; |
| 158 | } |
| 159 | // The whole grid is already filled with personal suggestions, no point in |
| 160 | // bothering with popular ones. |
| 161 | return false; |
| 162 | } |
| 163 | |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 164 | } // namespace |
| 165 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 166 | MostVisitedSites::Suggestion::Suggestion(const base::string16& title, |
| 167 | const std::string& url, |
| 168 | MostVisitedSource source) |
| 169 | : title(title), url(url), source(source), provider_index(-1) {} |
| 170 | |
| 171 | MostVisitedSites::Suggestion::Suggestion(const base::string16& title, |
| 172 | const GURL& url, |
| 173 | MostVisitedSource source) |
| 174 | : title(title), url(url), source(source), provider_index(-1) {} |
| 175 | |
| 176 | MostVisitedSites::Suggestion::Suggestion(const base::string16& title, |
| 177 | const std::string& url, |
| 178 | MostVisitedSource source, |
| 179 | int provider_index) |
| 180 | : title(title), url(url), source(source), provider_index(provider_index) { |
| 181 | DCHECK_EQ(MostVisitedSites::SUGGESTIONS_SERVICE, source); |
| 182 | } |
| 183 | |
| 184 | MostVisitedSites::Suggestion::~Suggestion() {} |
| 185 | |
| 186 | std::string MostVisitedSites::Suggestion::GetSourceHistogramName() const { |
| 187 | switch (source) { |
| 188 | case MostVisitedSites::TOP_SITES: |
| 189 | return kHistogramClientName; |
| 190 | case MostVisitedSites::POPULAR: |
| 191 | return kHistogramPopularName; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 192 | case MostVisitedSites::WHITELIST: |
| 193 | return kHistogramWhitelistName; |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 194 | case MostVisitedSites::SUGGESTIONS_SERVICE: |
| 195 | return provider_index >= 0 |
| 196 | ? base::StringPrintf(kHistogramServerFormat, provider_index) |
| 197 | : kHistogramServerName; |
| 198 | } |
| 199 | NOTREACHED(); |
| 200 | return std::string(); |
| 201 | } |
| 202 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 203 | MostVisitedSites::MostVisitedSites(Profile* profile) |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 204 | : profile_(profile), num_sites_(0), received_most_visited_sites_(false), |
| 205 | received_popular_sites_(false), recorded_uma_(false), |
mathp | d91e8eb | 2015-03-24 17:37:47 | [diff] [blame] | 206 | scoped_observer_(this), weak_ptr_factory_(this) { |
[email protected] | 81e09e7 | 2014-05-07 13:41:22 | [diff] [blame] | 207 | // Register the debugging page for the Suggestions Service and the thumbnails |
| 208 | // debugging page. |
| 209 | content::URLDataSource::Add(profile_, |
| 210 | new suggestions::SuggestionsSource(profile_)); |
| 211 | content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_)); |
[email protected] | 1d1ab15 | 2014-08-21 17:14:12 | [diff] [blame] | 212 | |
| 213 | // Register this class as an observer to the sync service. It is important to |
| 214 | // be notified of changes in the sync state such as initialization, sync |
| 215 | // being enabled or disabled, etc. |
| 216 | ProfileSyncService* profile_sync_service = |
| 217 | ProfileSyncServiceFactory::GetForProfile(profile_); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 218 | profile_sync_service->AddObserver(this); |
| 219 | |
| 220 | SupervisedUserService* supervised_user_service = |
| 221 | SupervisedUserServiceFactory::GetForProfile(profile_); |
| 222 | supervised_user_service->AddObserver(this); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 223 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 224 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 225 | MostVisitedSites::~MostVisitedSites() { |
feng | 94a4210 | 2014-08-26 23:42:39 | [diff] [blame] | 226 | ProfileSyncService* profile_sync_service = |
| 227 | ProfileSyncServiceFactory::GetForProfile(profile_); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 228 | profile_sync_service->RemoveObserver(this); |
| 229 | |
| 230 | SupervisedUserService* supervised_user_service = |
| 231 | SupervisedUserServiceFactory::GetForProfile(profile_); |
| 232 | supervised_user_service->RemoveObserver(this); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 233 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 234 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 235 | void MostVisitedSites::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 236 | delete this; |
| 237 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 238 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 239 | void MostVisitedSites::SetMostVisitedURLsObserver( |
| 240 | JNIEnv* env, |
| 241 | const JavaParamRef<jobject>& obj, |
| 242 | const JavaParamRef<jobject>& j_observer, |
| 243 | jint num_sites) { |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 244 | observer_.Reset(env, j_observer); |
| 245 | num_sites_ = num_sites; |
[email protected] | 852fc79 | 2013-10-04 04:15:12 | [diff] [blame] | 246 | |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 247 | if (ShouldShowPopularSites() && |
| 248 | NeedPopularSites(profile_->GetPrefs(), num_sites_)) { |
| 249 | popular_sites_.reset(new PopularSites( |
| 250 | profile_, |
treib | b28d650 | 2015-09-22 14:28:50 | [diff] [blame] | 251 | GetPopularSitesCountry(), |
| 252 | GetPopularSitesVersion(), |
treib | d73c9ad | 2015-09-22 16:20:48 | [diff] [blame] | 253 | false, |
treib | f79ed40d | 2015-09-22 12:38:06 | [diff] [blame] | 254 | base::Bind(&MostVisitedSites::OnPopularSitesAvailable, |
| 255 | base::Unretained(this)))); |
| 256 | } else { |
| 257 | received_popular_sites_ = true; |
| 258 | } |
| 259 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 260 | QueryMostVisitedURLs(); |
| 261 | |
jitendra.ks | 30f0339 | 2015-01-28 09:47:18 | [diff] [blame] | 262 | scoped_refptr<history::TopSites> top_sites = |
| 263 | TopSitesFactory::GetForProfile(profile_); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 264 | if (top_sites) { |
| 265 | // TopSites updates itself after a delay. To ensure up-to-date results, |
| 266 | // force an update now. |
| 267 | top_sites->SyncWithHistory(); |
| 268 | |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 269 | // Register as TopSitesObserver so that we can update ourselves when the |
| 270 | // TopSites changes. |
jitendra.ks | 30f0339 | 2015-01-28 09:47:18 | [diff] [blame] | 271 | scoped_observer_.Add(top_sites.get()); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 272 | } |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 273 | } |
| 274 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 275 | void MostVisitedSites::GetURLThumbnail( |
| 276 | JNIEnv* env, |
| 277 | const JavaParamRef<jobject>& obj, |
| 278 | const JavaParamRef<jstring>& j_url, |
| 279 | const JavaParamRef<jobject>& j_callback_obj) { |
mostynb | 351bf98 | 2015-03-25 22:26:58 | [diff] [blame] | 280 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 281 | scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback( |
| 282 | new ScopedJavaGlobalRef<jobject>()); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 283 | j_callback->Reset(env, j_callback_obj); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 284 | |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 285 | GURL url(ConvertJavaStringToUTF8(env, j_url)); |
jitendra.ks | 30f0339 | 2015-01-28 09:47:18 | [diff] [blame] | 286 | scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_)); |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 287 | |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 288 | BrowserThread::PostTaskAndReplyWithResult( |
[email protected] | d33adb40d | 2014-06-03 21:38:46 | [diff] [blame] | 289 | BrowserThread::DB, FROM_HERE, |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 290 | base::Bind(&MaybeFetchLocalThumbnail, url, top_sites), |
| 291 | base::Bind(&MostVisitedSites::OnLocalThumbnailFetched, |
| 292 | weak_ptr_factory_.GetWeakPtr(), url, |
| 293 | base::Passed(&j_callback))); |
| 294 | } |
| 295 | |
| 296 | void MostVisitedSites::OnLocalThumbnailFetched( |
| 297 | const GURL& url, |
| 298 | scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback, |
| 299 | scoped_ptr<SkBitmap> bitmap) { |
| 300 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 301 | if (!bitmap.get()) { |
| 302 | // A thumbnail is not locally available for |url|. Make sure it is put in |
| 303 | // the list to be fetched at the next visit to this site. |
| 304 | scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_)); |
| 305 | if (top_sites) |
| 306 | top_sites->AddForcedURL(url, base::Time::Now()); |
treib | ac5372f | 2015-09-09 09:08:22 | [diff] [blame] | 307 | // Also fetch a remote thumbnail if possible. PopularSites or the |
| 308 | // SuggestionsService can supply a thumbnail download URL. |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 309 | SuggestionsService* suggestions_service = |
treib | ac5372f | 2015-09-09 09:08:22 | [diff] [blame] | 310 | SuggestionsServiceFactory::GetForProfile(profile_); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 311 | if (suggestions_service) { |
treib | ac5372f | 2015-09-09 09:08:22 | [diff] [blame] | 312 | if (popular_sites_) { |
| 313 | const std::vector<PopularSites::Site>& sites = popular_sites_->sites(); |
| 314 | auto it = std::find_if(sites.begin(), sites.end(), |
| 315 | [&url](const PopularSites::Site& site) { |
| 316 | return site.url == url; |
| 317 | }); |
| 318 | if (it != sites.end() && it->thumbnail_url.is_valid()) { |
| 319 | return suggestions_service->GetPageThumbnailWithURL( |
| 320 | url, it->thumbnail_url, |
| 321 | base::Bind(&MostVisitedSites::OnObtainedThumbnail, |
| 322 | weak_ptr_factory_.GetWeakPtr(), false, |
| 323 | base::Passed(&j_callback))); |
| 324 | } |
| 325 | } |
| 326 | if (mv_source_ == SUGGESTIONS_SERVICE) { |
| 327 | return suggestions_service->GetPageThumbnail( |
| 328 | url, base::Bind(&MostVisitedSites::OnObtainedThumbnail, |
| 329 | weak_ptr_factory_.GetWeakPtr(), false, |
| 330 | base::Passed(&j_callback))); |
| 331 | } |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 332 | } |
| 333 | } |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 334 | OnObtainedThumbnail(true, std::move(j_callback), url, bitmap.get()); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 335 | } |
| 336 | |
| 337 | void MostVisitedSites::OnObtainedThumbnail( |
| 338 | bool is_local_thumbnail, |
| 339 | scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback, |
| 340 | const GURL& url, |
| 341 | const SkBitmap* bitmap) { |
| 342 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 343 | JNIEnv* env = AttachCurrentThread(); |
| 344 | ScopedJavaLocalRef<jobject> j_bitmap; |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 345 | if (bitmap) |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 346 | j_bitmap = gfx::ConvertToJavaBitmap(bitmap); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 347 | Java_ThumbnailCallback_onMostVisitedURLsThumbnailAvailable( |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 348 | env, j_callback->obj(), j_bitmap.obj(), is_local_thumbnail); |
[email protected] | c764b282 | 2013-07-03 04:16:48 | [diff] [blame] | 349 | } |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 350 | |
newt | 4f36ac0b | 2016-01-21 02:49:25 | [diff] [blame] | 351 | void MostVisitedSites::AddOrRemoveBlacklistedUrl( |
| 352 | JNIEnv* env, |
| 353 | const JavaParamRef<jobject>& obj, |
| 354 | const JavaParamRef<jstring>& j_url, |
| 355 | jboolean add_url) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 356 | GURL url(ConvertJavaStringToUTF8(env, j_url)); |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 357 | |
treib | fac7519 | 2015-08-17 13:21:24 | [diff] [blame] | 358 | // Always blacklist in the local TopSites. |
| 359 | scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_); |
newt | 4f36ac0b | 2016-01-21 02:49:25 | [diff] [blame] | 360 | if (top_sites) { |
| 361 | if (add_url) { |
| 362 | top_sites->AddBlacklistedURL(url); |
| 363 | } else { |
| 364 | top_sites->RemoveBlacklistedURL(url); |
| 365 | } |
| 366 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 367 | |
treib | fac7519 | 2015-08-17 13:21:24 | [diff] [blame] | 368 | // Only blacklist in the server-side suggestions service if it's active. |
| 369 | if (mv_source_ == SUGGESTIONS_SERVICE) { |
| 370 | SuggestionsService* suggestions_service = |
| 371 | SuggestionsServiceFactory::GetForProfile(profile_); |
| 372 | DCHECK(suggestions_service); |
newt | 4f36ac0b | 2016-01-21 02:49:25 | [diff] [blame] | 373 | suggestions::SuggestionsService::ResponseCallback callback( |
| 374 | base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable, |
| 375 | weak_ptr_factory_.GetWeakPtr())); |
| 376 | if (add_url) { |
| 377 | suggestions_service->BlacklistURL(url, callback, base::Closure()); |
| 378 | } else { |
| 379 | suggestions_service->UndoBlacklistURL(url, callback, base::Closure()); |
| 380 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 381 | } |
[email protected] | a4c2f28 | 2013-07-20 05:26:05 | [diff] [blame] | 382 | } |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 383 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 384 | void MostVisitedSites::RecordTileTypeMetrics( |
| 385 | JNIEnv* env, |
| 386 | const JavaParamRef<jobject>& obj, |
Newton Allen | ea5f0c0 | 2015-12-04 03:02:41 | [diff] [blame] | 387 | const JavaParamRef<jintArray>& jtile_types) { |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 388 | std::vector<int> tile_types; |
| 389 | base::android::JavaIntArrayToIntVector(env, jtile_types, &tile_types); |
| 390 | DCHECK_EQ(current_suggestions_.size(), tile_types.size()); |
| 391 | |
| 392 | int counts_per_type[NUM_TILE_TYPES] = {0}; |
| 393 | for (size_t i = 0; i < tile_types.size(); ++i) { |
| 394 | int tile_type = tile_types[i]; |
| 395 | ++counts_per_type[tile_type]; |
| 396 | std::string histogram = base::StringPrintf( |
| 397 | "NewTabPage.TileType.%s", |
| 398 | current_suggestions_[i]->GetSourceHistogramName().c_str()); |
| 399 | LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES); |
| 400 | } |
| 401 | |
Newton Allen | ea5f0c0 | 2015-12-04 03:02:41 | [diff] [blame] | 402 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal", |
| 403 | counts_per_type[ICON_REAL]); |
| 404 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor", |
| 405 | counts_per_type[ICON_COLOR]); |
| 406 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray", |
| 407 | counts_per_type[ICON_DEFAULT]); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 408 | } |
| 409 | |
torne | e621a27 | 2015-11-30 18:40:53 | [diff] [blame] | 410 | void MostVisitedSites::RecordOpenedMostVisitedItem( |
| 411 | JNIEnv* env, |
| 412 | const JavaParamRef<jobject>& obj, |
| 413 | jint index, |
| 414 | jint tile_type) { |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 415 | DCHECK_GE(index, 0); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 416 | DCHECK_LT(index, static_cast<int>(current_suggestions_.size())); |
| 417 | std::string histogram = base::StringPrintf( |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 418 | "NewTabPage.MostVisited.%s", |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 419 | current_suggestions_[index]->GetSourceHistogramName().c_str()); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 420 | LogHistogramEvent(histogram, index, num_sites_); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 421 | |
| 422 | histogram = base::StringPrintf( |
| 423 | "NewTabPage.TileTypeClicked.%s", |
| 424 | current_suggestions_[index]->GetSourceHistogramName().c_str()); |
| 425 | LogHistogramEvent(histogram, tile_type, NUM_TILE_TYPES); |
[email protected] | 92a59882 | 2014-06-17 10:25:56 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | 1d1ab15 | 2014-08-21 17:14:12 | [diff] [blame] | 428 | void MostVisitedSites::OnStateChanged() { |
| 429 | // There have been changes to the sync state. This class cares about a few |
| 430 | // (just initialized, enabled/disabled or history sync state changed). Re-run |
| 431 | // the query code which will use the proper state. |
| 432 | QueryMostVisitedURLs(); |
| 433 | } |
| 434 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 435 | void MostVisitedSites::OnURLFilterChanged() { |
| 436 | QueryMostVisitedURLs(); |
| 437 | } |
| 438 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 439 | // static |
| 440 | bool MostVisitedSites::Register(JNIEnv* env) { |
| 441 | return RegisterNativesImpl(env); |
| 442 | } |
| 443 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 444 | // static |
| 445 | void MostVisitedSites::RegisterProfilePrefs( |
| 446 | user_prefs::PrefRegistrySyncable* registry) { |
| 447 | registry->RegisterListPref(prefs::kNTPSuggestionsURL); |
| 448 | registry->RegisterListPref(prefs::kNTPSuggestionsIsPersonal); |
| 449 | } |
| 450 | |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 451 | void MostVisitedSites::QueryMostVisitedURLs() { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 452 | SuggestionsService* suggestions_service = |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 453 | SuggestionsServiceFactory::GetForProfile(profile_); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 454 | if (suggestions_service) { |
| 455 | // Suggestions service is enabled, initiate a query. |
| 456 | suggestions_service->FetchSuggestionsData( |
treib | b75f636a | 2016-02-12 10:21:12 | [diff] [blame] | 457 | suggestions::GetSyncState(profile_), |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 458 | base::Bind(&MostVisitedSites::OnSuggestionsProfileAvailable, |
| 459 | weak_ptr_factory_.GetWeakPtr())); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 460 | } else { |
| 461 | InitiateTopSitesQuery(); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | void MostVisitedSites::InitiateTopSitesQuery() { |
jitendra.ks | 30f0339 | 2015-01-28 09:47:18 | [diff] [blame] | 466 | scoped_refptr<TopSites> top_sites = TopSitesFactory::GetForProfile(profile_); |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 467 | if (!top_sites) |
| 468 | return; |
| 469 | |
| 470 | top_sites->GetMostVisitedURLs( |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 471 | base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable, |
| 472 | weak_ptr_factory_.GetWeakPtr()), |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 473 | false); |
| 474 | } |
| 475 | |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 476 | void MostVisitedSites::OnMostVisitedURLsAvailable( |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 477 | const history::MostVisitedURLList& visited_list) { |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 478 | MostVisitedSites::SuggestionsVector suggestions; |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 479 | size_t num_tiles = |
| 480 | std::min(visited_list.size(), static_cast<size_t>(num_sites_)); |
| 481 | for (size_t i = 0; i < num_tiles; ++i) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 482 | const history::MostVisitedURL& visited = visited_list[i]; |
| 483 | if (visited.url.is_empty()) { |
| 484 | num_tiles = i; |
| 485 | break; // This is the signal that there are no more real visited sites. |
| 486 | } |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 487 | suggestions.push_back(make_scoped_ptr( |
| 488 | new Suggestion(visited.title, visited.url.spec(), TOP_SITES))); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 489 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 490 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 491 | received_most_visited_sites_ = true; |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 492 | mv_source_ = TOP_SITES; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 493 | SaveNewNTPSuggestions(&suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 494 | NotifyMostVisitedURLsObserver(); |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 495 | } |
| 496 | |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 497 | void MostVisitedSites::OnSuggestionsProfileAvailable( |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 498 | const SuggestionsProfile& suggestions_profile) { |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 499 | int num_tiles = suggestions_profile.suggestions_size(); |
mathp | d91e8eb | 2015-03-24 17:37:47 | [diff] [blame] | 500 | // With no server suggestions, fall back to local Most Visited. |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 501 | if (num_tiles == 0) { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 502 | InitiateTopSitesQuery(); |
| 503 | return; |
| 504 | } |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 505 | if (num_sites_ < num_tiles) |
| 506 | num_tiles = num_sites_; |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 507 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 508 | MostVisitedSites::SuggestionsVector suggestions; |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 509 | for (int i = 0; i < num_tiles; ++i) { |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 510 | const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 511 | suggestions.push_back(make_scoped_ptr(new Suggestion( |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 512 | base::UTF8ToUTF16(suggestion.title()), suggestion.url(), |
| 513 | SUGGESTIONS_SERVICE, |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 514 | suggestion.providers_size() > 0 ? suggestion.providers(0) : -1))); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 515 | } |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 516 | |
| 517 | received_most_visited_sites_ = true; |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 518 | mv_source_ = SUGGESTIONS_SERVICE; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 519 | SaveNewNTPSuggestions(&suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 520 | NotifyMostVisitedURLsObserver(); |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 521 | } |
[email protected] | d497524 | 2014-06-02 18:16:20 | [diff] [blame] | 522 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 523 | MostVisitedSites::SuggestionsVector |
| 524 | MostVisitedSites::CreateWhitelistEntryPointSuggestions( |
| 525 | const MostVisitedSites::SuggestionsVector& personal_suggestions) { |
| 526 | size_t num_personal_suggestions = personal_suggestions.size(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 527 | DCHECK_LE(num_personal_suggestions, static_cast<size_t>(num_sites_)); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 528 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 529 | size_t num_whitelist_suggestions = num_sites_ - num_personal_suggestions; |
| 530 | MostVisitedSites::SuggestionsVector whitelist_suggestions; |
| 531 | |
| 532 | SupervisedUserService* supervised_user_service = |
| 533 | SupervisedUserServiceFactory::GetForProfile(profile_); |
| 534 | |
| 535 | for (const auto& whitelist : supervised_user_service->whitelists()) { |
| 536 | whitelist_suggestions.push_back(make_scoped_ptr(new Suggestion( |
| 537 | whitelist->title(), whitelist->entry_point(), WHITELIST))); |
| 538 | if (whitelist_suggestions.size() >= num_whitelist_suggestions) |
| 539 | break; |
| 540 | } |
| 541 | |
| 542 | return whitelist_suggestions; |
| 543 | } |
| 544 | |
| 545 | MostVisitedSites::SuggestionsVector |
| 546 | MostVisitedSites::CreatePopularSitesSuggestions( |
| 547 | const MostVisitedSites::SuggestionsVector& personal_suggestions, |
| 548 | const MostVisitedSites::SuggestionsVector& whitelist_suggestions) { |
| 549 | size_t num_suggestions = |
| 550 | personal_suggestions.size() + whitelist_suggestions.size(); |
| 551 | DCHECK_LE(num_suggestions, static_cast<size_t>(num_sites_)); |
| 552 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 553 | // Collect non-blacklisted popular suggestions, skipping those already present |
| 554 | // in the personal suggestions. |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 555 | size_t num_popular_sites_suggestions = num_sites_ - num_suggestions; |
| 556 | MostVisitedSites::SuggestionsVector popular_sites_suggestions; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 557 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 558 | if (num_popular_sites_suggestions > 0 && popular_sites_) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 559 | std::set<std::string> personal_hosts; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 560 | for (const auto& suggestion : personal_suggestions) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 561 | personal_hosts.insert(suggestion->url.host()); |
| 562 | scoped_refptr<TopSites> top_sites(TopSitesFactory::GetForProfile(profile_)); |
| 563 | for (const PopularSites::Site& popular_site : popular_sites_->sites()) { |
| 564 | // Skip blacklisted sites. |
| 565 | if (top_sites && top_sites->IsBlacklisted(popular_site.url)) |
| 566 | continue; |
| 567 | std::string host = popular_site.url.host(); |
| 568 | // Skip suggestions already present in personal. |
| 569 | if (personal_hosts.find(host) != personal_hosts.end()) |
| 570 | continue; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 571 | |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 572 | popular_sites_suggestions.push_back(make_scoped_ptr( |
| 573 | new Suggestion(popular_site.title, popular_site.url, POPULAR))); |
| 574 | if (popular_sites_suggestions.size() >= num_popular_sites_suggestions) |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 575 | break; |
| 576 | } |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 577 | } |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 578 | return popular_sites_suggestions; |
| 579 | } |
| 580 | |
| 581 | void MostVisitedSites::SaveNewNTPSuggestions( |
| 582 | MostVisitedSites::SuggestionsVector* personal_suggestions) { |
| 583 | MostVisitedSites::SuggestionsVector whitelist_suggestions = |
| 584 | CreateWhitelistEntryPointSuggestions(*personal_suggestions); |
| 585 | MostVisitedSites::SuggestionsVector popular_sites_suggestions = |
| 586 | CreatePopularSitesSuggestions(*personal_suggestions, |
| 587 | whitelist_suggestions); |
| 588 | size_t num_actual_tiles = personal_suggestions->size() + |
| 589 | whitelist_suggestions.size() + |
| 590 | popular_sites_suggestions.size(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 591 | std::vector<std::string> old_sites_url; |
| 592 | std::vector<bool> old_sites_is_personal; |
| 593 | GetPreviousNTPSites(num_actual_tiles, &old_sites_url, &old_sites_is_personal); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 594 | MostVisitedSites::SuggestionsVector merged_suggestions = MergeSuggestions( |
| 595 | personal_suggestions, &whitelist_suggestions, &popular_sites_suggestions, |
| 596 | old_sites_url, old_sites_is_personal); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 597 | DCHECK_EQ(num_actual_tiles, merged_suggestions.size()); |
| 598 | current_suggestions_.swap(merged_suggestions); |
| 599 | if (received_popular_sites_) |
| 600 | SaveCurrentNTPSites(); |
treib | cf674e6 | 2015-08-17 15:43:01 | [diff] [blame] | 601 | } |
| 602 | |
| 603 | // static |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 604 | MostVisitedSites::SuggestionsVector MostVisitedSites::MergeSuggestions( |
| 605 | MostVisitedSites::SuggestionsVector* personal_suggestions, |
| 606 | MostVisitedSites::SuggestionsVector* whitelist_suggestions, |
| 607 | MostVisitedSites::SuggestionsVector* popular_suggestions, |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 608 | const std::vector<std::string>& old_sites_url, |
| 609 | const std::vector<bool>& old_sites_is_personal) { |
| 610 | size_t num_personal_suggestions = personal_suggestions->size(); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 611 | size_t num_whitelist_suggestions = whitelist_suggestions->size(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 612 | size_t num_popular_suggestions = popular_suggestions->size(); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 613 | size_t num_tiles = num_popular_suggestions + num_whitelist_suggestions + |
| 614 | num_personal_suggestions; |
| 615 | MostVisitedSites::SuggestionsVector merged_suggestions; |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 616 | merged_suggestions.resize(num_tiles); |
treib | cf674e6 | 2015-08-17 15:43:01 | [diff] [blame] | 617 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 618 | size_t num_old_tiles = old_sites_url.size(); |
| 619 | DCHECK_LE(num_old_tiles, num_tiles); |
| 620 | DCHECK_EQ(num_old_tiles, old_sites_is_personal.size()); |
| 621 | std::vector<std::string> old_sites_host; |
| 622 | old_sites_host.reserve(num_old_tiles); |
| 623 | // Only populate the hosts for popular suggestions as only they can be |
| 624 | // replaced by host. Personal suggestions require an exact url match to be |
| 625 | // replaced. |
| 626 | for (size_t i = 0; i < num_old_tiles; ++i) { |
| 627 | old_sites_host.push_back(old_sites_is_personal[i] |
| 628 | ? std::string() |
| 629 | : GURL(old_sites_url[i]).host()); |
treib | cf674e6 | 2015-08-17 15:43:01 | [diff] [blame] | 630 | } |
| 631 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 632 | // Insert personal suggestions if they existed previously. |
| 633 | std::vector<size_t> new_personal_suggestions = InsertMatchingSuggestions( |
| 634 | personal_suggestions, &merged_suggestions, old_sites_url, old_sites_host); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 635 | // Insert whitelist suggestions if they existed previously. |
| 636 | std::vector<size_t> new_whitelist_suggestions = |
| 637 | InsertMatchingSuggestions(whitelist_suggestions, &merged_suggestions, |
| 638 | old_sites_url, old_sites_host); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 639 | // Insert popular suggestions if they existed previously. |
| 640 | std::vector<size_t> new_popular_suggestions = InsertMatchingSuggestions( |
| 641 | popular_suggestions, &merged_suggestions, old_sites_url, old_sites_host); |
| 642 | // Insert leftover personal suggestions. |
| 643 | size_t filled_so_far = InsertAllSuggestions( |
| 644 | 0, new_personal_suggestions, personal_suggestions, &merged_suggestions); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 645 | // Insert leftover whitelist suggestions. |
| 646 | filled_so_far = |
| 647 | InsertAllSuggestions(filled_so_far, new_whitelist_suggestions, |
| 648 | whitelist_suggestions, &merged_suggestions); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 649 | // Insert leftover popular suggestions. |
| 650 | InsertAllSuggestions(filled_so_far, new_popular_suggestions, |
| 651 | popular_suggestions, &merged_suggestions); |
dcheng | bc6125d | 2015-12-30 01:54:07 | [diff] [blame] | 652 | return merged_suggestions; |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 653 | } |
| 654 | |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 655 | void MostVisitedSites::GetPreviousNTPSites( |
| 656 | size_t num_tiles, |
| 657 | std::vector<std::string>* old_sites_url, |
| 658 | std::vector<bool>* old_sites_is_personal) const { |
| 659 | const PrefService* prefs = profile_->GetPrefs(); |
| 660 | const base::ListValue* url_list = prefs->GetList(prefs::kNTPSuggestionsURL); |
| 661 | const base::ListValue* source_list = |
| 662 | prefs->GetList(prefs::kNTPSuggestionsIsPersonal); |
| 663 | DCHECK_EQ(url_list->GetSize(), source_list->GetSize()); |
| 664 | if (url_list->GetSize() < num_tiles) |
| 665 | num_tiles = url_list->GetSize(); |
| 666 | if (num_tiles == 0) { |
| 667 | // No fallback required as Personal suggestions take precedence anyway. |
| 668 | return; |
| 669 | } |
| 670 | old_sites_url->reserve(num_tiles); |
| 671 | old_sites_is_personal->reserve(num_tiles); |
| 672 | for (size_t i = 0; i < num_tiles; ++i) { |
| 673 | std::string url_string; |
| 674 | bool success = url_list->GetString(i, &url_string); |
| 675 | DCHECK(success); |
| 676 | old_sites_url->push_back(url_string); |
| 677 | bool is_personal; |
| 678 | success = source_list->GetBoolean(i, &is_personal); |
| 679 | DCHECK(success); |
| 680 | old_sites_is_personal->push_back(is_personal); |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | void MostVisitedSites::SaveCurrentNTPSites() { |
| 685 | base::ListValue url_list; |
| 686 | base::ListValue source_list; |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 687 | for (const auto& suggestion : current_suggestions_) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 688 | url_list.AppendString(suggestion->url.spec()); |
| 689 | source_list.AppendBoolean(suggestion->source != MostVisitedSites::POPULAR); |
| 690 | } |
| 691 | PrefService* prefs = profile_->GetPrefs(); |
| 692 | prefs->Set(prefs::kNTPSuggestionsIsPersonal, source_list); |
| 693 | prefs->Set(prefs::kNTPSuggestionsURL, url_list); |
| 694 | } |
| 695 | |
| 696 | // static |
| 697 | std::vector<size_t> MostVisitedSites::InsertMatchingSuggestions( |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 698 | MostVisitedSites::SuggestionsVector* src_suggestions, |
| 699 | MostVisitedSites::SuggestionsVector* dst_suggestions, |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 700 | const std::vector<std::string>& match_urls, |
| 701 | const std::vector<std::string>& match_hosts) { |
| 702 | std::vector<size_t> unmatched_suggestions; |
| 703 | size_t num_src_suggestions = src_suggestions->size(); |
| 704 | size_t num_matchers = match_urls.size(); |
| 705 | for (size_t i = 0; i < num_src_suggestions; ++i) { |
| 706 | size_t position; |
| 707 | for (position = 0; position < num_matchers; ++position) { |
| 708 | if ((*dst_suggestions)[position] != nullptr) |
| 709 | continue; |
| 710 | if (match_urls[position] == (*src_suggestions)[i]->url.spec()) |
| 711 | break; |
| 712 | // match_hosts is only populated for suggestions which can be replaced by |
| 713 | // host matching like popular suggestions. |
| 714 | if (match_hosts[position] == (*src_suggestions)[i]->url.host()) |
| 715 | break; |
| 716 | } |
| 717 | if (position == num_matchers) { |
| 718 | unmatched_suggestions.push_back(i); |
| 719 | } else { |
| 720 | // A move is required as the source and destination containers own the |
| 721 | // elements. |
| 722 | std::swap((*dst_suggestions)[position], (*src_suggestions)[i]); |
| 723 | } |
| 724 | } |
| 725 | return unmatched_suggestions; |
| 726 | } |
| 727 | |
| 728 | // static |
| 729 | size_t MostVisitedSites::InsertAllSuggestions( |
| 730 | size_t start_position, |
| 731 | const std::vector<size_t>& insert_positions, |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 732 | std::vector<scoped_ptr<Suggestion>>* src_suggestions, |
| 733 | std::vector<scoped_ptr<Suggestion>>* dst_suggestions) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 734 | size_t num_inserts = insert_positions.size(); |
| 735 | size_t num_dests = dst_suggestions->size(); |
| 736 | |
| 737 | size_t src_pos = 0; |
| 738 | size_t i = start_position; |
| 739 | for (; i < num_dests && src_pos < num_inserts; ++i) { |
| 740 | if ((*dst_suggestions)[i] != nullptr) |
| 741 | continue; |
| 742 | size_t src = insert_positions[src_pos++]; |
| 743 | std::swap((*dst_suggestions)[i], (*src_suggestions)[src]); |
| 744 | } |
| 745 | // Return destination postions filled so far which becomes the start_position |
| 746 | // for future runs. |
| 747 | return i; |
| 748 | } |
| 749 | |
| 750 | void MostVisitedSites::NotifyMostVisitedURLsObserver() { |
| 751 | size_t num_suggestions = current_suggestions_.size(); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 752 | if (received_most_visited_sites_ && received_popular_sites_ && |
| 753 | !recorded_uma_) { |
| 754 | RecordImpressionUMAMetrics(); |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 755 | UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", num_suggestions); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 756 | recorded_uma_ = true; |
| 757 | } |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 758 | std::vector<base::string16> titles; |
| 759 | std::vector<std::string> urls; |
| 760 | titles.reserve(num_suggestions); |
| 761 | urls.reserve(num_suggestions); |
atanasova | 9572aaf | 2016-02-26 18:08:26 | [diff] [blame^] | 762 | for (const auto& suggestion : current_suggestions_) { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 763 | titles.push_back(suggestion->title); |
| 764 | urls.push_back(suggestion->url.spec()); |
| 765 | } |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 766 | JNIEnv* env = AttachCurrentThread(); |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 767 | DCHECK_EQ(titles.size(), urls.size()); |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 768 | Java_MostVisitedURLsObserver_onMostVisitedURLsAvailable( |
knn | 6cf777fc | 2015-05-06 13:21:48 | [diff] [blame] | 769 | env, observer_.obj(), ToJavaArrayOfStrings(env, titles).obj(), |
[email protected] | bc093e8 | 2014-03-04 21:49:11 | [diff] [blame] | 770 | ToJavaArrayOfStrings(env, urls).obj()); |
| 771 | } |
| 772 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 773 | void MostVisitedSites::OnPopularSitesAvailable(bool success) { |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 774 | received_popular_sites_ = true; |
| 775 | |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 776 | if (!success) { |
| 777 | LOG(WARNING) << "Download of popular sites failed"; |
| 778 | return; |
| 779 | } |
| 780 | |
treib | 7f5cf87 | 2015-08-11 13:11:47 | [diff] [blame] | 781 | if (observer_.is_null()) |
| 782 | return; |
| 783 | |
| 784 | std::vector<std::string> urls; |
| 785 | std::vector<std::string> favicon_urls; |
knn | 7d819222 | 2015-10-12 20:37:59 | [diff] [blame] | 786 | std::vector<std::string> large_icon_urls; |
treib | 7f5cf87 | 2015-08-11 13:11:47 | [diff] [blame] | 787 | for (const PopularSites::Site& popular_site : popular_sites_->sites()) { |
| 788 | urls.push_back(popular_site.url.spec()); |
| 789 | favicon_urls.push_back(popular_site.favicon_url.spec()); |
knn | 7d819222 | 2015-10-12 20:37:59 | [diff] [blame] | 790 | large_icon_urls.push_back(popular_site.large_icon_url.spec()); |
treib | 7f5cf87 | 2015-08-11 13:11:47 | [diff] [blame] | 791 | } |
| 792 | JNIEnv* env = AttachCurrentThread(); |
| 793 | Java_MostVisitedURLsObserver_onPopularURLsAvailable( |
| 794 | env, observer_.obj(), ToJavaArrayOfStrings(env, urls).obj(), |
knn | 7d819222 | 2015-10-12 20:37:59 | [diff] [blame] | 795 | ToJavaArrayOfStrings(env, favicon_urls).obj(), |
| 796 | ToJavaArrayOfStrings(env, large_icon_urls).obj()); |
treib | 7f5cf87 | 2015-08-11 13:11:47 | [diff] [blame] | 797 | QueryMostVisitedURLs(); |
treib | cffa650 | 2015-08-06 09:12:27 | [diff] [blame] | 798 | } |
| 799 | |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 800 | void MostVisitedSites::RecordImpressionUMAMetrics() { |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 801 | for (size_t i = 0; i < current_suggestions_.size(); i++) { |
| 802 | std::string histogram = base::StringPrintf( |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 803 | "NewTabPage.SuggestionsImpression.%s", |
knn | cbbd754d | 2015-09-09 15:43:40 | [diff] [blame] | 804 | current_suggestions_[i]->GetSourceHistogramName().c_str()); |
treib | 783a337 | 2015-08-25 16:24:39 | [diff] [blame] | 805 | LogHistogramEvent(histogram, static_cast<int>(i), num_sites_); |
| 806 | } |
| 807 | } |
| 808 | |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 809 | void MostVisitedSites::TopSitesLoaded(history::TopSites* top_sites) { |
| 810 | } |
| 811 | |
fserb | db57511 | 2015-06-29 21:31:59 | [diff] [blame] | 812 | void MostVisitedSites::TopSitesChanged(history::TopSites* top_sites, |
| 813 | ChangeReason change_reason) { |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 814 | if (mv_source_ == TOP_SITES) { |
| 815 | // The displayed suggestions are invalidated. |
newt | 2a47ce7 | 2015-10-01 20:09:49 | [diff] [blame] | 816 | InitiateTopSitesQuery(); |
sdefresne | edf9e01f | 2015-01-13 19:45:41 | [diff] [blame] | 817 | } |
| 818 | } |
| 819 | |
torne | 89cc5d9 | 2015-09-04 11:16:35 | [diff] [blame] | 820 | static jlong Init(JNIEnv* env, |
| 821 | const JavaParamRef<jobject>& obj, |
| 822 | const JavaParamRef<jobject>& jprofile) { |
[email protected] | 7ceb4e3 | 2013-12-06 04:13:04 | [diff] [blame] | 823 | MostVisitedSites* most_visited_sites = |
| 824 | new MostVisitedSites(ProfileAndroid::FromProfileAndroid(jprofile)); |
| 825 | return reinterpret_cast<intptr_t>(most_visited_sites); |
| 826 | } |