blob: 09059e13e0fd7adc89d8dfcc2967be30c33493dc [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& auth_header) {
fhorschigcb5d7fc02016-12-20 13:14:55233 auth_header_ = auth_header;
234 return *this;
235}
236
Michael Martis98cd8732017-07-14 03:26:19237JsonRequest::Builder& JsonRequest::Builder::SetLanguageHistogram(
238 const language::UrlLanguageHistogram* language_histogram) {
239 language_histogram_ = language_histogram;
fhorschigcb5d7fc02016-12-20 13:14:55240 return *this;
241}
242
treib9de525a2017-01-19 12:20:37243JsonRequest::Builder& JsonRequest::Builder::SetParams(
244 const RequestParams& params) {
fhorschigcb5d7fc02016-12-20 13:14:55245 params_ = params;
246 return *this;
247}
248
treib9de525a2017-01-19 12:20:37249JsonRequest::Builder& JsonRequest::Builder::SetParseJsonCallback(
fhorschigcb5d7fc02016-12-20 13:14:55250 ParseJSONCallback callback) {
251 parse_json_callback_ = callback;
252 return *this;
253}
254
Hajime Hoshi7a5c8c82019-01-10 05:44:51255JsonRequest::Builder& JsonRequest::Builder::SetClock(const base::Clock* clock) {
markusheintz05b1e882017-02-15 14:38:19256 clock_ = clock;
fhorschigcb5d7fc02016-12-20 13:14:55257 return *this;
258}
259
treib9de525a2017-01-19 12:20:37260JsonRequest::Builder& JsonRequest::Builder::SetUrl(const GURL& url) {
fhorschigcb5d7fc02016-12-20 13:14:55261 url_ = url;
262 return *this;
263}
264
Mark Pilgrimda33faa2018-06-06 14:29:47265JsonRequest::Builder& JsonRequest::Builder::SetUrlLoaderFactory(
266 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
267 url_loader_factory_ = std::move(url_loader_factory);
fhorschigcb5d7fc02016-12-20 13:14:55268 return *this;
269}
270
treib9de525a2017-01-19 12:20:37271JsonRequest::Builder& JsonRequest::Builder::SetUserClassifier(
fhorschigcb5d7fc02016-12-20 13:14:55272 const UserClassifier& user_classifier) {
273 if (IsSendingUserClassEnabled()) {
274 user_class_ = GetUserClassString(user_classifier.GetUserClass());
275 }
276 return *this;
277}
278
Justin DeWitt7943cde2019-07-24 17:28:52279JsonRequest::Builder& JsonRequest::Builder::SetOptionalImagesCapability(
280 bool supports_optional_images) {
281 if (supports_optional_images && IsSendingOptionalImagesCapabilityEnabled()) {
282 display_capability_ = "CAPABILITY_OPTIONAL_IMAGES";
283 }
284 return *this;
285}
286
Mark Pilgrimda33faa2018-06-06 14:29:47287std::unique_ptr<network::ResourceRequest>
288JsonRequest::Builder::BuildResourceRequest() const {
289 auto resource_request = std::make_unique<network::ResourceRequest>();
290 resource_request->url = url_;
Yutaka Hirano3d804982019-07-29 06:41:34291 resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
Mark Pilgrimda33faa2018-06-06 14:29:47292 resource_request->method = "POST";
293 resource_request->headers.SetHeader("Content-Type",
294 "application/json; charset=UTF-8");
fhorschigcb5d7fc02016-12-20 13:14:55295 if (!auth_header_.empty()) {
Mark Pilgrimda33faa2018-06-06 14:29:47296 resource_request->headers.SetHeader("Authorization", auth_header_);
fhorschigcb5d7fc02016-12-20 13:14:55297 }
298 // Add X-Client-Data header with experiment IDs from field trials.
Wang Hui00fff442018-07-09 23:50:23299 // TODO: We should call AppendVariationHeaders with explicit
300 // variations::SignedIn::kNo If the auth_header_ is empty
Takashi Toyoshimaf3ceca92019-02-04 07:49:05301 variations::AppendVariationsHeaderUnknownSignedIn(
302 url_, variations::InIncognito::kNo, resource_request.get());
Mark Pilgrimda33faa2018-06-06 14:29:47303 return resource_request;
fhorschigcb5d7fc02016-12-20 13:14:55304}
305
treib9de525a2017-01-19 12:20:37306std::string JsonRequest::Builder::BuildBody() const {
Jinho Bangedffb4ee2018-01-02 15:38:30307 auto request = std::make_unique<base::DictionaryValue>();
fhorschigcb5d7fc02016-12-20 13:14:55308 std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code);
treiba57f51e2017-03-23 14:47:52309 if (!user_locale.empty()) {
310 request->SetString("uiLanguage", user_locale);
311 }
fhorschigcb5d7fc02016-12-20 13:14:55312
treiba57f51e2017-03-23 14:47:52313 request->SetString("priority", params_.interactive_request
314 ? "USER_ACTION"
315 : "BACKGROUND_PREFETCH");
fhorschigcb5d7fc02016-12-20 13:14:55316
Jinho Bangedffb4ee2018-01-02 15:38:30317 auto excluded = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52318 for (const auto& id : params_.excluded_ids) {
319 excluded->AppendString(id);
fhorschigcb5d7fc02016-12-20 13:14:55320 }
treiba57f51e2017-03-23 14:47:52321 request->Set("excludedSuggestionIds", std::move(excluded));
322
323 if (!user_class_.empty()) {
324 request->SetString("userActivenessClass", user_class_);
325 }
326
Justin DeWitt7943cde2019-07-24 17:28:52327 if (!display_capability_.empty()) {
328 request->SetString("displayCapability", display_capability_);
329 }
330
Michael Martis98cd8732017-07-14 03:26:19331 language::UrlLanguageHistogram::LanguageInfo ui_language;
332 language::UrlLanguageHistogram::LanguageInfo other_top_language;
treiba57f51e2017-03-23 14:47:52333 PrepareLanguages(&ui_language, &other_top_language);
334 if (ui_language.frequency != 0 || other_top_language.frequency != 0) {
Jinho Bangedffb4ee2018-01-02 15:38:30335 auto language_list = std::make_unique<base::ListValue>();
treiba57f51e2017-03-23 14:47:52336 if (ui_language.frequency > 0) {
337 AppendLanguageInfoToList(language_list.get(), ui_language);
338 }
339 if (other_top_language.frequency > 0) {
340 AppendLanguageInfoToList(language_list.get(), other_top_language);
341 }
342 request->Set("topLanguages", std::move(language_list));
343 }
344
Vitalii Iarko3fdb9f912017-09-21 08:28:25345 // TODO(vitaliii): Support count_to_fetch without requiring
346 // |exclusive_category|.
347 if (params_.exclusive_category.has_value()) {
348 base::DictionaryValue exclusive_category_parameters;
349 exclusive_category_parameters.SetInteger(
350 "id", params_.exclusive_category->remote_id());
351 exclusive_category_parameters.SetInteger("numSuggestions",
352 params_.count_to_fetch);
353 base::ListValue category_parameters;
Jan Wilken Dörried322b292019-09-11 09:03:49354 category_parameters.Append(std::move(exclusive_category_parameters));
Vitalii Iarko3fdb9f912017-09-21 08:28:25355 request->SetKey("categoryParameters", std::move(category_parameters));
356 }
fhorschigcb5d7fc02016-12-20 13:14:55357
358 std::string request_json;
359 bool success = base::JSONWriter::WriteWithOptions(
360 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
361 DCHECK(success);
362 return request_json;
363}
364
Mark Pilgrimda33faa2018-06-06 14:29:47365std::unique_ptr<network::SimpleURLLoader> JsonRequest::Builder::BuildURLLoader(
fhorschigcb5d7fc02016-12-20 13:14:55366 const std::string& body) const {
rhalavatic0ea6f042017-02-27 15:58:06367 net::NetworkTrafficAnnotationTag traffic_annotation =
368 net::DefineNetworkTrafficAnnotation("ntp_snippets_fetch", R"(
369 semantics {
370 sender: "New Tab Page Content Suggestions Fetch"
371 description:
372 "Chromium can show content suggestions (e.g. news articles) on the "
373 "New Tab page. For signed-in users, these may be personalized "
374 "based on the user's synced browsing history."
375 trigger:
376 "Triggered periodically in the background, or upon explicit user "
377 "request."
378 data:
379 "The Chromium UI language, as well as a second language the user "
Michael Martis98cd8732017-07-14 03:26:19380 "understands, based on language::UrlLanguageHistogram. For "
381 "signed-in users, the requests is authenticated."
rhalavatic0ea6f042017-02-27 15:58:06382 destination: GOOGLE_OWNED_SERVICE
383 }
384 policy {
Ramin Halavati3b979782017-07-21 11:40:26385 cookies_allowed: NO
rhalavatic0ea6f042017-02-27 15:58:06386 setting:
387 "This feature cannot be disabled by settings now (but is requested "
388 "to be implemented in crbug.com/695129)."
rhalavatieaa64e92017-04-03 09:36:43389 chrome_policy {
rhalavatic0ea6f042017-02-27 15:58:06390 NTPContentSuggestionsEnabled {
391 policy_options {mode: MANDATORY}
rhalavatieaa64e92017-04-03 09:36:43392 NTPContentSuggestionsEnabled: false
rhalavatic0ea6f042017-02-27 15:58:06393 }
394 }
395 })");
Mark Pilgrimda33faa2018-06-06 14:29:47396 auto resource_request = BuildResourceRequest();
fhorschigcb5d7fc02016-12-20 13:14:55397
Mark Pilgrimda33faa2018-06-06 14:29:47398 // Log the request for debugging network issues.
399 VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
400 << resource_request->headers.ToString() << "\n"
401 << body;
fhorschigcb5d7fc02016-12-20 13:14:55402
Mark Pilgrimda33faa2018-06-06 14:29:47403 auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
404 traffic_annotation);
405 loader->AttachStringForUpload(body, "application/json");
406 int max_retries = JsonRequest::Get5xxRetryCount(params_.interactive_request);
407 if (max_retries > 0) {
408 loader->SetRetryOptions(
409 max_retries, network::SimpleURLLoader::RETRY_ON_5XX |
410 network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
411 }
412 return loader;
fhorschigcb5d7fc02016-12-20 13:14:55413}
414
treib9de525a2017-01-19 12:20:37415void JsonRequest::Builder::PrepareLanguages(
Michael Martis98cd8732017-07-14 03:26:19416 language::UrlLanguageHistogram::LanguageInfo* ui_language,
417 language::UrlLanguageHistogram::LanguageInfo* other_top_language) const {
fhorschigcb5d7fc02016-12-20 13:14:55418 // TODO(jkrcal): Add language model factory for iOS and add fakes to tests so
Michael Martis98cd8732017-07-14 03:26:19419 // that |language_histogram| is never nullptr. Remove this check and add a
420 // DCHECK into the constructor.
421 if (!language_histogram_ || !IsSendingTopLanguagesEnabled()) {
fhorschigcb5d7fc02016-12-20 13:14:55422 return;
423 }
424
425 // TODO(jkrcal): Is this back-and-forth converting necessary?
426 ui_language->language_code = ISO639FromPosixLocale(
427 PosixLocaleFromBCP47Language(params_.language_code));
428 ui_language->frequency =
Michael Martis98cd8732017-07-14 03:26:19429 language_histogram_->GetLanguageFrequency(ui_language->language_code);
fhorschigcb5d7fc02016-12-20 13:14:55430
Michael Martis98cd8732017-07-14 03:26:19431 std::vector<UrlLanguageHistogram::LanguageInfo> top_languages =
432 language_histogram_->GetTopLanguages();
433 for (const UrlLanguageHistogram::LanguageInfo& info : top_languages) {
fhorschigcb5d7fc02016-12-20 13:14:55434 if (info.language_code != ui_language->language_code) {
435 *other_top_language = info;
436
437 // Report to UMA how important the UI language is.
438 DCHECK_GT(other_top_language->frequency, 0)
439 << "GetTopLanguages() should not return languages with 0 frequency";
440 float ratio_ui_in_both_languages =
441 ui_language->frequency /
442 (ui_language->frequency + other_top_language->frequency);
443 UMA_HISTOGRAM_PERCENTAGE(
444 "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages",
445 ratio_ui_in_both_languages * 100);
446 break;
447 }
448 }
449}
450
451} // namespace internal
452
453} // namespace ntp_snippets