blob: 070595f7b315f66b7872163db1531ff663e34f44 [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);
Ken Rockotebba67882019-12-20 18:22:24193 parse_json_callback_.Run(last_response_string_,
194 base::BindOnce(&JsonRequest::OnJsonParsed,
195 weak_ptr_factory_.GetWeakPtr()),
196 base::BindOnce(&JsonRequest::OnJsonError,
197 weak_ptr_factory_.GetWeakPtr()));
fhorschigcb5d7fc02016-12-20 13:14:55198 }
199}
200
Sylvain Defresne16967242019-05-09 17:59:25201void JsonRequest::OnJsonParsed(base::Value result) {
fhorschigcb5d7fc02016-12-20 13:14:55202 std::move(request_completed_callback_)
203 .Run(std::move(result), FetchResult::SUCCESS,
204 /*error_details=*/std::string());
205}
206
treib9de525a2017-01-19 12:20:37207void JsonRequest::OnJsonError(const std::string& error) {
Mark Pilgrimda33faa2018-06-06 14:29:47208 LOG(WARNING) << "Received invalid JSON (" << error
209 << "): " << last_response_string_;
fhorschigcb5d7fc02016-12-20 13:14:55210 std::move(request_completed_callback_)
Sylvain Defresne16967242019-05-09 17:59:25211 .Run(/*result=*/base::Value(), FetchResult::JSON_PARSE_ERROR,
fhorschigcb5d7fc02016-12-20 13:14:55212 /*error_details=*/base::StringPrintf(" (error %s)", error.c_str()));
213}
214
Michael Martis98cd8732017-07-14 03:26:19215JsonRequest::Builder::Builder() : language_histogram_(nullptr) {}
treib9de525a2017-01-19 12:20:37216JsonRequest::Builder::Builder(JsonRequest::Builder&&) = default;
217JsonRequest::Builder::~Builder() = default;
fhorschigcb5d7fc02016-12-20 13:14:55218
treib9de525a2017-01-19 12:20:37219std::unique_ptr<JsonRequest> JsonRequest::Builder::Build() const {
fhorschigcb5d7fc02016-12-20 13:14:55220 DCHECK(!url_.is_empty());
Mark Pilgrimda33faa2018-06-06 14:29:47221 DCHECK(url_loader_factory_);
markusheintz05b1e882017-02-15 14:38:19222 DCHECK(clock_);
Jinho Bangedffb4ee2018-01-02 15:38:30223 auto request = std::make_unique<JsonRequest>(params_.exclusive_category,
markusheintz05b1e882017-02-15 14:38:19224 clock_, parse_json_callback_);
fhorschigcb5d7fc02016-12-20 13:14:55225 std::string body = BuildBody();
Mark Pilgrimda33faa2018-06-06 14:29:47226 request->simple_url_loader_ = BuildURLLoader(body);
227 request->url_loader_factory_ = std::move(url_loader_factory_);
fhorschigcb5d7fc02016-12-20 13:14:55228
229 return request;
230}
231
treib9de525a2017-01-19 12:20:37232JsonRequest::Builder& JsonRequest::Builder::SetAuthentication(
fhorschigcb5d7fc02016-12-20 13:14:55233 const std::string& auth_header) {
fhorschigcb5d7fc02016-12-20 13:14:55234 auth_header_ = auth_header;
235 return *this;
236}
237
Michael Martis98cd8732017-07-14 03:26:19238JsonRequest::Builder& JsonRequest::Builder::SetLanguageHistogram(
239 const language::UrlLanguageHistogram* language_histogram) {
240 language_histogram_ = language_histogram;
fhorschigcb5d7fc02016-12-20 13:14:55241 return *this;
242}
243
treib9de525a2017-01-19 12:20:37244JsonRequest::Builder& JsonRequest::Builder::SetParams(
245 const RequestParams& params) {
fhorschigcb5d7fc02016-12-20 13:14:55246 params_ = params;
247 return *this;
248}
249
treib9de525a2017-01-19 12:20:37250JsonRequest::Builder& JsonRequest::Builder::SetParseJsonCallback(
fhorschigcb5d7fc02016-12-20 13:14:55251 ParseJSONCallback callback) {
252 parse_json_callback_ = callback;
253 return *this;
254}
255
Hajime Hoshi7a5c8c82019-01-10 05:44:51256JsonRequest::Builder& JsonRequest::Builder::SetClock(const base::Clock* clock) {
markusheintz05b1e882017-02-15 14:38:19257 clock_ = clock;
fhorschigcb5d7fc02016-12-20 13:14:55258 return *this;
259}
260
treib9de525a2017-01-19 12:20:37261JsonRequest::Builder& JsonRequest::Builder::SetUrl(const GURL& url) {
fhorschigcb5d7fc02016-12-20 13:14:55262 url_ = url;
263 return *this;
264}
265
Mark Pilgrimda33faa2018-06-06 14:29:47266JsonRequest::Builder& JsonRequest::Builder::SetUrlLoaderFactory(
267 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
268 url_loader_factory_ = std::move(url_loader_factory);
fhorschigcb5d7fc02016-12-20 13:14:55269 return *this;
270}
271
treib9de525a2017-01-19 12:20:37272JsonRequest::Builder& JsonRequest::Builder::SetUserClassifier(
fhorschigcb5d7fc02016-12-20 13:14:55273 const UserClassifier& user_classifier) {
274 if (IsSendingUserClassEnabled()) {
275 user_class_ = GetUserClassString(user_classifier.GetUserClass());
276 }
277 return *this;
278}
279
Justin DeWitt7943cde2019-07-24 17:28:52280JsonRequest::Builder& JsonRequest::Builder::SetOptionalImagesCapability(
281 bool supports_optional_images) {
282 if (supports_optional_images && IsSendingOptionalImagesCapabilityEnabled()) {
283 display_capability_ = "CAPABILITY_OPTIONAL_IMAGES";
284 }
285 return *this;
286}
287
Mark Pilgrimda33faa2018-06-06 14:29:47288std::unique_ptr<network::ResourceRequest>
289JsonRequest::Builder::BuildResourceRequest() const {
290 auto resource_request = std::make_unique<network::ResourceRequest>();
291 resource_request->url = url_;
Yutaka Hirano3d804982019-07-29 06:41:34292 resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
Mark Pilgrimda33faa2018-06-06 14:29:47293 resource_request->method = "POST";
294 resource_request->headers.SetHeader("Content-Type",
295 "application/json; charset=UTF-8");
fhorschigcb5d7fc02016-12-20 13:14:55296 if (!auth_header_.empty()) {
Mark Pilgrimda33faa2018-06-06 14:29:47297 resource_request->headers.SetHeader("Authorization", auth_header_);
fhorschigcb5d7fc02016-12-20 13:14:55298 }
299 // Add X-Client-Data header with experiment IDs from field trials.
Wang Hui00fff442018-07-09 23:50:23300 // TODO: We should call AppendVariationHeaders with explicit
301 // variations::SignedIn::kNo If the auth_header_ is empty
Takashi Toyoshimaf3ceca92019-02-04 07:49:05302 variations::AppendVariationsHeaderUnknownSignedIn(
303 url_, variations::InIncognito::kNo, resource_request.get());
Mark Pilgrimda33faa2018-06-06 14:29:47304 return resource_request;
fhorschigcb5d7fc02016-12-20 13:14:55305}
306
treib9de525a2017-01-19 12:20:37307std::string JsonRequest::Builder::BuildBody() const {
Jinho Bangedffb4ee2018-01-02 15:38:30308 auto request = std::make_unique<base::DictionaryValue>();
fhorschigcb5d7fc02016-12-20 13:14:55309 std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code);
treiba57f51e2017-03-23 14:47:52310 if (!user_locale.empty()) {
311 request->SetString("uiLanguage", user_locale);
312 }
fhorschigcb5d7fc02016-12-20 13:14:55313
treiba57f51e2017-03-23 14:47:52314 request->SetString("priority", params_.interactive_request
315 ? "USER_ACTION"
316 : "BACKGROUND_PREFETCH");
fhorschigcb5d7fc02016-12-20 13:14:55317
Jinho Bangedffb4ee2018-01-02 15:38:30318 auto excluded = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52319 for (const auto& id : params_.excluded_ids) {
320 excluded->AppendString(id);
fhorschigcb5d7fc02016-12-20 13:14:55321 }
treiba57f51e2017-03-23 14:47:52322 request->Set("excludedSuggestionIds", std::move(excluded));
323
324 if (!user_class_.empty()) {
325 request->SetString("userActivenessClass", user_class_);
326 }
327
Justin DeWitt7943cde2019-07-24 17:28:52328 if (!display_capability_.empty()) {
329 request->SetString("displayCapability", display_capability_);
330 }
331
Michael Martis98cd8732017-07-14 03:26:19332 language::UrlLanguageHistogram::LanguageInfo ui_language;
333 language::UrlLanguageHistogram::LanguageInfo other_top_language;
treiba57f51e2017-03-23 14:47:52334 PrepareLanguages(&ui_language, &other_top_language);
335 if (ui_language.frequency != 0 || other_top_language.frequency != 0) {
Jinho Bangedffb4ee2018-01-02 15:38:30336 auto language_list = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52337 if (ui_language.frequency > 0) {
338 AppendLanguageInfoToList(language_list.get(), ui_language);
339 }
340 if (other_top_language.frequency > 0) {
341 AppendLanguageInfoToList(language_list.get(), other_top_language);
342 }
343 request->Set("topLanguages", std::move(language_list));
344 }
345
Vitalii Iarko3fdb9f912017-09-21 08:28:25346 // TODO(vitaliii): Support count_to_fetch without requiring
347 // |exclusive_category|.
348 if (params_.exclusive_category.has_value()) {
349 base::DictionaryValue exclusive_category_parameters;
350 exclusive_category_parameters.SetInteger(
351 "id", params_.exclusive_category->remote_id());
352 exclusive_category_parameters.SetInteger("numSuggestions",
353 params_.count_to_fetch);
354 base::ListValue category_parameters;
Jan Wilken Dörried322b292019-09-11 09:03:49355 category_parameters.Append(std::move(exclusive_category_parameters));
Vitalii Iarko3fdb9f912017-09-21 08:28:25356 request->SetKey("categoryParameters", std::move(category_parameters));
357 }
fhorschigcb5d7fc02016-12-20 13:14:55358
359 std::string request_json;
360 bool success = base::JSONWriter::WriteWithOptions(
361 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
362 DCHECK(success);
363 return request_json;
364}
365
Mark Pilgrimda33faa2018-06-06 14:29:47366std::unique_ptr<network::SimpleURLLoader> JsonRequest::Builder::BuildURLLoader(
fhorschigcb5d7fc02016-12-20 13:14:55367 const std::string& body) const {
rhalavatic0ea6f042017-02-27 15:58:06368 net::NetworkTrafficAnnotationTag traffic_annotation =
369 net::DefineNetworkTrafficAnnotation("ntp_snippets_fetch", R"(
370 semantics {
371 sender: "New Tab Page Content Suggestions Fetch"
372 description:
373 "Chromium can show content suggestions (e.g. news articles) on the "
374 "New Tab page. For signed-in users, these may be personalized "
375 "based on the user's synced browsing history."
376 trigger:
377 "Triggered periodically in the background, or upon explicit user "
378 "request."
379 data:
380 "The Chromium UI language, as well as a second language the user "
Michael Martis98cd8732017-07-14 03:26:19381 "understands, based on language::UrlLanguageHistogram. For "
382 "signed-in users, the requests is authenticated."
rhalavatic0ea6f042017-02-27 15:58:06383 destination: GOOGLE_OWNED_SERVICE
384 }
385 policy {
Ramin Halavati3b979782017-07-21 11:40:26386 cookies_allowed: NO
rhalavatic0ea6f042017-02-27 15:58:06387 setting:
388 "This feature cannot be disabled by settings now (but is requested "
389 "to be implemented in crbug.com/695129)."
rhalavatieaa64e92017-04-03 09:36:43390 chrome_policy {
rhalavatic0ea6f042017-02-27 15:58:06391 NTPContentSuggestionsEnabled {
392 policy_options {mode: MANDATORY}
rhalavatieaa64e92017-04-03 09:36:43393 NTPContentSuggestionsEnabled: false
rhalavatic0ea6f042017-02-27 15:58:06394 }
395 }
396 })");
Mark Pilgrimda33faa2018-06-06 14:29:47397 auto resource_request = BuildResourceRequest();
fhorschigcb5d7fc02016-12-20 13:14:55398
Mark Pilgrimda33faa2018-06-06 14:29:47399 // Log the request for debugging network issues.
400 VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
401 << resource_request->headers.ToString() << "\n"
402 << body;
fhorschigcb5d7fc02016-12-20 13:14:55403
Mark Pilgrimda33faa2018-06-06 14:29:47404 auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
405 traffic_annotation);
406 loader->AttachStringForUpload(body, "application/json");
407 int max_retries = JsonRequest::Get5xxRetryCount(params_.interactive_request);
408 if (max_retries > 0) {
409 loader->SetRetryOptions(
410 max_retries, network::SimpleURLLoader::RETRY_ON_5XX |
411 network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
412 }
413 return loader;
fhorschigcb5d7fc02016-12-20 13:14:55414}
415
treib9de525a2017-01-19 12:20:37416void JsonRequest::Builder::PrepareLanguages(
Michael Martis98cd8732017-07-14 03:26:19417 language::UrlLanguageHistogram::LanguageInfo* ui_language,
418 language::UrlLanguageHistogram::LanguageInfo* other_top_language) const {
fhorschigcb5d7fc02016-12-20 13:14:55419 // TODO(jkrcal): Add language model factory for iOS and add fakes to tests so
Michael Martis98cd8732017-07-14 03:26:19420 // that |language_histogram| is never nullptr. Remove this check and add a
421 // DCHECK into the constructor.
422 if (!language_histogram_ || !IsSendingTopLanguagesEnabled()) {
fhorschigcb5d7fc02016-12-20 13:14:55423 return;
424 }
425
426 // TODO(jkrcal): Is this back-and-forth converting necessary?
427 ui_language->language_code = ISO639FromPosixLocale(
428 PosixLocaleFromBCP47Language(params_.language_code));
429 ui_language->frequency =
Michael Martis98cd8732017-07-14 03:26:19430 language_histogram_->GetLanguageFrequency(ui_language->language_code);
fhorschigcb5d7fc02016-12-20 13:14:55431
Michael Martis98cd8732017-07-14 03:26:19432 std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
433 language_histogram_->GetTopLanguages();
434 for (const UrlLanguageHistogram::LanguageInfo& info : top_languages) {
fhorschigcb5d7fc02016-12-20 13:14:55435 if (info.language_code != ui_language->language_code) {
436 *other_top_language = info;
437
438 // Report to UMA how important the UI language is.
439 DCHECK_GT(other_top_language->frequency, 0)
440 << "GetTopLanguages() should not return languages with 0 frequency";
441 float ratio_ui_in_both_languages =
442 ui_language->frequency /
443 (ui_language->frequency + other_top_language->frequency);
444 UMA_HISTOGRAM_PERCENTAGE(
445 "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages",
446 ratio_ui_in_both_languages * 100);
447 break;
448 }
449 }
450}
451
452} // namespace internal
453
454} // namespace ntp_snippets