blob: 76b2325e7d6ceaab2aaf4803f64a92a38e6d8dcd [file] [log] [blame]
fhorschigcb5d7fc02016-12-20 13:14:551// Copyright 2016 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
treib9de525a2017-01-19 12:20:375#include "components/ntp_snippets/remote/json_request.h"
fhorschigcb5d7fc02016-12-20 13:14:556
7#include <algorithm>
8#include <utility>
9#include <vector>
10
Sebastien Marchand53801a32019-01-25 16:26:1111#include "base/bind.h"
fhorschigcb5d7fc02016-12-20 13:14:5512#include "base/command_line.h"
Justin DeWitt7943cde2019-07-24 17:28:5213#include "base/feature_list.h"
fhorschigcb5d7fc02016-12-20 13:14:5514#include "base/json/json_writer.h"
Ilya Sherman1edb6f182017-12-12 04:00:4215#include "base/metrics/histogram_functions.h"
fhorschigcb5d7fc02016-12-20 13:14:5516#include "base/metrics/histogram_macros.h"
fhorschigcb5d7fc02016-12-20 13:14:5517#include "base/strings/stringprintf.h"
markusheintz05b1e882017-02-15 14:38:1918#include "base/time/clock.h"
fhorschigcb5d7fc02016-12-20 13:14:5519#include "base/values.h"
fhorschigcb5d7fc02016-12-20 13:14:5520#include "components/ntp_snippets/category_info.h"
21#include "components/ntp_snippets/features.h"
treib9de525a2017-01-19 12:20:3722#include "components/ntp_snippets/remote/request_params.h"
fhorschigcb5d7fc02016-12-20 13:14:5523#include "components/ntp_snippets/user_classifier.h"
thakisfe8fa0a2017-02-23 19:46:3624#include "components/strings/grit/components_strings.h"
fhorschigcb5d7fc02016-12-20 13:14:5525#include "components/variations/net/variations_http_headers.h"
26#include "components/variations/variations_associated_data.h"
fhorschigcb5d7fc02016-12-20 13:14:5527#include "net/base/load_flags.h"
28#include "net/http/http_response_headers.h"
29#include "net/http/http_status_code.h"
rhalavatic0ea6f042017-02-27 15:58:0630#include "net/traffic_annotation/network_traffic_annotation.h"
Mark Pilgrimda33faa2018-06-06 14:29:4731#include "services/network/public/cpp/resource_request.h"
32#include "services/network/public/cpp/shared_url_loader_factory.h"
33#include "services/network/public/cpp/simple_url_loader.h"
fhorschigcb5d7fc02016-12-20 13:14:5534#include "third_party/icu/source/common/unicode/uloc.h"
35#include "third_party/icu/source/common/unicode/utypes.h"
36#include "ui/base/l10n/l10n_util.h"
37
Michael Martis98cd8732017-07-14 03:26:1938using language::UrlLanguageHistogram;
fhorschigcb5d7fc02016-12-20 13:14:5539
40namespace ntp_snippets {
41
42namespace internal {
43
44namespace {
45
46// Variation parameter for disabling the retry.
47const char kBackground5xxRetriesName[] = "background_5xx_retries_count";
48
Michael Martis98cd8732017-07-14 03:26:1949// Variation parameter for sending UrlLanguageHistogram info to the server.
fhorschigcb5d7fc02016-12-20 13:14:5550const char kSendTopLanguagesName[] = "send_top_languages";
51
52// Variation parameter for sending UserClassifier info to the server.
53const char kSendUserClassName[] = "send_user_class";
54
fhorschigcb5d7fc02016-12-20 13:14:5555bool IsSendingTopLanguagesEnabled() {
56 return variations::GetVariationParamByFeatureAsBool(
57 ntp_snippets::kArticleSuggestionsFeature, kSendTopLanguagesName,
jkrcald1625e22017-01-18 16:54:3858 /*default_value=*/true);
fhorschigcb5d7fc02016-12-20 13:14:5559}
60
61bool IsSendingUserClassEnabled() {
62 return variations::GetVariationParamByFeatureAsBool(
63 ntp_snippets::kArticleSuggestionsFeature, kSendUserClassName,
Tim Schumann7e4602982017-08-28 13:34:5364 /*default_value=*/true);
fhorschigcb5d7fc02016-12-20 13:14:5565}
66
Justin DeWitt7943cde2019-07-24 17:28:5267bool IsSendingOptionalImagesCapabilityEnabled() {
68 return base::FeatureList::IsEnabled(
69 ntp_snippets::kOptionalImagesEnabledFeature);
70}
71
fhorschigcb5d7fc02016-12-20 13:14:5572// Translate the BCP 47 |language_code| into a posix locale string.
73std::string PosixLocaleFromBCP47Language(const std::string& language_code) {
74 char locale[ULOC_FULLNAME_CAPACITY];
75 UErrorCode error = U_ZERO_ERROR;
76 // Translate the input to a posix locale.
77 uloc_forLanguageTag(language_code.c_str(), locale, ULOC_FULLNAME_CAPACITY,
78 nullptr, &error);
79 if (error != U_ZERO_ERROR) {
80 DLOG(WARNING) << "Error in translating language code to a locale string: "
81 << error;
82 return std::string();
83 }
84 return locale;
85}
86
87std::string ISO639FromPosixLocale(const std::string& locale) {
88 char language[ULOC_LANG_CAPACITY];
89 UErrorCode error = U_ZERO_ERROR;
90 uloc_getLanguage(locale.c_str(), language, ULOC_LANG_CAPACITY, &error);
91 if (error != U_ZERO_ERROR) {
92 DLOG(WARNING)
93 << "Error in translating locale string to a ISO639 language code: "
94 << error;
95 return std::string();
96 }
97 return language;
98}
99
100void AppendLanguageInfoToList(base::ListValue* list,
Michael Martis98cd8732017-07-14 03:26:19101 const UrlLanguageHistogram::LanguageInfo& info) {
Jinho Bangedffb4ee2018-01-02 15:38:30102 auto lang = std::make_unique<base::DictionaryValue>();
fhorschigcb5d7fc02016-12-20 13:14:55103 lang->SetString("language", info.language_code);
104 lang->SetDouble("frequency", info.frequency);
105 list->Append(std::move(lang));
106}
107
108std::string GetUserClassString(UserClassifier::UserClass user_class) {
109 switch (user_class) {
110 case UserClassifier::UserClass::RARE_NTP_USER:
111 return "RARE_NTP_USER";
112 case UserClassifier::UserClass::ACTIVE_NTP_USER:
113 return "ACTIVE_NTP_USER";
114 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
115 return "ACTIVE_SUGGESTIONS_CONSUMER";
116 }
117 NOTREACHED();
118 return std::string();
119}
120
121} // namespace
122
treib9de525a2017-01-19 12:20:37123JsonRequest::JsonRequest(
fhorschigcb5d7fc02016-12-20 13:14:55124 base::Optional<Category> exclusive_category,
Hajime Hoshi7a5c8c82019-01-10 05:44:51125 const base::Clock* clock, // Needed until destruction of the request.
fhorschigcb5d7fc02016-12-20 13:14:55126 const ParseJSONCallback& callback)
127 : exclusive_category_(exclusive_category),
markusheintz05b1e882017-02-15 14:38:19128 clock_(clock),
Jeremy Roman5c341f6d2019-07-15 15:56:10129 parse_json_callback_(callback) {
markusheintz05b1e882017-02-15 14:38:19130 creation_time_ = clock_->Now();
fhorschigcb5d7fc02016-12-20 13:14:55131}
132
treib9de525a2017-01-19 12:20:37133JsonRequest::~JsonRequest() {
fhorschigcb5d7fc02016-12-20 13:14:55134 LOG_IF(DFATAL, !request_completed_callback_.is_null())
135 << "The CompletionCallback was never called!";
136}
137
treib9de525a2017-01-19 12:20:37138void JsonRequest::Start(CompletedCallback callback) {
Mark Pilgrimda33faa2018-06-06 14:29:47139 DCHECK(simple_url_loader_);
140 DCHECK(url_loader_factory_);
fhorschigcb5d7fc02016-12-20 13:14:55141 request_completed_callback_ = std::move(callback);
Mark Pilgrimda33faa2018-06-06 14:29:47142 last_response_string_.clear();
143 simple_url_loader_->SetAllowHttpErrorResults(true);
144 simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
145 url_loader_factory_.get(),
146 base::BindOnce(&JsonRequest::OnSimpleLoaderComplete,
147 base::Unretained(this)));
148}
149
150// static
151int JsonRequest::Get5xxRetryCount(bool interactive_request) {
152 if (interactive_request) {
153 return 2;
154 }
155 return std::max(0, variations::GetVariationParamByFeatureAsInt(
156 ntp_snippets::kArticleSuggestionsFeature,
157 kBackground5xxRetriesName, 0));
fhorschigcb5d7fc02016-12-20 13:14:55158}
159
treib9de525a2017-01-19 12:20:37160base::TimeDelta JsonRequest::GetFetchDuration() const {
markusheintz05b1e882017-02-15 14:38:19161 return clock_->Now() - creation_time_;
fhorschigcb5d7fc02016-12-20 13:14:55162}
163
treib9de525a2017-01-19 12:20:37164std::string JsonRequest::GetResponseString() const {
Mark Pilgrimda33faa2018-06-06 14:29:47165 return last_response_string_;
fhorschigcb5d7fc02016-12-20 13:14:55166}
167
Mark Pilgrimda33faa2018-06-06 14:29:47168void JsonRequest::OnSimpleLoaderComplete(
169 std::unique_ptr<std::string> response_body) {
170 int net_error = simple_url_loader_->NetError();
171 int response_code = -1;
172 if (simple_url_loader_->ResponseInfo() &&
173 simple_url_loader_->ResponseInfo()->headers) {
174 response_code =
175 simple_url_loader_->ResponseInfo()->headers->response_code();
176 }
177 simple_url_loader_.reset();
Ilya Sherman1edb6f182017-12-12 04:00:42178 base::UmaHistogramSparse("NewTabPage.Snippets.FetchHttpResponseOrErrorCode",
Mark Pilgrimda33faa2018-06-06 14:29:47179 net_error == net::OK ? response_code : net_error);
180 if (net_error != net::OK) {
fhorschigcb5d7fc02016-12-20 13:14:55181 std::move(request_completed_callback_)
Sylvain Defresne16967242019-05-09 17:59:25182 .Run(/*result=*/base::Value(), FetchResult::URL_REQUEST_STATUS_ERROR,
Mark Pilgrimda33faa2018-06-06 14:29:47183 /*error_details=*/base::StringPrintf(" %d", net_error));
184 } else if (response_code / 100 != 2) {
Patrick Nolandbbc62fc2018-07-11 18:34:38185 FetchResult result = response_code == net::HTTP_UNAUTHORIZED
186 ? FetchResult::HTTP_ERROR_UNAUTHORIZED
187 : FetchResult::HTTP_ERROR;
fhorschigcb5d7fc02016-12-20 13:14:55188 std::move(request_completed_callback_)
Sylvain Defresne16967242019-05-09 17:59:25189 .Run(/*result=*/base::Value(), result,
Mark Pilgrimda33faa2018-06-06 14:29:47190 /*error_details=*/base::StringPrintf(" %d", response_code));
fhorschigcb5d7fc02016-12-20 13:14:55191 } else {
Mark Pilgrimda33faa2018-06-06 14:29:47192 last_response_string_ = std::move(*response_body);
193 parse_json_callback_.Run(
194 last_response_string_,
195 base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
196 base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr()));
fhorschigcb5d7fc02016-12-20 13:14:55197 }
198}
199
Sylvain Defresne16967242019-05-09 17:59:25200void JsonRequest::OnJsonParsed(base::Value result) {
fhorschigcb5d7fc02016-12-20 13:14:55201 std::move(request_completed_callback_)
202 .Run(std::move(result), FetchResult::SUCCESS,
203 /*error_details=*/std::string());
204}
205
treib9de525a2017-01-19 12:20:37206void JsonRequest::OnJsonError(const std::string& error) {
Mark Pilgrimda33faa2018-06-06 14:29:47207 LOG(WARNING) << "Received invalid JSON (" << error
208 << "): " << last_response_string_;
fhorschigcb5d7fc02016-12-20 13:14:55209 std::move(request_completed_callback_)
Sylvain Defresne16967242019-05-09 17:59:25210 .Run(/*result=*/base::Value(), FetchResult::JSON_PARSE_ERROR,
fhorschigcb5d7fc02016-12-20 13:14:55211 /*error_details=*/base::StringPrintf(" (error %s)", error.c_str()));
212}
213
Michael Martis98cd8732017-07-14 03:26:19214JsonRequest::Builder::Builder() : language_histogram_(nullptr) {}
treib9de525a2017-01-19 12:20:37215JsonRequest::Builder::Builder(JsonRequest::Builder&&) = default;
216JsonRequest::Builder::~Builder() = default;
fhorschigcb5d7fc02016-12-20 13:14:55217
treib9de525a2017-01-19 12:20:37218std::unique_ptr<JsonRequest> JsonRequest::Builder::Build() const {
fhorschigcb5d7fc02016-12-20 13:14:55219 DCHECK(!url_.is_empty());
Mark Pilgrimda33faa2018-06-06 14:29:47220 DCHECK(url_loader_factory_);
markusheintz05b1e882017-02-15 14:38:19221 DCHECK(clock_);
Jinho Bangedffb4ee2018-01-02 15:38:30222 auto request = std::make_unique<JsonRequest>(params_.exclusive_category,
markusheintz05b1e882017-02-15 14:38:19223 clock_, parse_json_callback_);
fhorschigcb5d7fc02016-12-20 13:14:55224 std::string body = BuildBody();
Mark Pilgrimda33faa2018-06-06 14:29:47225 request->simple_url_loader_ = BuildURLLoader(body);
226 request->url_loader_factory_ = std::move(url_loader_factory_);
fhorschigcb5d7fc02016-12-20 13:14:55227
228 return request;
229}
230
treib9de525a2017-01-19 12:20:37231JsonRequest::Builder& JsonRequest::Builder::SetAuthentication(
fhorschigcb5d7fc02016-12-20 13:14:55232 const std::string& account_id,
233 const std::string& auth_header) {
234 obfuscated_gaia_id_ = account_id;
235 auth_header_ = auth_header;
236 return *this;
237}
238
Michael Martis98cd8732017-07-14 03:26:19239JsonRequest::Builder& JsonRequest::Builder::SetLanguageHistogram(
240 const language::UrlLanguageHistogram* language_histogram) {
241 language_histogram_ = language_histogram;
fhorschigcb5d7fc02016-12-20 13:14:55242 return *this;
243}
244
treib9de525a2017-01-19 12:20:37245JsonRequest::Builder& JsonRequest::Builder::SetParams(
246 const RequestParams& params) {
fhorschigcb5d7fc02016-12-20 13:14:55247 params_ = params;
248 return *this;
249}
250
treib9de525a2017-01-19 12:20:37251JsonRequest::Builder& JsonRequest::Builder::SetParseJsonCallback(
fhorschigcb5d7fc02016-12-20 13:14:55252 ParseJSONCallback callback) {
253 parse_json_callback_ = callback;
254 return *this;
255}
256
Hajime Hoshi7a5c8c82019-01-10 05:44:51257JsonRequest::Builder& JsonRequest::Builder::SetClock(const base::Clock* clock) {
markusheintz05b1e882017-02-15 14:38:19258 clock_ = clock;
fhorschigcb5d7fc02016-12-20 13:14:55259 return *this;
260}
261
treib9de525a2017-01-19 12:20:37262JsonRequest::Builder& JsonRequest::Builder::SetUrl(const GURL& url) {
fhorschigcb5d7fc02016-12-20 13:14:55263 url_ = url;
264 return *this;
265}
266
Mark Pilgrimda33faa2018-06-06 14:29:47267JsonRequest::Builder& JsonRequest::Builder::SetUrlLoaderFactory(
268 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
269 url_loader_factory_ = std::move(url_loader_factory);
fhorschigcb5d7fc02016-12-20 13:14:55270 return *this;
271}
272
treib9de525a2017-01-19 12:20:37273JsonRequest::Builder& JsonRequest::Builder::SetUserClassifier(
fhorschigcb5d7fc02016-12-20 13:14:55274 const UserClassifier& user_classifier) {
275 if (IsSendingUserClassEnabled()) {
276 user_class_ = GetUserClassString(user_classifier.GetUserClass());
277 }
278 return *this;
279}
280
Justin DeWitt7943cde2019-07-24 17:28:52281JsonRequest::Builder& JsonRequest::Builder::SetOptionalImagesCapability(
282 bool supports_optional_images) {
283 if (supports_optional_images && IsSendingOptionalImagesCapabilityEnabled()) {
284 display_capability_ = "CAPABILITY_OPTIONAL_IMAGES";
285 }
286 return *this;
287}
288
Mark Pilgrimda33faa2018-06-06 14:29:47289std::unique_ptr<network::ResourceRequest>
290JsonRequest::Builder::BuildResourceRequest() const {
291 auto resource_request = std::make_unique<network::ResourceRequest>();
292 resource_request->url = url_;
Jesse Selover591e0412019-05-15 22:19:58293 resource_request->allow_credentials = false;
Mark Pilgrimda33faa2018-06-06 14:29:47294 resource_request->method = "POST";
295 resource_request->headers.SetHeader("Content-Type",
296 "application/json; charset=UTF-8");
fhorschigcb5d7fc02016-12-20 13:14:55297 if (!auth_header_.empty()) {
Mark Pilgrimda33faa2018-06-06 14:29:47298 resource_request->headers.SetHeader("Authorization", auth_header_);
fhorschigcb5d7fc02016-12-20 13:14:55299 }
300 // Add X-Client-Data header with experiment IDs from field trials.
Wang Hui00fff442018-07-09 23:50:23301 // TODO: We should call AppendVariationHeaders with explicit
302 // variations::SignedIn::kNo If the auth_header_ is empty
Takashi Toyoshimaf3ceca92019-02-04 07:49:05303 variations::AppendVariationsHeaderUnknownSignedIn(
304 url_, variations::InIncognito::kNo, resource_request.get());
Mark Pilgrimda33faa2018-06-06 14:29:47305 return resource_request;
fhorschigcb5d7fc02016-12-20 13:14:55306}
307
treib9de525a2017-01-19 12:20:37308std::string JsonRequest::Builder::BuildBody() const {
Jinho Bangedffb4ee2018-01-02 15:38:30309 auto request = std::make_unique<base::DictionaryValue>();
fhorschigcb5d7fc02016-12-20 13:14:55310 std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code);
treiba57f51e2017-03-23 14:47:52311 if (!user_locale.empty()) {
312 request->SetString("uiLanguage", user_locale);
313 }
fhorschigcb5d7fc02016-12-20 13:14:55314
treiba57f51e2017-03-23 14:47:52315 request->SetString("priority", params_.interactive_request
316 ? "USER_ACTION"
317 : "BACKGROUND_PREFETCH");
fhorschigcb5d7fc02016-12-20 13:14:55318
Jinho Bangedffb4ee2018-01-02 15:38:30319 auto excluded = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52320 for (const auto& id : params_.excluded_ids) {
321 excluded->AppendString(id);
fhorschigcb5d7fc02016-12-20 13:14:55322 }
treiba57f51e2017-03-23 14:47:52323 request->Set("excludedSuggestionIds", std::move(excluded));
324
325 if (!user_class_.empty()) {
326 request->SetString("userActivenessClass", user_class_);
327 }
328
Justin DeWitt7943cde2019-07-24 17:28:52329 if (!display_capability_.empty()) {
330 request->SetString("displayCapability", display_capability_);
331 }
332
Michael Martis98cd8732017-07-14 03:26:19333 language::UrlLanguageHistogram::LanguageInfo ui_language;
334 language::UrlLanguageHistogram::LanguageInfo other_top_language;
treiba57f51e2017-03-23 14:47:52335 PrepareLanguages(&ui_language, &other_top_language);
336 if (ui_language.frequency != 0 || other_top_language.frequency != 0) {
Jinho Bangedffb4ee2018-01-02 15:38:30337 auto language_list = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52338 if (ui_language.frequency > 0) {
339 AppendLanguageInfoToList(language_list.get(), ui_language);
340 }
341 if (other_top_language.frequency > 0) {
342 AppendLanguageInfoToList(language_list.get(), other_top_language);
343 }
344 request->Set("topLanguages", std::move(language_list));
345 }
346
Vitalii Iarko3fdb9f912017-09-21 08:28:25347 // TODO(vitaliii): Support count_to_fetch without requiring
348 // |exclusive_category|.
349 if (params_.exclusive_category.has_value()) {
350 base::DictionaryValue exclusive_category_parameters;
351 exclusive_category_parameters.SetInteger(
352 "id", params_.exclusive_category->remote_id());
353 exclusive_category_parameters.SetInteger("numSuggestions",
354 params_.count_to_fetch);
355 base::ListValue category_parameters;
356 category_parameters.GetList().push_back(
357 std::move(exclusive_category_parameters));
358 request->SetKey("categoryParameters", std::move(category_parameters));
359 }
fhorschigcb5d7fc02016-12-20 13:14:55360
361 std::string request_json;
362 bool success = base::JSONWriter::WriteWithOptions(
363 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
364 DCHECK(success);
365 return request_json;
366}
367
Mark Pilgrimda33faa2018-06-06 14:29:47368std::unique_ptr<network::SimpleURLLoader> JsonRequest::Builder::BuildURLLoader(
fhorschigcb5d7fc02016-12-20 13:14:55369 const std::string& body) const {
rhalavatic0ea6f042017-02-27 15:58:06370 net::NetworkTrafficAnnotationTag traffic_annotation =
371 net::DefineNetworkTrafficAnnotation("ntp_snippets_fetch", R"(
372 semantics {
373 sender: "New Tab Page Content Suggestions Fetch"
374 description:
375 "Chromium can show content suggestions (e.g. news articles) on the "
376 "New Tab page. For signed-in users, these may be personalized "
377 "based on the user's synced browsing history."
378 trigger:
379 "Triggered periodically in the background, or upon explicit user "
380 "request."
381 data:
382 "The Chromium UI language, as well as a second language the user "
Michael Martis98cd8732017-07-14 03:26:19383 "understands, based on language::UrlLanguageHistogram. For "
384 "signed-in users, the requests is authenticated."
rhalavatic0ea6f042017-02-27 15:58:06385 destination: GOOGLE_OWNED_SERVICE
386 }
387 policy {
Ramin Halavati3b979782017-07-21 11:40:26388 cookies_allowed: NO
rhalavatic0ea6f042017-02-27 15:58:06389 setting:
390 "This feature cannot be disabled by settings now (but is requested "
391 "to be implemented in crbug.com/695129)."
rhalavatieaa64e92017-04-03 09:36:43392 chrome_policy {
rhalavatic0ea6f042017-02-27 15:58:06393 NTPContentSuggestionsEnabled {
394 policy_options {mode: MANDATORY}
rhalavatieaa64e92017-04-03 09:36:43395 NTPContentSuggestionsEnabled: false
rhalavatic0ea6f042017-02-27 15:58:06396 }
397 }
398 })");
Mark Pilgrimda33faa2018-06-06 14:29:47399 auto resource_request = BuildResourceRequest();
fhorschigcb5d7fc02016-12-20 13:14:55400
Mark Pilgrimda33faa2018-06-06 14:29:47401 // Log the request for debugging network issues.
402 VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
403 << resource_request->headers.ToString() << "\n"
404 << body;
fhorschigcb5d7fc02016-12-20 13:14:55405
Mark Pilgrimda33faa2018-06-06 14:29:47406 auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
407 traffic_annotation);
408 loader->AttachStringForUpload(body, "application/json");
409 int max_retries = JsonRequest::Get5xxRetryCount(params_.interactive_request);
410 if (max_retries > 0) {
411 loader->SetRetryOptions(
412 max_retries, network::SimpleURLLoader::RETRY_ON_5XX |
413 network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
414 }
415 return loader;
fhorschigcb5d7fc02016-12-20 13:14:55416}
417
treib9de525a2017-01-19 12:20:37418void JsonRequest::Builder::PrepareLanguages(
Michael Martis98cd8732017-07-14 03:26:19419 language::UrlLanguageHistogram::LanguageInfo* ui_language,
420 language::UrlLanguageHistogram::LanguageInfo* other_top_language) const {
fhorschigcb5d7fc02016-12-20 13:14:55421 // TODO(jkrcal): Add language model factory for iOS and add fakes to tests so
Michael Martis98cd8732017-07-14 03:26:19422 // that |language_histogram| is never nullptr. Remove this check and add a
423 // DCHECK into the constructor.
424 if (!language_histogram_ || !IsSendingTopLanguagesEnabled()) {
fhorschigcb5d7fc02016-12-20 13:14:55425 return;
426 }
427
428 // TODO(jkrcal): Is this back-and-forth converting necessary?
429 ui_language->language_code = ISO639FromPosixLocale(
430 PosixLocaleFromBCP47Language(params_.language_code));
431 ui_language->frequency =
Michael Martis98cd8732017-07-14 03:26:19432 language_histogram_->GetLanguageFrequency(ui_language->language_code);
fhorschigcb5d7fc02016-12-20 13:14:55433
Michael Martis98cd8732017-07-14 03:26:19434 std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
435 language_histogram_->GetTopLanguages();
436 for (const UrlLanguageHistogram::LanguageInfo& info : top_languages) {
fhorschigcb5d7fc02016-12-20 13:14:55437 if (info.language_code != ui_language->language_code) {
438 *other_top_language = info;
439
440 // Report to UMA how important the UI language is.
441 DCHECK_GT(other_top_language->frequency, 0)
442 << "GetTopLanguages() should not return languages with 0 frequency";
443 float ratio_ui_in_both_languages =
444 ui_language->frequency /
445 (ui_language->frequency + other_top_language->frequency);
446 UMA_HISTOGRAM_PERCENTAGE(
447 "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages",
448 ratio_ui_in_both_languages * 100);
449 break;
450 }
451 }
452}
453
454} // namespace internal
455
456} // namespace ntp_snippets