Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: chrome/browser/android/ntp/popular_sites.cc

Issue 1983063002: Move classes to //components/ntp_tiles. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/ntp/popular_sites.h" 5 #include "chrome/browser/android/ntp/popular_sites.h"
6 6
7 #include <stddef.h>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/important_file_writer.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_writer.h"
16 #include "base/path_service.h" 7 #include "base/path_service.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/task_runner_util.h"
20 #include "base/time/time.h"
21 #include "base/values.h"
22 #include "chrome/common/chrome_paths.h" 8 #include "chrome/common/chrome_paths.h"
23 #include "components/google/core/browser/google_util.h"
24 #include "components/ntp_tiles/pref_names.h"
25 #include "components/ntp_tiles/switches.h"
26 #include "components/pref_registry/pref_registry_syncable.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/search_engines/search_engine_type.h"
29 #include "components/search_engines/template_url_prepopulate_data.h"
30 #include "components/search_engines/template_url_service.h"
31 #include "components/variations/service/variations_service.h"
32 #include "net/base/load_flags.h"
33 #include "net/http/http_status_code.h"
34
35 #if !defined(OS_IOS)
36 #include "components/safe_json/safe_json_parser.h"
37 #endif
38
39 using net::URLFetcher;
40 using variations::VariationsService;
41
42 namespace {
43
44 const char kPopularSitesURLFormat[] =
45 "https://www.gstatic.com/chrome/ntp/suggested_sites_%s_%s.json";
46 const char kPopularSitesDefaultCountryCode[] = "DEFAULT";
47 const char kPopularSitesDefaultVersion[] = "5";
48 const char kPopularSitesLocalFilename[] = "suggested_sites.json";
49 const int kPopularSitesRedownloadIntervalHours = 24;
50
51 const char kPopularSitesLastDownloadPref[] = "popular_sites_last_download";
52 const char kPopularSitesCountryPref[] = "popular_sites_country";
53 const char kPopularSitesVersionPref[] = "popular_sites_version";
54
55 // Extract the country from the default search engine if the default search
56 // engine is Google.
57 std::string GetDefaultSearchEngineCountryCode(
58 const TemplateURLService* template_url_service) {
59 DCHECK(template_url_service);
60
61 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
62 if (!cmd_line->HasSwitch(
63 ntp_tiles::switches::kEnableNTPSearchEngineCountryDetection))
64 return std::string();
65
66 const TemplateURL* default_provider =
67 template_url_service->GetDefaultSearchProvider();
68 // It's possible to not have a default provider in the case that the default
69 // search engine is defined by policy.
70 if (default_provider) {
71 bool is_google_search_engine =
72 TemplateURLPrepopulateData::GetEngineType(
73 *default_provider, template_url_service->search_terms_data()) ==
74 SearchEngineType::SEARCH_ENGINE_GOOGLE;
75
76 if (is_google_search_engine) {
77 GURL search_url = default_provider->GenerateSearchURL(
78 template_url_service->search_terms_data());
79 return google_util::GetGoogleCountryCode(search_url);
80 }
81 }
82
83 return std::string();
84 }
85
86 // Determine the country code to use. In order of precedence:
87 // - The explicit "override country" pref set by the user.
88 // - The country code from the field trial config (variation parameter).
89 // - The Google country code if Google is the default search engine (and the
90 // "--enable-ntp-search-engine-country-detection" switch is present).
91 // - The country provided by the VariationsService.
92 // - A default fallback.
93 std::string GetCountryToUse(const PrefService* prefs,
94 const TemplateURLService* template_url_service,
95 VariationsService* variations_service,
96 const std::string& variation_param_country) {
97 std::string country_code =
98 prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideCountry);
99
100 if (country_code.empty())
101 country_code = variation_param_country;
102
103 if (country_code.empty())
104 country_code = GetDefaultSearchEngineCountryCode(template_url_service);
105
106 if (country_code.empty() && variations_service)
107 country_code = variations_service->GetStoredPermanentCountry();
108
109 if (country_code.empty())
110 country_code = kPopularSitesDefaultCountryCode;
111
112 return base::ToUpperASCII(country_code);
113 }
114
115 // Determine the version to use. In order of precedence:
116 // - The explicit "override version" pref set by the user.
117 // - The version from the field trial config (variation parameter).
118 // - A default fallback.
119 std::string GetVersionToUse(const PrefService* prefs,
120 const std::string& variation_param_version) {
121 std::string version =
122 prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideVersion);
123
124 if (version.empty())
125 version = variation_param_version;
126
127 if (version.empty())
128 version = kPopularSitesDefaultVersion;
129
130 return version;
131 }
132
133 // Must run on the blocking thread pool.
134 bool WriteJsonToFile(const base::FilePath& local_path,
135 const base::Value* json) {
136 std::string json_string;
137 return base::JSONWriter::Write(*json, &json_string) &&
138 base::ImportantFileWriter::WriteFileAtomically(local_path,
139 json_string);
140 }
141
142 #if defined(OS_IOS)
143 // Mimics SafeJsonParser API, but parses unsafely for iOS.
144 class JsonUnsafeParser {
145 public:
146 using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
147 using ErrorCallback = base::Callback<void(const std::string&)>;
148
149 // As with SafeJsonParser, runs either success_callback or error_callback on
150 // the calling thread, but not before the call returns.
151 static void Parse(const std::string& unsafe_json,
152 const SuccessCallback& success_callback,
153 const ErrorCallback& error_callback) {
154 base::ThreadTaskRunnerHandle::Get()->PostTask(
155 FROM_HERE,
156 base::Bind(DoParse, unsafe_json, success_callback, error_callback));
157 }
158
159 JsonUnsafeParser() = delete;
160
161 private:
162 static void DoParse(const std::string& unsafe_json,
163 const SuccessCallback& success_callback,
164 const ErrorCallback& error_callback) {
165 std::string error_msg;
166 int error_line, error_column;
167 std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
168 unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error_msg,
169 &error_line, &error_column);
170 if (value) {
171 success_callback.Run(std::move(value));
172 } else {
173 error_callback.Run(base::StringPrintf("%s (%d:%d)", error_msg.c_str(),
174 error_line, error_column));
175 }
176 }
177 };
178
179 using UntrustedJsonParser = JsonUnsafeParser;
180 #else
181 using UntrustedJsonParser = safe_json::SafeJsonParser;
182 #endif
183
184 } // namespace
185 9
186 base::FilePath ChromePopularSites::GetDirectory() { 10 base::FilePath ChromePopularSites::GetDirectory() {
187 base::FilePath dir; 11 base::FilePath dir;
188 PathService::Get(chrome::DIR_USER_DATA, &dir); 12 PathService::Get(chrome::DIR_USER_DATA, &dir);
189 return dir; // empty if PathService::Get() failed. 13 return dir; // empty if PathService::Get() failed.
190 } 14 }
191
192 PopularSites::Site::Site(const base::string16& title,
193 const GURL& url,
194 const GURL& favicon_url,
195 const GURL& large_icon_url,
196 const GURL& thumbnail_url)
197 : title(title),
198 url(url),
199 favicon_url(favicon_url),
200 large_icon_url(large_icon_url),
201 thumbnail_url(thumbnail_url) {}
202
203 PopularSites::Site::Site(const Site& other) = default;
204
205 PopularSites::Site::~Site() {}
206
207 PopularSites::PopularSites(
208 const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
209 PrefService* prefs,
210 const TemplateURLService* template_url_service,
211 VariationsService* variations_service,
212 net::URLRequestContextGetter* download_context,
213 const base::FilePath& directory,
214 const std::string& variation_param_country,
215 const std::string& variation_param_version,
216 bool force_download,
217 const FinishedCallback& callback)
218 : PopularSites(blocking_pool,
219 prefs,
220 download_context,
221 directory,
222 GetCountryToUse(prefs,
223 template_url_service,
224 variations_service,
225 variation_param_country),
226 GetVersionToUse(prefs, variation_param_version),
227 GURL(),
228 force_download,
229 callback) {}
230
231 PopularSites::PopularSites(
232 const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
233 PrefService* prefs,
234 net::URLRequestContextGetter* download_context,
235 const base::FilePath& directory,
236 const GURL& url,
237 const FinishedCallback& callback)
238 : PopularSites(blocking_pool,
239 prefs,
240 download_context,
241 directory,
242 std::string(),
243 std::string(),
244 url,
245 true,
246 callback) {}
247
248 PopularSites::~PopularSites() {}
249
250 std::string PopularSites::GetCountry() const {
251 return prefs_->GetString(kPopularSitesCountryPref);
252 }
253
254 std::string PopularSites::GetVersion() const {
255 return prefs_->GetString(kPopularSitesVersionPref);
256 }
257
258 // static
259 void PopularSites::RegisterProfilePrefs(
260 user_prefs::PrefRegistrySyncable* user_prefs) {
261 user_prefs->RegisterStringPref(ntp_tiles::prefs::kPopularSitesOverrideCountry,
262 std::string());
263 user_prefs->RegisterStringPref(ntp_tiles::prefs::kPopularSitesOverrideVersion,
264 std::string());
265
266 user_prefs->RegisterInt64Pref(kPopularSitesLastDownloadPref, 0);
267 user_prefs->RegisterStringPref(kPopularSitesCountryPref, std::string());
268 user_prefs->RegisterStringPref(kPopularSitesVersionPref, std::string());
269 }
270
271 PopularSites::PopularSites(
272 const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
273 PrefService* prefs,
274 net::URLRequestContextGetter* download_context,
275 const base::FilePath& directory,
276 const std::string& country,
277 const std::string& version,
278 const GURL& override_url,
279 bool force_download,
280 const FinishedCallback& callback)
281 : callback_(callback),
282 is_fallback_(false),
283 pending_country_(country),
284 pending_version_(version),
285 local_path_(directory.empty()
286 ? base::FilePath()
287 : directory.AppendASCII(kPopularSitesLocalFilename)),
288 prefs_(prefs),
289 download_context_(download_context),
290 blocking_runner_(blocking_pool->GetTaskRunnerWithShutdownBehavior(
291 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)),
292 weak_ptr_factory_(this) {
293 const base::Time last_download_time = base::Time::FromInternalValue(
294 prefs_->GetInt64(kPopularSitesLastDownloadPref));
295 const base::TimeDelta time_since_last_download =
296 base::Time::Now() - last_download_time;
297 const base::TimeDelta redownload_interval =
298 base::TimeDelta::FromHours(kPopularSitesRedownloadIntervalHours);
299 const bool download_time_is_future = base::Time::Now() < last_download_time;
300 const bool country_changed = GetCountry() != pending_country_;
301 const bool version_changed = GetVersion() != pending_version_;
302
303 const GURL url =
304 override_url.is_valid() ? override_url : GetPopularSitesURL();
305
306 // No valid path to save to. Immediately post failure.
307 if (local_path_.empty()) {
308 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
309 base::Bind(callback_, false));
310 return;
311 }
312
313 // Download forced, or we need to download a new file.
314 if (force_download || download_time_is_future ||
315 (time_since_last_download > redownload_interval) || country_changed ||
316 version_changed) {
317 FetchPopularSites(url);
318 return;
319 }
320
321 std::unique_ptr<std::string> file_data(new std::string);
322 std::string* file_data_ptr = file_data.get();
323 base::PostTaskAndReplyWithResult(
324 blocking_runner_.get(), FROM_HERE,
325 base::Bind(&base::ReadFileToString, local_path_, file_data_ptr),
326 base::Bind(&PopularSites::OnReadFileDone, weak_ptr_factory_.GetWeakPtr(),
327 url, base::Passed(std::move(file_data))));
328 }
329
330 GURL PopularSites::GetPopularSitesURL() const {
331 return GURL(base::StringPrintf(kPopularSitesURLFormat,
332 pending_country_.c_str(),
333 pending_version_.c_str()));
334 }
335
336 void PopularSites::OnReadFileDone(const GURL& url,
337 std::unique_ptr<std::string> data,
338 bool success) {
339 if (success) {
340 auto json = base::JSONReader::Read(*data, base::JSON_ALLOW_TRAILING_COMMAS);
341 if (json) {
342 ParseSiteList(std::move(json));
343 } else {
344 OnJsonParseFailed("previously-fetched JSON was no longer parseable");
345 }
346 } else {
347 // File didn't exist, or couldn't be read for some other reason.
348 FetchPopularSites(url);
349 }
350 }
351
352 void PopularSites::FetchPopularSites(const GURL& url) {
353 fetcher_ = URLFetcher::Create(url, URLFetcher::GET, this);
354 fetcher_->SetRequestContext(download_context_);
355 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
356 net::LOAD_DO_NOT_SAVE_COOKIES);
357 fetcher_->SetAutomaticallyRetryOnNetworkChanges(1);
358 fetcher_->Start();
359 }
360
361 void PopularSites::OnURLFetchComplete(const net::URLFetcher* source) {
362 DCHECK_EQ(fetcher_.get(), source);
363 std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_);
364
365 std::string json_string;
366 if (!(source->GetStatus().is_success() &&
367 source->GetResponseCode() == net::HTTP_OK &&
368 source->GetResponseAsString(&json_string))) {
369 OnDownloadFailed();
370 return;
371 }
372
373 UntrustedJsonParser::Parse(
374 json_string,
375 base::Bind(&PopularSites::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
376 base::Bind(&PopularSites::OnJsonParseFailed,
377 weak_ptr_factory_.GetWeakPtr()));
378 }
379
380 void PopularSites::OnJsonParsed(std::unique_ptr<base::Value> json) {
381 const base::Value* json_ptr = json.get();
382 base::PostTaskAndReplyWithResult(
383 blocking_runner_.get(), FROM_HERE,
384 base::Bind(&WriteJsonToFile, local_path_, json_ptr),
385 base::Bind(&PopularSites::OnFileWriteDone, weak_ptr_factory_.GetWeakPtr(),
386 base::Passed(std::move(json))));
387 }
388
389 void PopularSites::OnJsonParseFailed(const std::string& error_message) {
390 DLOG(WARNING) << "JSON parsing failed: " << error_message;
391 OnDownloadFailed();
392 }
393
394 void PopularSites::OnFileWriteDone(std::unique_ptr<base::Value> json,
395 bool success) {
396 if (success) {
397 prefs_->SetInt64(kPopularSitesLastDownloadPref,
398 base::Time::Now().ToInternalValue());
399 prefs_->SetString(kPopularSitesCountryPref, pending_country_);
400 prefs_->SetString(kPopularSitesVersionPref, pending_version_);
401 ParseSiteList(std::move(json));
402 } else {
403 DLOG(WARNING) << "Could not write file to "
404 << local_path_.LossyDisplayName();
405 OnDownloadFailed();
406 }
407 }
408
409 void PopularSites::ParseSiteList(std::unique_ptr<base::Value> json) {
410 base::ListValue* list = nullptr;
411 if (!json || !json->GetAsList(&list)) {
412 DLOG(WARNING) << "JSON is not a list";
413 sites_.clear();
414 callback_.Run(false);
415 return;
416 }
417
418 std::vector<PopularSites::Site> sites;
419 for (size_t i = 0; i < list->GetSize(); i++) {
420 base::DictionaryValue* item;
421 if (!list->GetDictionary(i, &item))
422 continue;
423 base::string16 title;
424 std::string url;
425 if (!item->GetString("title", &title) || !item->GetString("url", &url))
426 continue;
427 std::string favicon_url;
428 item->GetString("favicon_url", &favicon_url);
429 std::string thumbnail_url;
430 item->GetString("thumbnail_url", &thumbnail_url);
431 std::string large_icon_url;
432 item->GetString("large_icon_url", &large_icon_url);
433
434 sites.push_back(PopularSites::Site(title, GURL(url), GURL(favicon_url),
435 GURL(large_icon_url),
436 GURL(thumbnail_url)));
437 }
438
439 sites_.swap(sites);
440 callback_.Run(true);
441 }
442
443 void PopularSites::OnDownloadFailed() {
444 if (!is_fallback_) {
445 DLOG(WARNING) << "Download country site list failed";
446 is_fallback_ = true;
447 pending_country_ = kPopularSitesDefaultCountryCode;
448 pending_version_ = kPopularSitesDefaultVersion;
449 FetchPopularSites(GetPopularSitesURL());
450 } else {
451 DLOG(WARNING) << "Download fallback site list failed";
452 callback_.Run(false);
453 }
454 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698