| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "components/ntp_snippets/remote/ntp_snippets_fetcher.h" | 5 #include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 #include <cstdlib> | 7 #include <cstdlib> |
| 9 #include <utility> | 8 #include <utility> |
| 10 | 9 |
| 11 #include "base/command_line.h" | |
| 12 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| 13 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 14 #include "base/json/json_writer.h" | |
| 15 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 16 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 18 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 19 #include "base/strings/string_number_conversions.h" | |
| 20 #include "base/strings/string_util.h" | |
| 21 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 22 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 23 #include "base/time/default_tick_clock.h" | 18 #include "base/time/default_tick_clock.h" |
| 24 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 25 #include "base/values.h" | 20 #include "base/values.h" |
| 26 #include "components/data_use_measurement/core/data_use_user_data.h" | 21 #include "components/data_use_measurement/core/data_use_user_data.h" |
| 27 #include "components/ntp_snippets/category.h" | 22 #include "components/ntp_snippets/category.h" |
| 28 #include "components/ntp_snippets/features.h" | 23 #include "components/ntp_snippets/features.h" |
| 29 #include "components/ntp_snippets/ntp_snippets_constants.h" | 24 #include "components/ntp_snippets/ntp_snippets_constants.h" |
| 25 #include "components/ntp_snippets/remote/ntp_snippets_request_params.h" |
| 30 #include "components/ntp_snippets/user_classifier.h" | 26 #include "components/ntp_snippets/user_classifier.h" |
| 31 #include "components/signin/core/browser/profile_oauth2_token_service.h" | |
| 32 #include "components/signin/core/browser/signin_manager.h" | 27 #include "components/signin/core/browser/signin_manager.h" |
| 33 #include "components/signin/core/browser/signin_manager_base.h" | 28 #include "components/signin/core/browser/signin_manager_base.h" |
| 34 #include "components/variations/net/variations_http_headers.h" | |
| 35 #include "components/variations/variations_associated_data.h" | 29 #include "components/variations/variations_associated_data.h" |
| 36 #include "grit/components_strings.h" | 30 #include "grit/components_strings.h" |
| 37 #include "net/base/load_flags.h" | |
| 38 #include "net/http/http_response_headers.h" | |
| 39 #include "net/http/http_status_code.h" | |
| 40 #include "net/url_request/url_fetcher.h" | 31 #include "net/url_request/url_fetcher.h" |
| 41 #include "third_party/icu/source/common/unicode/uloc.h" | |
| 42 #include "third_party/icu/source/common/unicode/utypes.h" | |
| 43 #include "ui/base/l10n/l10n_util.h" | 32 #include "ui/base/l10n/l10n_util.h" |
| 44 | 33 |
| 45 using net::URLFetcher; | 34 using net::URLFetcher; |
| 46 using net::URLRequestContextGetter; | 35 using net::URLRequestContextGetter; |
| 47 using net::HttpRequestHeaders; | 36 using net::HttpRequestHeaders; |
| 48 using net::URLRequestStatus; | 37 using net::URLRequestStatus; |
| 49 using translate::LanguageModel; | 38 using translate::LanguageModel; |
| 50 | 39 |
| 51 namespace ntp_snippets { | 40 namespace ntp_snippets { |
| 52 | 41 |
| 42 using internal::NTPSnippetsJsonRequest; |
| 43 using internal::FetchAPI; |
| 44 using internal::FetchResult; |
| 45 |
| 53 namespace { | 46 namespace { |
| 54 | 47 |
| 55 const char kChromeReaderApiScope[] = | 48 const char kChromeReaderApiScope[] = |
| 56 "https://www.googleapis.com/auth/webhistory"; | 49 "https://www.googleapis.com/auth/webhistory"; |
| 57 const char kContentSuggestionsApiScope[] = | 50 const char kContentSuggestionsApiScope[] = |
| 58 "https://www.googleapis.com/auth/chrome-content-suggestions"; | 51 "https://www.googleapis.com/auth/chrome-content-suggestions"; |
| 59 const char kSnippetsServerNonAuthorizedFormat[] = "%s?key=%s"; | 52 const char kSnippetsServerNonAuthorizedFormat[] = "%s?key=%s"; |
| 60 const char kAuthorizationRequestHeaderFormat[] = "Bearer %s"; | 53 const char kAuthorizationRequestHeaderFormat[] = "Bearer %s"; |
| 61 | 54 |
| 62 // Variation parameter for personalizing fetching of snippets. | 55 // Variation parameter for personalizing fetching of snippets. |
| 63 const char kPersonalizationName[] = "fetching_personalization"; | 56 const char kPersonalizationName[] = "fetching_personalization"; |
| 64 | 57 |
| 65 // Variation parameter for disabling the retry. | |
| 66 const char kBackground5xxRetriesName[] = "background_5xx_retries_count"; | |
| 67 | |
| 68 // Variation parameter for chrome-content-suggestions backend. | 58 // Variation parameter for chrome-content-suggestions backend. |
| 69 const char kContentSuggestionsBackend[] = "content_suggestions_backend"; | 59 const char kContentSuggestionsBackend[] = "content_suggestions_backend"; |
| 70 | 60 |
| 71 // Constants for possible values of the "fetching_personalization" parameter. | 61 // Constants for possible values of the "fetching_personalization" parameter. |
| 72 const char kPersonalizationPersonalString[] = "personal"; | 62 const char kPersonalizationPersonalString[] = "personal"; |
| 73 const char kPersonalizationNonPersonalString[] = "non_personal"; | 63 const char kPersonalizationNonPersonalString[] = "non_personal"; |
| 74 const char kPersonalizationBothString[] = "both"; // the default value | 64 const char kPersonalizationBothString[] = "both"; // the default value |
| 75 | 65 |
| 76 const int kMaxExcludedIds = 100; | |
| 77 | |
| 78 // Variation parameter for sending LanguageModel info to the server. | |
| 79 const char kSendTopLanguagesName[] = "send_top_languages"; | |
| 80 | |
| 81 // Variation parameter for sending UserClassifier info to the server. | |
| 82 const char kSendUserClassName[] = "send_user_class"; | |
| 83 | |
| 84 const char kBooleanParameterEnabled[] = "true"; | |
| 85 const char kBooleanParameterDisabled[] = "false"; | |
| 86 | |
| 87 const int kFetchTimeHistogramResolution = 5; | 66 const int kFetchTimeHistogramResolution = 5; |
| 88 | 67 |
| 89 std::string FetchResultToString(NTPSnippetsFetcher::FetchResult result) { | 68 std::string FetchResultToString(FetchResult result) { |
| 90 switch (result) { | 69 switch (result) { |
| 91 case NTPSnippetsFetcher::FetchResult::SUCCESS: | 70 case FetchResult::SUCCESS: |
| 92 return "OK"; | 71 return "OK"; |
| 93 case NTPSnippetsFetcher::FetchResult::DEPRECATED_EMPTY_HOSTS: | 72 case FetchResult::DEPRECATED_EMPTY_HOSTS: |
| 94 return "Cannot fetch for empty hosts list."; | 73 return "Cannot fetch for empty hosts list."; |
| 95 case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR: | 74 case FetchResult::URL_REQUEST_STATUS_ERROR: |
| 96 return "URLRequestStatus error"; | 75 return "URLRequestStatus error"; |
| 97 case NTPSnippetsFetcher::FetchResult::HTTP_ERROR: | 76 case FetchResult::HTTP_ERROR: |
| 98 return "HTTP error"; | 77 return "HTTP error"; |
| 99 case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR: | 78 case FetchResult::JSON_PARSE_ERROR: |
| 100 return "Received invalid JSON"; | 79 return "Received invalid JSON"; |
| 101 case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR: | 80 case FetchResult::INVALID_SNIPPET_CONTENT_ERROR: |
| 102 return "Invalid / empty list."; | 81 return "Invalid / empty list."; |
| 103 case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR: | 82 case FetchResult::OAUTH_TOKEN_ERROR: |
| 104 return "Error in obtaining an OAuth2 access token."; | 83 return "Error in obtaining an OAuth2 access token."; |
| 105 case NTPSnippetsFetcher::FetchResult::INTERACTIVE_QUOTA_ERROR: | 84 case FetchResult::INTERACTIVE_QUOTA_ERROR: |
| 106 return "Out of interactive quota."; | 85 return "Out of interactive quota."; |
| 107 case NTPSnippetsFetcher::FetchResult::NON_INTERACTIVE_QUOTA_ERROR: | 86 case FetchResult::NON_INTERACTIVE_QUOTA_ERROR: |
| 108 return "Out of non-interactive quota."; | 87 return "Out of non-interactive quota."; |
| 109 case NTPSnippetsFetcher::FetchResult::RESULT_MAX: | 88 case FetchResult::RESULT_MAX: |
| 110 break; | 89 break; |
| 111 } | 90 } |
| 112 NOTREACHED(); | 91 NOTREACHED(); |
| 113 return "Unknown error"; | 92 return "Unknown error"; |
| 114 } | 93 } |
| 115 | 94 |
| 116 Status FetchResultToStatus(NTPSnippetsFetcher::FetchResult result) { | 95 Status FetchResultToStatus(FetchResult result) { |
| 117 switch (result) { | 96 switch (result) { |
| 118 case NTPSnippetsFetcher::FetchResult::SUCCESS: | 97 case FetchResult::SUCCESS: |
| 119 return Status::Success(); | 98 return Status::Success(); |
| 120 // Permanent errors occur if it is more likely that the error originated | 99 // Permanent errors occur if it is more likely that the error originated |
| 121 // from the client. | 100 // from the client. |
| 122 case NTPSnippetsFetcher::FetchResult::DEPRECATED_EMPTY_HOSTS: | 101 case FetchResult::DEPRECATED_EMPTY_HOSTS: |
| 123 case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR: | 102 case FetchResult::OAUTH_TOKEN_ERROR: |
| 124 return Status(StatusCode::PERMANENT_ERROR, FetchResultToString(result)); | 103 return Status(StatusCode::PERMANENT_ERROR, FetchResultToString(result)); |
| 125 // Temporary errors occur if it's more likely that the client behaved | 104 // Temporary errors occur if it's more likely that the client behaved |
| 126 // correctly but the server failed to respond as expected. | 105 // correctly but the server failed to respond as expected. |
| 127 // TODO(fhorschig): Revisit HTTP_ERROR once the rescheduling was reworked. | 106 // TODO(fhorschig): Revisit HTTP_ERROR once the rescheduling was reworked. |
| 128 case NTPSnippetsFetcher::FetchResult::HTTP_ERROR: | 107 case FetchResult::HTTP_ERROR: |
| 129 case NTPSnippetsFetcher::FetchResult::INTERACTIVE_QUOTA_ERROR: | 108 case FetchResult::INTERACTIVE_QUOTA_ERROR: |
| 130 case NTPSnippetsFetcher::FetchResult::NON_INTERACTIVE_QUOTA_ERROR: | 109 case FetchResult::NON_INTERACTIVE_QUOTA_ERROR: |
| 131 case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR: | 110 case FetchResult::URL_REQUEST_STATUS_ERROR: |
| 132 case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR: | 111 case FetchResult::INVALID_SNIPPET_CONTENT_ERROR: |
| 133 case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR: | 112 case FetchResult::JSON_PARSE_ERROR: |
| 134 return Status(StatusCode::TEMPORARY_ERROR, FetchResultToString(result)); | 113 return Status(StatusCode::TEMPORARY_ERROR, FetchResultToString(result)); |
| 135 case NTPSnippetsFetcher::FetchResult::RESULT_MAX: | 114 case FetchResult::RESULT_MAX: |
| 136 break; | 115 break; |
| 137 } | 116 } |
| 138 NOTREACHED(); | 117 NOTREACHED(); |
| 139 return Status(StatusCode::PERMANENT_ERROR, std::string()); | 118 return Status(StatusCode::PERMANENT_ERROR, std::string()); |
| 140 } | 119 } |
| 141 | 120 |
| 142 std::string GetFetchEndpoint() { | 121 std::string GetFetchEndpoint() { |
| 143 std::string endpoint = variations::GetVariationParamValue( | 122 std::string endpoint = variations::GetVariationParamValue( |
| 144 ntp_snippets::kStudyName, kContentSuggestionsBackend); | 123 ntp_snippets::kStudyName, kContentSuggestionsBackend); |
| 145 return endpoint.empty() ? kChromeReaderServer : endpoint; | 124 return endpoint.empty() ? kChromeReaderServer : endpoint; |
| 146 } | 125 } |
| 147 | 126 |
| 148 bool IsBooleanParameterEnabled(const std::string& param_name, | |
| 149 bool default_value) { | |
| 150 std::string param_value = variations::GetVariationParamValueByFeature( | |
| 151 ntp_snippets::kArticleSuggestionsFeature, param_name); | |
| 152 if (param_value == kBooleanParameterEnabled) { | |
| 153 return true; | |
| 154 } | |
| 155 if (param_value == kBooleanParameterDisabled) { | |
| 156 return false; | |
| 157 } | |
| 158 if (!param_value.empty()) { | |
| 159 LOG(WARNING) << "Invalid value \"" << param_value | |
| 160 << "\" for variation parameter " << param_name; | |
| 161 } | |
| 162 return default_value; | |
| 163 } | |
| 164 | |
| 165 int Get5xxRetryCount(bool interactive_request) { | |
| 166 if (interactive_request) { | |
| 167 return 2; | |
| 168 } | |
| 169 return std::max(0, variations::GetVariationParamByFeatureAsInt( | |
| 170 ntp_snippets::kArticleSuggestionsFeature, | |
| 171 kBackground5xxRetriesName, 0)); | |
| 172 } | |
| 173 | |
| 174 bool IsSendingTopLanguagesEnabled() { | |
| 175 return IsBooleanParameterEnabled(kSendTopLanguagesName, | |
| 176 /*default_value=*/false); | |
| 177 } | |
| 178 | |
| 179 bool IsSendingUserClassEnabled() { | |
| 180 return IsBooleanParameterEnabled(kSendUserClassName, | |
| 181 /*default_value=*/false); | |
| 182 } | |
| 183 | |
| 184 bool UsesChromeContentSuggestionsAPI(const GURL& endpoint) { | 127 bool UsesChromeContentSuggestionsAPI(const GURL& endpoint) { |
| 185 if (endpoint == kChromeReaderServer) { | 128 if (endpoint == kChromeReaderServer) { |
| 186 return false; | 129 return false; |
| 187 } | 130 } |
| 188 | 131 |
| 189 if (endpoint != kContentSuggestionsServer && | 132 if (endpoint != kContentSuggestionsServer && |
| 190 endpoint != kContentSuggestionsStagingServer && | 133 endpoint != kContentSuggestionsStagingServer && |
| 191 endpoint != kContentSuggestionsAlphaServer) { | 134 endpoint != kContentSuggestionsAlphaServer) { |
| 192 LOG(WARNING) << "Unknown value for " << kContentSuggestionsBackend << ": " | 135 LOG(WARNING) << "Unknown value for " << kContentSuggestionsBackend << ": " |
| 193 << "assuming chromecontentsuggestions-style API"; | 136 << "assuming chromecontentsuggestions-style API"; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 217 } | 160 } |
| 218 if (!snippet) { | 161 if (!snippet) { |
| 219 return false; | 162 return false; |
| 220 } | 163 } |
| 221 | 164 |
| 222 snippets->push_back(std::move(snippet)); | 165 snippets->push_back(std::move(snippet)); |
| 223 } | 166 } |
| 224 return true; | 167 return true; |
| 225 } | 168 } |
| 226 | 169 |
| 227 // Translate the BCP 47 |language_code| into a posix locale string. | |
| 228 std::string PosixLocaleFromBCP47Language(const std::string& language_code) { | |
| 229 char locale[ULOC_FULLNAME_CAPACITY]; | |
| 230 UErrorCode error = U_ZERO_ERROR; | |
| 231 // Translate the input to a posix locale. | |
| 232 uloc_forLanguageTag(language_code.c_str(), locale, ULOC_FULLNAME_CAPACITY, | |
| 233 nullptr, &error); | |
| 234 if (error != U_ZERO_ERROR) { | |
| 235 DLOG(WARNING) << "Error in translating language code to a locale string: " | |
| 236 << error; | |
| 237 return std::string(); | |
| 238 } | |
| 239 return locale; | |
| 240 } | |
| 241 | |
| 242 std::string ISO639FromPosixLocale(const std::string& locale) { | |
| 243 char language[ULOC_LANG_CAPACITY]; | |
| 244 UErrorCode error = U_ZERO_ERROR; | |
| 245 uloc_getLanguage(locale.c_str(), language, ULOC_LANG_CAPACITY, &error); | |
| 246 if (error != U_ZERO_ERROR) { | |
| 247 DLOG(WARNING) | |
| 248 << "Error in translating locale string to a ISO639 language code: " | |
| 249 << error; | |
| 250 return std::string(); | |
| 251 } | |
| 252 return language; | |
| 253 } | |
| 254 | |
| 255 void AppendLanguageInfoToList(base::ListValue* list, | |
| 256 const LanguageModel::LanguageInfo& info) { | |
| 257 auto lang = base::MakeUnique<base::DictionaryValue>(); | |
| 258 lang->SetString("language", info.language_code); | |
| 259 lang->SetDouble("frequency", info.frequency); | |
| 260 list->Append(std::move(lang)); | |
| 261 } | |
| 262 | |
| 263 std::string GetUserClassString(UserClassifier::UserClass user_class) { | |
| 264 switch (user_class) { | |
| 265 case UserClassifier::UserClass::RARE_NTP_USER: | |
| 266 return "RARE_NTP_USER"; | |
| 267 case UserClassifier::UserClass::ACTIVE_NTP_USER: | |
| 268 return "ACTIVE_NTP_USER"; | |
| 269 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: | |
| 270 return "ACTIVE_SUGGESTIONS_CONSUMER"; | |
| 271 } | |
| 272 NOTREACHED(); | |
| 273 return std::string(); | |
| 274 } | |
| 275 | |
| 276 int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) { | 170 int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) { |
| 277 base::Time now(base::Time::Now()); | 171 base::Time now(base::Time::Now()); |
| 278 base::Time::Exploded now_exploded{}; | 172 base::Time::Exploded now_exploded{}; |
| 279 local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded); | 173 local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded); |
| 280 int now_minute = reduced_resolution | 174 int now_minute = reduced_resolution |
| 281 ? now_exploded.minute / kFetchTimeHistogramResolution * | 175 ? now_exploded.minute / kFetchTimeHistogramResolution * |
| 282 kFetchTimeHistogramResolution | 176 kFetchTimeHistogramResolution |
| 283 : now_exploded.minute; | 177 : now_exploded.minute; |
| 284 return now_exploded.hour * 60 + now_minute; | 178 return now_exploded.hour * 60 + now_minute; |
| 285 } | 179 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 302 categories->clear(); | 196 categories->clear(); |
| 303 return; | 197 return; |
| 304 } | 198 } |
| 305 NTPSnippetsFetcher::FetchedCategory category = std::move(*category_it); | 199 NTPSnippetsFetcher::FetchedCategory category = std::move(*category_it); |
| 306 categories->clear(); | 200 categories->clear(); |
| 307 categories->push_back(std::move(category)); | 201 categories->push_back(std::move(category)); |
| 308 } | 202 } |
| 309 | 203 |
| 310 } // namespace | 204 } // namespace |
| 311 | 205 |
| 312 // A single request to query snippets. | |
| 313 class NTPSnippetsFetcher::JsonRequest : public net::URLFetcherDelegate { | |
| 314 public: | |
| 315 JsonRequest(base::Optional<Category> exclusive_category, | |
| 316 base::TickClock* tick_clock, | |
| 317 const ParseJSONCallback& callback); | |
| 318 JsonRequest(JsonRequest&&); | |
| 319 ~JsonRequest() override; | |
| 320 | 206 |
| 321 // A client can expect error_details only, if there was any error during the | |
| 322 // fetching or parsing. In successful cases, it will be an empty string. | |
| 323 using CompletedCallback = | |
| 324 base::OnceCallback<void(std::unique_ptr<base::Value> result, | |
| 325 FetchResult result_code, | |
| 326 const std::string& error_details)>; | |
| 327 | |
| 328 void Start(CompletedCallback callback); | |
| 329 | |
| 330 const base::Optional<Category>& exclusive_category() const { | |
| 331 return exclusive_category_; | |
| 332 } | |
| 333 | |
| 334 base::TimeDelta GetFetchDuration() const; | |
| 335 std::string GetResponseString() const; | |
| 336 | |
| 337 private: | |
| 338 friend class RequestBuilder; | |
| 339 | |
| 340 // URLFetcherDelegate implementation. | |
| 341 void OnURLFetchComplete(const net::URLFetcher* source) override; | |
| 342 | |
| 343 void ParseJsonResponse(); | |
| 344 void OnJsonParsed(std::unique_ptr<base::Value> result); | |
| 345 void OnJsonError(const std::string& error); | |
| 346 | |
| 347 // The fetcher for downloading the snippets. Only non-null if a fetch is | |
| 348 // currently ongoing. | |
| 349 std::unique_ptr<net::URLFetcher> url_fetcher_; | |
| 350 | |
| 351 // If set, only return results for this category. | |
| 352 base::Optional<Category> exclusive_category_; | |
| 353 | |
| 354 // Use the TickClock from the Fetcher to measure the fetch time. It will be | |
| 355 // used on creation and after the fetch returned. It has to be alive until the | |
| 356 // request is destroyed. | |
| 357 base::TickClock* tick_clock_; | |
| 358 base::TimeTicks creation_time_; | |
| 359 | |
| 360 // This callback is called to parse a json string. It contains callbacks for | |
| 361 // error and success cases. | |
| 362 ParseJSONCallback parse_json_callback_; | |
| 363 | |
| 364 // The callback to notify when URLFetcher finished and results are available. | |
| 365 CompletedCallback request_completed_callback_; | |
| 366 | |
| 367 base::WeakPtrFactory<JsonRequest> weak_ptr_factory_; | |
| 368 | |
| 369 DISALLOW_COPY_AND_ASSIGN(JsonRequest); | |
| 370 }; | |
| 371 | 207 |
| 372 CategoryInfo BuildArticleCategoryInfo( | 208 CategoryInfo BuildArticleCategoryInfo( |
| 373 const base::Optional<base::string16>& title) { | 209 const base::Optional<base::string16>& title) { |
| 374 return CategoryInfo( | 210 return CategoryInfo( |
| 375 title.has_value() ? title.value() | 211 title.has_value() ? title.value() |
| 376 : l10n_util::GetStringUTF16( | 212 : l10n_util::GetStringUTF16( |
| 377 IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER), | 213 IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER), |
| 378 ContentSuggestionsCardLayout::FULL_CARD, | 214 ContentSuggestionsCardLayout::FULL_CARD, |
| 379 // TODO(dgn): merge has_more_action and has_reload_action when we remove | 215 // TODO(dgn): merge has_more_action and has_reload_action when we remove |
| 380 // the kFetchMoreFeature flag. See https://crbug.com/667752 | 216 // the kFetchMoreFeature flag. See https://crbug.com/667752 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 407 : category(c), info(info) {} | 243 : category(c), info(info) {} |
| 408 | 244 |
| 409 NTPSnippetsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) = | 245 NTPSnippetsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) = |
| 410 default; | 246 default; |
| 411 | 247 |
| 412 NTPSnippetsFetcher::FetchedCategory::~FetchedCategory() = default; | 248 NTPSnippetsFetcher::FetchedCategory::~FetchedCategory() = default; |
| 413 | 249 |
| 414 NTPSnippetsFetcher::FetchedCategory& NTPSnippetsFetcher::FetchedCategory:: | 250 NTPSnippetsFetcher::FetchedCategory& NTPSnippetsFetcher::FetchedCategory:: |
| 415 operator=(FetchedCategory&&) = default; | 251 operator=(FetchedCategory&&) = default; |
| 416 | 252 |
| 417 NTPSnippetsFetcher::Params::Params() = default; | |
| 418 NTPSnippetsFetcher::Params::Params(const Params&) = default; | |
| 419 NTPSnippetsFetcher::Params::~Params() = default; | |
| 420 | |
| 421 NTPSnippetsFetcher::NTPSnippetsFetcher( | 253 NTPSnippetsFetcher::NTPSnippetsFetcher( |
| 422 SigninManagerBase* signin_manager, | 254 SigninManagerBase* signin_manager, |
| 423 OAuth2TokenService* token_service, | 255 OAuth2TokenService* token_service, |
| 424 scoped_refptr<URLRequestContextGetter> url_request_context_getter, | 256 scoped_refptr<URLRequestContextGetter> url_request_context_getter, |
| 425 PrefService* pref_service, | 257 PrefService* pref_service, |
| 426 LanguageModel* language_model, | 258 LanguageModel* language_model, |
| 427 const ParseJSONCallback& parse_json_callback, | 259 const ParseJSONCallback& parse_json_callback, |
| 428 const std::string& api_key, | 260 const std::string& api_key, |
| 429 const UserClassifier* user_classifier) | 261 const UserClassifier* user_classifier) |
| 430 : OAuth2TokenService::Consumer("ntp_snippets"), | 262 : OAuth2TokenService::Consumer("ntp_snippets"), |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 << personalization; | 299 << personalization; |
| 468 } | 300 } |
| 469 } | 301 } |
| 470 | 302 |
| 471 NTPSnippetsFetcher::~NTPSnippetsFetcher() { | 303 NTPSnippetsFetcher::~NTPSnippetsFetcher() { |
| 472 if (waiting_for_refresh_token_) { | 304 if (waiting_for_refresh_token_) { |
| 473 token_service_->RemoveObserver(this); | 305 token_service_->RemoveObserver(this); |
| 474 } | 306 } |
| 475 } | 307 } |
| 476 | 308 |
| 477 void NTPSnippetsFetcher::FetchSnippets(const Params& params, | 309 void NTPSnippetsFetcher::FetchSnippets(const NTPSnippetsRequestParams& params, |
| 478 SnippetsAvailableCallback callback) { | 310 SnippetsAvailableCallback callback) { |
| 479 if (!DemandQuotaForRequest(params.interactive_request)) { | 311 if (!DemandQuotaForRequest(params.interactive_request)) { |
| 480 FetchFinished(OptionalFetchedCategories(), std::move(callback), | 312 FetchFinished(OptionalFetchedCategories(), std::move(callback), |
| 481 params.interactive_request | 313 params.interactive_request |
| 482 ? FetchResult::INTERACTIVE_QUOTA_ERROR | 314 ? FetchResult::INTERACTIVE_QUOTA_ERROR |
| 483 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR, | 315 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR, |
| 484 /*error_details=*/std::string()); | 316 /*error_details=*/std::string()); |
| 485 return; | 317 return; |
| 486 } | 318 } |
| 487 | 319 |
| 488 if (!params.interactive_request) { | 320 if (!params.interactive_request) { |
| 489 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", | 321 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", |
| 490 GetMinuteOfTheDay(/*local_time=*/true, | 322 GetMinuteOfTheDay(/*local_time=*/true, |
| 491 /*reduced_resolution=*/true)); | 323 /*reduced_resolution=*/true)); |
| 492 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", | 324 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", |
| 493 GetMinuteOfTheDay(/*local_time=*/false, | 325 GetMinuteOfTheDay(/*local_time=*/false, |
| 494 /*reduced_resolution=*/true)); | 326 /*reduced_resolution=*/true)); |
| 495 } | 327 } |
| 496 | 328 |
| 497 RequestBuilder builder; | 329 NTPSnippetsJsonRequest::Builder builder; |
| 498 builder.SetFetchAPI(fetch_api_) | 330 builder.SetFetchAPI(fetch_api_) |
| 499 .SetFetchAPI(fetch_api_) | 331 .SetFetchAPI(fetch_api_) |
| 500 .SetLanguageModel(language_model_) | 332 .SetLanguageModel(language_model_) |
| 501 .SetParams(params) | 333 .SetParams(params) |
| 502 .SetParseJsonCallback(parse_json_callback_) | 334 .SetParseJsonCallback(parse_json_callback_) |
| 503 .SetPersonalization(personalization_) | 335 .SetPersonalization(personalization_) |
| 504 .SetTickClock(tick_clock_.get()) | 336 .SetTickClock(tick_clock_.get()) |
| 505 .SetUrlRequestContextGetter(url_request_context_getter_) | 337 .SetUrlRequestContextGetter(url_request_context_getter_) |
| 506 .SetUserClassifier(*user_classifier_); | 338 .SetUserClassifier(*user_classifier_); |
| 507 | 339 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 518 // Wait until we get a refresh token. | 350 // Wait until we get a refresh token. |
| 519 waiting_for_refresh_token_ = true; | 351 waiting_for_refresh_token_ = true; |
| 520 token_service_->AddObserver(this); | 352 token_service_->AddObserver(this); |
| 521 } | 353 } |
| 522 } else { | 354 } else { |
| 523 // Not signed in: fetch snippets (without authentication). | 355 // Not signed in: fetch snippets (without authentication). |
| 524 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); | 356 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); |
| 525 } | 357 } |
| 526 } | 358 } |
| 527 | 359 |
| 528 NTPSnippetsFetcher::JsonRequest::JsonRequest( | |
| 529 base::Optional<Category> exclusive_category, | |
| 530 base::TickClock* tick_clock, // Needed until destruction of the request. | |
| 531 const ParseJSONCallback& callback) | |
| 532 : exclusive_category_(exclusive_category), | |
| 533 tick_clock_(tick_clock), | |
| 534 parse_json_callback_(callback), | |
| 535 weak_ptr_factory_(this) { | |
| 536 creation_time_ = tick_clock_->NowTicks(); | |
| 537 } | |
| 538 | |
| 539 NTPSnippetsFetcher::JsonRequest::~JsonRequest() { | |
| 540 LOG_IF(DFATAL, !request_completed_callback_.is_null()) | |
| 541 << "The CompletionCallback was never called!"; | |
| 542 } | |
| 543 | |
| 544 void NTPSnippetsFetcher::JsonRequest::Start(CompletedCallback callback) { | |
| 545 request_completed_callback_ = std::move(callback); | |
| 546 url_fetcher_->Start(); | |
| 547 } | |
| 548 | |
| 549 base::TimeDelta NTPSnippetsFetcher::JsonRequest::GetFetchDuration() const { | |
| 550 return tick_clock_->NowTicks() - creation_time_; | |
| 551 } | |
| 552 | |
| 553 std::string NTPSnippetsFetcher::JsonRequest::GetResponseString() const { | |
| 554 std::string response; | |
| 555 url_fetcher_->GetResponseAsString(&response); | |
| 556 return response; | |
| 557 } | |
| 558 | |
| 559 //////////////////////////////////////////////////////////////////////////////// | |
| 560 // URLFetcherDelegate overrides | |
| 561 void NTPSnippetsFetcher::JsonRequest::OnURLFetchComplete( | |
| 562 const net::URLFetcher* source) { | |
| 563 DCHECK_EQ(url_fetcher_.get(), source); | |
| 564 const URLRequestStatus& status = url_fetcher_->GetStatus(); | |
| 565 int response = url_fetcher_->GetResponseCode(); | |
| 566 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 567 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode", | |
| 568 status.is_success() ? response : status.error()); | |
| 569 | |
| 570 if (!status.is_success()) { | |
| 571 std::move(request_completed_callback_) | |
| 572 .Run(/*result=*/nullptr, FetchResult::URL_REQUEST_STATUS_ERROR, | |
| 573 /*error_details=*/base::StringPrintf(" %d", status.error())); | |
| 574 } else if (response != net::HTTP_OK) { | |
| 575 // TODO(jkrcal): https://crbug.com/609084 | |
| 576 // We need to deal with the edge case again where the auth | |
| 577 // token expires just before we send the request (in which case we need to | |
| 578 // fetch a new auth token). We should extract that into a common class | |
| 579 // instead of adding it to every single class that uses auth tokens. | |
| 580 std::move(request_completed_callback_) | |
| 581 .Run(/*result=*/nullptr, FetchResult::HTTP_ERROR, | |
| 582 /*error_details=*/base::StringPrintf(" %d", response)); | |
| 583 } else { | |
| 584 ParseJsonResponse(); | |
| 585 } | |
| 586 } | |
| 587 | |
| 588 void NTPSnippetsFetcher::JsonRequest::ParseJsonResponse() { | |
| 589 std::string json_string; | |
| 590 bool stores_result_to_string = | |
| 591 url_fetcher_->GetResponseAsString(&json_string); | |
| 592 DCHECK(stores_result_to_string); | |
| 593 | |
| 594 parse_json_callback_.Run( | |
| 595 json_string, | |
| 596 base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()), | |
| 597 base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr())); | |
| 598 } | |
| 599 | |
| 600 void NTPSnippetsFetcher::JsonRequest::OnJsonParsed( | |
| 601 std::unique_ptr<base::Value> result) { | |
| 602 std::move(request_completed_callback_) | |
| 603 .Run(std::move(result), FetchResult::SUCCESS, | |
| 604 /*error_details=*/std::string()); | |
| 605 } | |
| 606 | |
| 607 void NTPSnippetsFetcher::JsonRequest::OnJsonError(const std::string& error) { | |
| 608 std::string json_string; | |
| 609 url_fetcher_->GetResponseAsString(&json_string); | |
| 610 LOG(WARNING) << "Received invalid JSON (" << error << "): " << json_string; | |
| 611 std::move(request_completed_callback_) | |
| 612 .Run(/*result=*/nullptr, FetchResult::JSON_PARSE_ERROR, | |
| 613 /*error_details=*/base::StringPrintf(" (error %s)", error.c_str())); | |
| 614 } | |
| 615 | |
| 616 NTPSnippetsFetcher::RequestBuilder::RequestBuilder() | |
| 617 : fetch_api_(CHROME_READER_API), | |
| 618 personalization_(Personalization::kBoth), | |
| 619 language_model_(nullptr) {} | |
| 620 NTPSnippetsFetcher::RequestBuilder::RequestBuilder(RequestBuilder&&) = default; | |
| 621 NTPSnippetsFetcher::RequestBuilder::~RequestBuilder() = default; | |
| 622 | |
| 623 std::unique_ptr<NTPSnippetsFetcher::JsonRequest> | |
| 624 NTPSnippetsFetcher::RequestBuilder::Build() const { | |
| 625 DCHECK(!url_.is_empty()); | |
| 626 DCHECK(url_request_context_getter_); | |
| 627 auto request = base::MakeUnique<JsonRequest>( | |
| 628 params_.exclusive_category, tick_clock_, parse_json_callback_); | |
| 629 std::string body = BuildBody(); | |
| 630 std::string headers = BuildHeaders(); | |
| 631 request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body); | |
| 632 | |
| 633 // Log the request for debugging network issues. | |
| 634 VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n" | |
| 635 << headers << "\n" | |
| 636 << body; | |
| 637 | |
| 638 return request; | |
| 639 } | |
| 640 | |
| 641 NTPSnippetsFetcher::RequestBuilder& | |
| 642 NTPSnippetsFetcher::RequestBuilder::SetAuthentication( | |
| 643 const std::string& account_id, | |
| 644 const std::string& auth_header) { | |
| 645 obfuscated_gaia_id_ = account_id; | |
| 646 auth_header_ = auth_header; | |
| 647 return *this; | |
| 648 } | |
| 649 | |
| 650 NTPSnippetsFetcher::RequestBuilder& | |
| 651 NTPSnippetsFetcher::RequestBuilder::SetFetchAPI(FetchAPI fetch_api) { | |
| 652 fetch_api_ = fetch_api; | |
| 653 return *this; | |
| 654 } | |
| 655 | |
| 656 NTPSnippetsFetcher::RequestBuilder& | |
| 657 NTPSnippetsFetcher::RequestBuilder::SetLanguageModel( | |
| 658 const translate::LanguageModel* language_model) { | |
| 659 language_model_ = language_model; | |
| 660 return *this; | |
| 661 } | |
| 662 | |
| 663 NTPSnippetsFetcher::RequestBuilder& | |
| 664 NTPSnippetsFetcher::RequestBuilder::SetParams(const Params& params) { | |
| 665 params_ = params; | |
| 666 return *this; | |
| 667 } | |
| 668 | |
| 669 NTPSnippetsFetcher::RequestBuilder& | |
| 670 NTPSnippetsFetcher::RequestBuilder::SetParseJsonCallback( | |
| 671 ParseJSONCallback callback) { | |
| 672 parse_json_callback_ = callback; | |
| 673 return *this; | |
| 674 } | |
| 675 | |
| 676 NTPSnippetsFetcher::RequestBuilder& | |
| 677 NTPSnippetsFetcher::RequestBuilder::SetPersonalization( | |
| 678 Personalization personalization) { | |
| 679 personalization_ = personalization; | |
| 680 return *this; | |
| 681 } | |
| 682 | |
| 683 NTPSnippetsFetcher::RequestBuilder& | |
| 684 NTPSnippetsFetcher::RequestBuilder::SetTickClock(base::TickClock* tick_clock) { | |
| 685 tick_clock_ = tick_clock; | |
| 686 return *this; | |
| 687 } | |
| 688 | |
| 689 NTPSnippetsFetcher::RequestBuilder& NTPSnippetsFetcher::RequestBuilder::SetUrl( | |
| 690 const GURL& url) { | |
| 691 url_ = url; | |
| 692 return *this; | |
| 693 } | |
| 694 | |
| 695 NTPSnippetsFetcher::RequestBuilder& | |
| 696 NTPSnippetsFetcher::RequestBuilder::SetUrlRequestContextGetter( | |
| 697 const scoped_refptr<net::URLRequestContextGetter>& context_getter) { | |
| 698 url_request_context_getter_ = context_getter; | |
| 699 return *this; | |
| 700 } | |
| 701 | |
| 702 NTPSnippetsFetcher::RequestBuilder& | |
| 703 NTPSnippetsFetcher::RequestBuilder::SetUserClassifier( | |
| 704 const UserClassifier& user_classifier) { | |
| 705 if (IsSendingUserClassEnabled()) { | |
| 706 user_class_ = GetUserClassString(user_classifier.GetUserClass()); | |
| 707 } | |
| 708 return *this; | |
| 709 } | |
| 710 | |
| 711 std::string NTPSnippetsFetcher::RequestBuilder::BuildHeaders() const { | |
| 712 net::HttpRequestHeaders headers; | |
| 713 headers.SetHeader("Content-Type", "application/json; charset=UTF-8"); | |
| 714 if (!auth_header_.empty()) { | |
| 715 headers.SetHeader("Authorization", auth_header_); | |
| 716 } | |
| 717 // Add X-Client-Data header with experiment IDs from field trials. | |
| 718 // Note: It's fine to pass in |is_signed_in| false, which does not affect | |
| 719 // transmission of experiment ids coming from the variations server. | |
| 720 bool is_signed_in = false; | |
| 721 variations::AppendVariationHeaders(url_, | |
| 722 false, // incognito | |
| 723 false, // uma_enabled | |
| 724 is_signed_in, &headers); | |
| 725 return headers.ToString(); | |
| 726 } | |
| 727 | |
| 728 std::string NTPSnippetsFetcher::RequestBuilder::BuildBody() const { | |
| 729 auto request = base::MakeUnique<base::DictionaryValue>(); | |
| 730 std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code); | |
| 731 switch (fetch_api_) { | |
| 732 case NTPSnippetsFetcher::CHROME_READER_API: { | |
| 733 auto content_params = base::MakeUnique<base::DictionaryValue>(); | |
| 734 content_params->SetBoolean("only_return_personalized_results", | |
| 735 ReturnOnlyPersonalizedResults()); | |
| 736 | |
| 737 auto content_restricts = base::MakeUnique<base::ListValue>(); | |
| 738 for (const auto* metadata : {"TITLE", "SNIPPET", "THUMBNAIL"}) { | |
| 739 auto entry = base::MakeUnique<base::DictionaryValue>(); | |
| 740 entry->SetString("type", "METADATA"); | |
| 741 entry->SetString("value", metadata); | |
| 742 content_restricts->Append(std::move(entry)); | |
| 743 } | |
| 744 | |
| 745 auto local_scoring_params = base::MakeUnique<base::DictionaryValue>(); | |
| 746 local_scoring_params->Set("content_params", std::move(content_params)); | |
| 747 local_scoring_params->Set("content_restricts", | |
| 748 std::move(content_restricts)); | |
| 749 | |
| 750 auto global_scoring_params = base::MakeUnique<base::DictionaryValue>(); | |
| 751 global_scoring_params->SetInteger("num_to_return", | |
| 752 params_.count_to_fetch); | |
| 753 global_scoring_params->SetInteger("sort_type", 1); | |
| 754 | |
| 755 auto advanced = base::MakeUnique<base::DictionaryValue>(); | |
| 756 advanced->Set("local_scoring_params", std::move(local_scoring_params)); | |
| 757 advanced->Set("global_scoring_params", std::move(global_scoring_params)); | |
| 758 | |
| 759 request->SetString("response_detail_level", "STANDARD"); | |
| 760 request->Set("advanced_options", std::move(advanced)); | |
| 761 if (!obfuscated_gaia_id_.empty()) { | |
| 762 request->SetString("obfuscated_gaia_id", obfuscated_gaia_id_); | |
| 763 } | |
| 764 if (!user_locale.empty()) { | |
| 765 request->SetString("user_locale", user_locale); | |
| 766 } | |
| 767 break; | |
| 768 } | |
| 769 | |
| 770 case NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API: { | |
| 771 if (!user_locale.empty()) { | |
| 772 request->SetString("uiLanguage", user_locale); | |
| 773 } | |
| 774 | |
| 775 request->SetString("priority", params_.interactive_request | |
| 776 ? "USER_ACTION" | |
| 777 : "BACKGROUND_PREFETCH"); | |
| 778 | |
| 779 auto excluded = base::MakeUnique<base::ListValue>(); | |
| 780 for (const auto& id : params_.excluded_ids) { | |
| 781 excluded->AppendString(id); | |
| 782 if (excluded->GetSize() >= kMaxExcludedIds) { | |
| 783 break; | |
| 784 } | |
| 785 } | |
| 786 request->Set("excludedSuggestionIds", std::move(excluded)); | |
| 787 | |
| 788 if (!user_class_.empty()) { | |
| 789 request->SetString("userActivenessClass", user_class_); | |
| 790 } | |
| 791 | |
| 792 translate::LanguageModel::LanguageInfo ui_language; | |
| 793 translate::LanguageModel::LanguageInfo other_top_language; | |
| 794 PrepareLanguages(&ui_language, &other_top_language); | |
| 795 | |
| 796 if (ui_language.frequency == 0 && other_top_language.frequency == 0) { | |
| 797 break; | |
| 798 } | |
| 799 | |
| 800 auto language_list = base::MakeUnique<base::ListValue>(); | |
| 801 if (ui_language.frequency > 0) { | |
| 802 AppendLanguageInfoToList(language_list.get(), ui_language); | |
| 803 } | |
| 804 if (other_top_language.frequency > 0) { | |
| 805 AppendLanguageInfoToList(language_list.get(), other_top_language); | |
| 806 } | |
| 807 request->Set("topLanguages", std::move(language_list)); | |
| 808 | |
| 809 // TODO(sfiera): Support only_return_personalized_results. | |
| 810 // TODO(sfiera): Support count_to_fetch. | |
| 811 break; | |
| 812 } | |
| 813 } | |
| 814 | |
| 815 std::string request_json; | |
| 816 bool success = base::JSONWriter::WriteWithOptions( | |
| 817 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json); | |
| 818 DCHECK(success); | |
| 819 return request_json; | |
| 820 } | |
| 821 | |
| 822 std::unique_ptr<net::URLFetcher> | |
| 823 NTPSnippetsFetcher::RequestBuilder::BuildURLFetcher( | |
| 824 net::URLFetcherDelegate* delegate, | |
| 825 const std::string& headers, | |
| 826 const std::string& body) const { | |
| 827 std::unique_ptr<net::URLFetcher> url_fetcher = | |
| 828 net::URLFetcher::Create(url_, net::URLFetcher::POST, delegate); | |
| 829 url_fetcher->SetRequestContext(url_request_context_getter_.get()); | |
| 830 url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 831 net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 832 data_use_measurement::DataUseUserData::AttachToFetcher( | |
| 833 url_fetcher.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS); | |
| 834 | |
| 835 url_fetcher->SetExtraRequestHeaders(headers); | |
| 836 url_fetcher->SetUploadData("application/json", body); | |
| 837 | |
| 838 // Fetchers are sometimes cancelled because a network change was detected. | |
| 839 url_fetcher->SetAutomaticallyRetryOnNetworkChanges(3); | |
| 840 url_fetcher->SetMaxRetriesOn5xx( | |
| 841 Get5xxRetryCount(params_.interactive_request)); | |
| 842 return url_fetcher; | |
| 843 } | |
| 844 | |
| 845 void NTPSnippetsFetcher::RequestBuilder::PrepareLanguages( | |
| 846 translate::LanguageModel::LanguageInfo* ui_language, | |
| 847 translate::LanguageModel::LanguageInfo* other_top_language) const { | |
| 848 // TODO(jkrcal): Add language model factory for iOS and add fakes to tests so | |
| 849 // that |language_model| is never nullptr. Remove this check and add a DCHECK | |
| 850 // into the constructor. | |
| 851 if (!language_model_ || !IsSendingTopLanguagesEnabled()) { | |
| 852 return; | |
| 853 } | |
| 854 | |
| 855 // TODO(jkrcal): Is this back-and-forth converting necessary? | |
| 856 ui_language->language_code = ISO639FromPosixLocale( | |
| 857 PosixLocaleFromBCP47Language(params_.language_code)); | |
| 858 ui_language->frequency = | |
| 859 language_model_->GetLanguageFrequency(ui_language->language_code); | |
| 860 | |
| 861 std::vector<LanguageModel::LanguageInfo> top_languages = | |
| 862 language_model_->GetTopLanguages(); | |
| 863 for (const LanguageModel::LanguageInfo& info : top_languages) { | |
| 864 if (info.language_code != ui_language->language_code) { | |
| 865 *other_top_language = info; | |
| 866 | |
| 867 // Report to UMA how important the UI language is. | |
| 868 DCHECK_GT(other_top_language->frequency, 0) | |
| 869 << "GetTopLanguages() should not return languages with 0 frequency"; | |
| 870 float ratio_ui_in_both_languages = | |
| 871 ui_language->frequency / | |
| 872 (ui_language->frequency + other_top_language->frequency); | |
| 873 UMA_HISTOGRAM_PERCENTAGE( | |
| 874 "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages", | |
| 875 ratio_ui_in_both_languages * 100); | |
| 876 break; | |
| 877 } | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated( | 360 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated( |
| 882 RequestBuilder builder, | 361 NTPSnippetsJsonRequest::Builder builder, |
| 883 SnippetsAvailableCallback callback) { | 362 SnippetsAvailableCallback callback) { |
| 884 // When not providing OAuth token, we need to pass the Google API key. | 363 // When not providing OAuth token, we need to pass the Google API key. |
| 885 builder.SetUrl( | 364 builder.SetUrl( |
| 886 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, | 365 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, |
| 887 fetch_url_.spec().c_str(), api_key_.c_str()))); | 366 fetch_url_.spec().c_str(), api_key_.c_str()))); |
| 888 StartRequest(std::move(builder), std::move(callback)); | 367 StartRequest(std::move(builder), std::move(callback)); |
| 889 } | 368 } |
| 890 | 369 |
| 891 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( | 370 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( |
| 892 RequestBuilder builder, | 371 NTPSnippetsJsonRequest::Builder builder, |
| 893 SnippetsAvailableCallback callback, | 372 SnippetsAvailableCallback callback, |
| 894 const std::string& account_id, | 373 const std::string& account_id, |
| 895 const std::string& oauth_access_token) { | 374 const std::string& oauth_access_token) { |
| 896 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. | 375 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. |
| 897 builder.SetUrl(fetch_url_) | 376 builder.SetUrl(fetch_url_) |
| 898 .SetAuthentication(account_id, | 377 .SetAuthentication(account_id, |
| 899 base::StringPrintf(kAuthorizationRequestHeaderFormat, | 378 base::StringPrintf(kAuthorizationRequestHeaderFormat, |
| 900 oauth_access_token.c_str())); | 379 oauth_access_token.c_str())); |
| 901 StartRequest(std::move(builder), std::move(callback)); | 380 StartRequest(std::move(builder), std::move(callback)); |
| 902 } | 381 } |
| 903 | 382 |
| 904 void NTPSnippetsFetcher::StartRequest(RequestBuilder builder, | 383 void NTPSnippetsFetcher::StartRequest(NTPSnippetsJsonRequest::Builder builder, |
| 905 SnippetsAvailableCallback callback) { | 384 SnippetsAvailableCallback callback) { |
| 906 std::unique_ptr<JsonRequest> request = builder.Build(); | 385 std::unique_ptr<NTPSnippetsJsonRequest> request = builder.Build(); |
| 907 JsonRequest* raw_request = request.get(); | 386 NTPSnippetsJsonRequest* raw_request = request.get(); |
| 908 raw_request->Start(base::BindOnce(&NTPSnippetsFetcher::JsonRequestDone, | 387 raw_request->Start(base::BindOnce(&NTPSnippetsFetcher::JsonRequestDone, |
| 909 base::Unretained(this), std::move(request), | 388 base::Unretained(this), std::move(request), |
| 910 std::move(callback))); | 389 std::move(callback))); |
| 911 } | 390 } |
| 912 | 391 |
| 913 void NTPSnippetsFetcher::StartTokenRequest() { | 392 void NTPSnippetsFetcher::StartTokenRequest() { |
| 914 OAuth2TokenService::ScopeSet scopes; | 393 OAuth2TokenService::ScopeSet scopes; |
| 915 scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API | 394 scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API |
| 916 ? kContentSuggestionsApiScope | 395 ? kContentSuggestionsApiScope |
| 917 : kChromeReaderApiScope); | 396 : kChromeReaderApiScope); |
| 918 oauth_request_ = token_service_->StartRequest( | 397 oauth_request_ = token_service_->StartRequest( |
| 919 signin_manager_->GetAuthenticatedAccountId(), scopes, this); | 398 signin_manager_->GetAuthenticatedAccountId(), scopes, this); |
| 920 } | 399 } |
| 921 | 400 |
| 922 //////////////////////////////////////////////////////////////////////////////// | 401 //////////////////////////////////////////////////////////////////////////////// |
| 923 // OAuth2TokenService::Consumer overrides | 402 // OAuth2TokenService::Consumer overrides |
| 924 void NTPSnippetsFetcher::OnGetTokenSuccess( | 403 void NTPSnippetsFetcher::OnGetTokenSuccess( |
| 925 const OAuth2TokenService::Request* request, | 404 const OAuth2TokenService::Request* request, |
| 926 const std::string& access_token, | 405 const std::string& access_token, |
| 927 const base::Time& expiration_time) { | 406 const base::Time& expiration_time) { |
| 928 // Delete the request after we leave this method. | 407 // Delete the request after we leave this method. |
| 929 std::unique_ptr<OAuth2TokenService::Request> oauth_request( | 408 std::unique_ptr<OAuth2TokenService::Request> oauth_request( |
| 930 std::move(oauth_request_)); | 409 std::move(oauth_request_)); |
| 931 DCHECK_EQ(oauth_request.get(), request) | 410 DCHECK_EQ(oauth_request.get(), request) |
| 932 << "Got tokens from some previous request"; | 411 << "Got tokens from some previous request"; |
| 933 | 412 |
| 934 while (!pending_requests_.empty()) { | 413 while (!pending_requests_.empty()) { |
| 935 std::pair<RequestBuilder, SnippetsAvailableCallback> builder_and_callback = | 414 std::pair<NTPSnippetsJsonRequest::Builder, SnippetsAvailableCallback> |
| 936 std::move(pending_requests_.front()); | 415 builder_and_callback = std::move(pending_requests_.front()); |
| 937 pending_requests_.pop(); | 416 pending_requests_.pop(); |
| 938 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), | 417 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), |
| 939 std::move(builder_and_callback.second), | 418 std::move(builder_and_callback.second), |
| 940 oauth_request->GetAccountId(), access_token); | 419 oauth_request->GetAccountId(), access_token); |
| 941 } | 420 } |
| 942 } | 421 } |
| 943 | 422 |
| 944 void NTPSnippetsFetcher::OnGetTokenFailure( | 423 void NTPSnippetsFetcher::OnGetTokenFailure( |
| 945 const OAuth2TokenService::Request* request, | 424 const OAuth2TokenService::Request* request, |
| 946 const GoogleServiceAuthError& error) { | 425 const GoogleServiceAuthError& error) { |
| 947 oauth_request_.reset(); | 426 oauth_request_.reset(); |
| 948 | 427 |
| 949 if (!oauth_token_retried_ && | 428 if (!oauth_token_retried_ && |
| 950 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { | 429 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { |
| 951 // The request (especially on startup) can get reset by loading the refresh | 430 // The request (especially on startup) can get reset by loading the refresh |
| 952 // token - do it one more time. | 431 // token - do it one more time. |
| 953 oauth_token_retried_ = true; | 432 oauth_token_retried_ = true; |
| 954 StartTokenRequest(); | 433 StartTokenRequest(); |
| 955 return; | 434 return; |
| 956 } | 435 } |
| 957 | 436 |
| 958 DLOG(ERROR) << "Unable to get token: " << error.ToString(); | 437 DLOG(ERROR) << "Unable to get token: " << error.ToString(); |
| 959 while (!pending_requests_.empty()) { | 438 while (!pending_requests_.empty()) { |
| 960 std::pair<RequestBuilder, SnippetsAvailableCallback> builder_and_callback = | 439 std::pair<NTPSnippetsJsonRequest::Builder, SnippetsAvailableCallback> |
| 961 std::move(pending_requests_.front()); | 440 builder_and_callback = std::move(pending_requests_.front()); |
| 962 | 441 |
| 963 FetchFinished(OptionalFetchedCategories(), | 442 FetchFinished(OptionalFetchedCategories(), |
| 964 std::move(builder_and_callback.second), | 443 std::move(builder_and_callback.second), |
| 965 FetchResult::OAUTH_TOKEN_ERROR, | 444 FetchResult::OAUTH_TOKEN_ERROR, |
| 966 /*error_details=*/base::StringPrintf( | 445 /*error_details=*/base::StringPrintf( |
| 967 " (%s)", error.ToString().c_str())); | 446 " (%s)", error.ToString().c_str())); |
| 968 pending_requests_.pop(); | 447 pending_requests_.pop(); |
| 969 } | 448 } |
| 970 } | 449 } |
| 971 | 450 |
| 972 //////////////////////////////////////////////////////////////////////////////// | 451 //////////////////////////////////////////////////////////////////////////////// |
| 973 // OAuth2TokenService::Observer overrides | 452 // OAuth2TokenService::Observer overrides |
| 974 void NTPSnippetsFetcher::OnRefreshTokenAvailable( | 453 void NTPSnippetsFetcher::OnRefreshTokenAvailable( |
| 975 const std::string& account_id) { | 454 const std::string& account_id) { |
| 976 // Only react on tokens for the account the user has signed in with. | 455 // Only react on tokens for the account the user has signed in with. |
| 977 if (account_id != signin_manager_->GetAuthenticatedAccountId()) { | 456 if (account_id != signin_manager_->GetAuthenticatedAccountId()) { |
| 978 return; | 457 return; |
| 979 } | 458 } |
| 980 | 459 |
| 981 token_service_->RemoveObserver(this); | 460 token_service_->RemoveObserver(this); |
| 982 waiting_for_refresh_token_ = false; | 461 waiting_for_refresh_token_ = false; |
| 983 oauth_token_retried_ = false; | 462 oauth_token_retried_ = false; |
| 984 StartTokenRequest(); | 463 StartTokenRequest(); |
| 985 } | 464 } |
| 986 | 465 |
| 987 void NTPSnippetsFetcher::JsonRequestDone(std::unique_ptr<JsonRequest> request, | 466 void NTPSnippetsFetcher::JsonRequestDone( |
| 988 SnippetsAvailableCallback callback, | 467 std::unique_ptr<NTPSnippetsJsonRequest> request, |
| 989 std::unique_ptr<base::Value> result, | 468 SnippetsAvailableCallback callback, |
| 990 FetchResult status_code, | 469 std::unique_ptr<base::Value> result, |
| 991 const std::string& error_details) { | 470 FetchResult status_code, |
| 471 const std::string& error_details) { |
| 992 DCHECK(request); | 472 DCHECK(request); |
| 993 last_fetch_json_ = request->GetResponseString(); | 473 last_fetch_json_ = request->GetResponseString(); |
| 994 | 474 |
| 995 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", | 475 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", |
| 996 request->GetFetchDuration()); | 476 request->GetFetchDuration()); |
| 997 | 477 |
| 998 if (!result) { | 478 if (!result) { |
| 999 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, | 479 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, |
| 1000 error_details); | 480 error_details); |
| 1001 return; | 481 return; |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1121 } | 601 } |
| 1122 NOTREACHED(); | 602 NOTREACHED(); |
| 1123 return false; | 603 return false; |
| 1124 } | 604 } |
| 1125 | 605 |
| 1126 bool NTPSnippetsFetcher::NeedsAuthentication() const { | 606 bool NTPSnippetsFetcher::NeedsAuthentication() const { |
| 1127 return (personalization_ == Personalization::kPersonal || | 607 return (personalization_ == Personalization::kPersonal || |
| 1128 personalization_ == Personalization::kBoth); | 608 personalization_ == Personalization::kBoth); |
| 1129 } | 609 } |
| 1130 | 610 |
| 611 std::string NTPSnippetsFetcher::PersonalizationModeString() const { |
| 612 switch (personalization_) { |
| 613 case Personalization::kPersonal: |
| 614 return "Only personalized"; |
| 615 break; |
| 616 case Personalization::kBoth: |
| 617 return "Both personalized and non-personalized"; |
| 618 break; |
| 619 case Personalization::kNonPersonal: |
| 620 return "Only non-personalized"; |
| 621 break; |
| 622 } |
| 623 NOTREACHED(); |
| 624 return std::string(); |
| 625 } |
| 626 |
| 1131 } // namespace ntp_snippets | 627 } // namespace ntp_snippets |
| OLD | NEW |