| 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_factory.h" | 22 #include "components/ntp_snippets/category_factory.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 CategoryFactory* category_factory, | 258 CategoryFactory* category_factory, |
| 427 LanguageModel* language_model, | 259 LanguageModel* language_model, |
| 428 const ParseJSONCallback& parse_json_callback, | 260 const ParseJSONCallback& parse_json_callback, |
| 429 const std::string& api_key, | 261 const std::string& api_key, |
| 430 const UserClassifier* user_classifier) | 262 const UserClassifier* user_classifier) |
| (...skipping 20 matching lines...) Expand all Loading... |
| 451 RequestThrottler::RequestType:: | 283 RequestThrottler::RequestType:: |
| 452 CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER), | 284 CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER), |
| 453 request_throttler_active_suggestions_consumer_( | 285 request_throttler_active_suggestions_consumer_( |
| 454 pref_service, | 286 pref_service, |
| 455 RequestThrottler::RequestType:: | 287 RequestThrottler::RequestType:: |
| 456 CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER), | 288 CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER), |
| 457 weak_ptr_factory_(this) { | 289 weak_ptr_factory_(this) { |
| 458 std::string personalization = variations::GetVariationParamValue( | 290 std::string personalization = variations::GetVariationParamValue( |
| 459 ntp_snippets::kStudyName, kPersonalizationName); | 291 ntp_snippets::kStudyName, kPersonalizationName); |
| 460 if (personalization == kPersonalizationNonPersonalString) { | 292 if (personalization == kPersonalizationNonPersonalString) { |
| 461 personalization_ = Personalization::kNonPersonal; | 293 personalization_ = internal::Personalization::kNonPersonal; |
| 462 } else if (personalization == kPersonalizationPersonalString) { | 294 } else if (personalization == kPersonalizationPersonalString) { |
| 463 personalization_ = Personalization::kPersonal; | 295 personalization_ = internal::Personalization::kPersonal; |
| 464 } else { | 296 } else { |
| 465 personalization_ = Personalization::kBoth; | 297 personalization_ = internal::Personalization::kBoth; |
| 466 LOG_IF(WARNING, !personalization.empty() && | 298 LOG_IF(WARNING, !personalization.empty() && |
| 467 personalization != kPersonalizationBothString) | 299 personalization != kPersonalizationBothString) |
| 468 << "Unknown value for " << kPersonalizationName << ": " | 300 << "Unknown value for " << kPersonalizationName << ": " |
| 469 << personalization; | 301 << personalization; |
| 470 } | 302 } |
| 471 } | 303 } |
| 472 | 304 |
| 473 NTPSnippetsFetcher::~NTPSnippetsFetcher() { | 305 NTPSnippetsFetcher::~NTPSnippetsFetcher() { |
| 474 if (waiting_for_refresh_token_) { | 306 if (waiting_for_refresh_token_) { |
| 475 token_service_->RemoveObserver(this); | 307 token_service_->RemoveObserver(this); |
| 476 } | 308 } |
| 477 } | 309 } |
| 478 | 310 |
| 479 void NTPSnippetsFetcher::FetchSnippets(const Params& params, | 311 void NTPSnippetsFetcher::FetchSnippets(const NTPSnippetsRequestParams& params, |
| 480 SnippetsAvailableCallback callback) { | 312 SnippetsAvailableCallback callback) { |
| 481 if (!DemandQuotaForRequest(params.interactive_request)) { | 313 if (!DemandQuotaForRequest(params.interactive_request)) { |
| 482 FetchFinished(OptionalFetchedCategories(), std::move(callback), | 314 FetchFinished(OptionalFetchedCategories(), std::move(callback), |
| 483 params.interactive_request | 315 params.interactive_request |
| 484 ? FetchResult::INTERACTIVE_QUOTA_ERROR | 316 ? FetchResult::INTERACTIVE_QUOTA_ERROR |
| 485 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR, | 317 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR, |
| 486 /*error_details=*/std::string()); | 318 /*error_details=*/std::string()); |
| 487 return; | 319 return; |
| 488 } | 320 } |
| 489 | 321 |
| 490 if (!params.interactive_request) { | 322 if (!params.interactive_request) { |
| 491 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", | 323 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", |
| 492 GetMinuteOfTheDay(/*local_time=*/true, | 324 GetMinuteOfTheDay(/*local_time=*/true, |
| 493 /*reduced_resolution=*/true)); | 325 /*reduced_resolution=*/true)); |
| 494 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", | 326 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", |
| 495 GetMinuteOfTheDay(/*local_time=*/false, | 327 GetMinuteOfTheDay(/*local_time=*/false, |
| 496 /*reduced_resolution=*/true)); | 328 /*reduced_resolution=*/true)); |
| 497 } | 329 } |
| 498 | 330 |
| 499 RequestBuilder builder; | 331 NTPSnippetsJsonRequest::Builder builder; |
| 500 builder.SetFetchAPI(fetch_api_) | 332 builder.SetFetchAPI(fetch_api_) |
| 501 .SetFetchAPI(fetch_api_) | 333 .SetFetchAPI(fetch_api_) |
| 502 .SetLanguageModel(language_model_) | 334 .SetLanguageModel(language_model_) |
| 503 .SetParams(params) | 335 .SetParams(params) |
| 504 .SetParseJsonCallback(parse_json_callback_) | 336 .SetParseJsonCallback(parse_json_callback_) |
| 505 .SetPersonalization(personalization_) | 337 .SetPersonalization(personalization_) |
| 506 .SetTickClock(tick_clock_.get()) | 338 .SetTickClock(tick_clock_.get()) |
| 507 .SetUrlRequestContextGetter(url_request_context_getter_) | 339 .SetUrlRequestContextGetter(url_request_context_getter_) |
| 508 .SetUserClassifier(*user_classifier_); | 340 .SetUserClassifier(*user_classifier_); |
| 509 | 341 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 520 // Wait until we get a refresh token. | 352 // Wait until we get a refresh token. |
| 521 waiting_for_refresh_token_ = true; | 353 waiting_for_refresh_token_ = true; |
| 522 token_service_->AddObserver(this); | 354 token_service_->AddObserver(this); |
| 523 } | 355 } |
| 524 } else { | 356 } else { |
| 525 // Not signed in: fetch snippets (without authentication). | 357 // Not signed in: fetch snippets (without authentication). |
| 526 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); | 358 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); |
| 527 } | 359 } |
| 528 } | 360 } |
| 529 | 361 |
| 530 NTPSnippetsFetcher::JsonRequest::JsonRequest( | |
| 531 base::Optional<Category> exclusive_category, | |
| 532 base::TickClock* tick_clock, // Needed until destruction of the request. | |
| 533 const ParseJSONCallback& callback) | |
| 534 : exclusive_category_(exclusive_category), | |
| 535 tick_clock_(tick_clock), | |
| 536 parse_json_callback_(callback), | |
| 537 weak_ptr_factory_(this) { | |
| 538 creation_time_ = tick_clock_->NowTicks(); | |
| 539 } | |
| 540 | |
| 541 NTPSnippetsFetcher::JsonRequest::~JsonRequest() { | |
| 542 LOG_IF(DFATAL, !request_completed_callback_.is_null()) | |
| 543 << "The CompletionCallback was never called!"; | |
| 544 } | |
| 545 | |
| 546 void NTPSnippetsFetcher::JsonRequest::Start(CompletedCallback callback) { | |
| 547 request_completed_callback_ = std::move(callback); | |
| 548 url_fetcher_->Start(); | |
| 549 } | |
| 550 | |
| 551 base::TimeDelta NTPSnippetsFetcher::JsonRequest::GetFetchDuration() const { | |
| 552 return tick_clock_->NowTicks() - creation_time_; | |
| 553 } | |
| 554 | |
| 555 std::string NTPSnippetsFetcher::JsonRequest::GetResponseString() const { | |
| 556 std::string response; | |
| 557 url_fetcher_->GetResponseAsString(&response); | |
| 558 return response; | |
| 559 } | |
| 560 | |
| 561 //////////////////////////////////////////////////////////////////////////////// | |
| 562 // URLFetcherDelegate overrides | |
| 563 void NTPSnippetsFetcher::JsonRequest::OnURLFetchComplete( | |
| 564 const net::URLFetcher* source) { | |
| 565 DCHECK_EQ(url_fetcher_.get(), source); | |
| 566 const URLRequestStatus& status = url_fetcher_->GetStatus(); | |
| 567 int response = url_fetcher_->GetResponseCode(); | |
| 568 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 569 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode", | |
| 570 status.is_success() ? response : status.error()); | |
| 571 | |
| 572 if (!status.is_success()) { | |
| 573 std::move(request_completed_callback_) | |
| 574 .Run(/*result=*/nullptr, FetchResult::URL_REQUEST_STATUS_ERROR, | |
| 575 /*error_details=*/base::StringPrintf(" %d", status.error())); | |
| 576 } else if (response != net::HTTP_OK) { | |
| 577 // TODO(jkrcal): https://crbug.com/609084 | |
| 578 // We need to deal with the edge case again where the auth | |
| 579 // token expires just before we send the request (in which case we need to | |
| 580 // fetch a new auth token). We should extract that into a common class | |
| 581 // instead of adding it to every single class that uses auth tokens. | |
| 582 std::move(request_completed_callback_) | |
| 583 .Run(/*result=*/nullptr, FetchResult::HTTP_ERROR, | |
| 584 /*error_details=*/base::StringPrintf(" %d", response)); | |
| 585 } else { | |
| 586 ParseJsonResponse(); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 void NTPSnippetsFetcher::JsonRequest::ParseJsonResponse() { | |
| 591 std::string json_string; | |
| 592 bool stores_result_to_string = | |
| 593 url_fetcher_->GetResponseAsString(&json_string); | |
| 594 DCHECK(stores_result_to_string); | |
| 595 | |
| 596 parse_json_callback_.Run( | |
| 597 json_string, | |
| 598 base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()), | |
| 599 base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr())); | |
| 600 } | |
| 601 | |
| 602 void NTPSnippetsFetcher::JsonRequest::OnJsonParsed( | |
| 603 std::unique_ptr<base::Value> result) { | |
| 604 std::move(request_completed_callback_) | |
| 605 .Run(std::move(result), FetchResult::SUCCESS, | |
| 606 /*error_details=*/std::string()); | |
| 607 } | |
| 608 | |
| 609 void NTPSnippetsFetcher::JsonRequest::OnJsonError(const std::string& error) { | |
| 610 std::string json_string; | |
| 611 url_fetcher_->GetResponseAsString(&json_string); | |
| 612 LOG(WARNING) << "Received invalid JSON (" << error << "): " << json_string; | |
| 613 std::move(request_completed_callback_) | |
| 614 .Run(/*result=*/nullptr, FetchResult::JSON_PARSE_ERROR, | |
| 615 /*error_details=*/base::StringPrintf(" (error %s)", error.c_str())); | |
| 616 } | |
| 617 | |
| 618 NTPSnippetsFetcher::RequestBuilder::RequestBuilder() | |
| 619 : fetch_api_(CHROME_READER_API), | |
| 620 personalization_(Personalization::kBoth), | |
| 621 language_model_(nullptr) {} | |
| 622 NTPSnippetsFetcher::RequestBuilder::RequestBuilder(RequestBuilder&&) = default; | |
| 623 NTPSnippetsFetcher::RequestBuilder::~RequestBuilder() = default; | |
| 624 | |
| 625 std::unique_ptr<NTPSnippetsFetcher::JsonRequest> | |
| 626 NTPSnippetsFetcher::RequestBuilder::Build() const { | |
| 627 DCHECK(!url_.is_empty()); | |
| 628 DCHECK(url_request_context_getter_); | |
| 629 auto request = base::MakeUnique<JsonRequest>( | |
| 630 params_.exclusive_category, tick_clock_, parse_json_callback_); | |
| 631 std::string body = BuildBody(); | |
| 632 std::string headers = BuildHeaders(); | |
| 633 request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body); | |
| 634 | |
| 635 // Log the request for debugging network issues. | |
| 636 VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n" | |
| 637 << headers << "\n" | |
| 638 << body; | |
| 639 | |
| 640 return request; | |
| 641 } | |
| 642 | |
| 643 NTPSnippetsFetcher::RequestBuilder& | |
| 644 NTPSnippetsFetcher::RequestBuilder::SetAuthentication( | |
| 645 const std::string& account_id, | |
| 646 const std::string& auth_header) { | |
| 647 obfuscated_gaia_id_ = account_id; | |
| 648 auth_header_ = auth_header; | |
| 649 return *this; | |
| 650 } | |
| 651 | |
| 652 NTPSnippetsFetcher::RequestBuilder& | |
| 653 NTPSnippetsFetcher::RequestBuilder::SetFetchAPI(FetchAPI fetch_api) { | |
| 654 fetch_api_ = fetch_api; | |
| 655 return *this; | |
| 656 } | |
| 657 | |
| 658 NTPSnippetsFetcher::RequestBuilder& | |
| 659 NTPSnippetsFetcher::RequestBuilder::SetLanguageModel( | |
| 660 const translate::LanguageModel* language_model) { | |
| 661 language_model_ = language_model; | |
| 662 return *this; | |
| 663 } | |
| 664 | |
| 665 NTPSnippetsFetcher::RequestBuilder& | |
| 666 NTPSnippetsFetcher::RequestBuilder::SetParams(const Params& params) { | |
| 667 params_ = params; | |
| 668 return *this; | |
| 669 } | |
| 670 | |
| 671 NTPSnippetsFetcher::RequestBuilder& | |
| 672 NTPSnippetsFetcher::RequestBuilder::SetParseJsonCallback( | |
| 673 ParseJSONCallback callback) { | |
| 674 parse_json_callback_ = callback; | |
| 675 return *this; | |
| 676 } | |
| 677 | |
| 678 NTPSnippetsFetcher::RequestBuilder& | |
| 679 NTPSnippetsFetcher::RequestBuilder::SetPersonalization( | |
| 680 Personalization personalization) { | |
| 681 personalization_ = personalization; | |
| 682 return *this; | |
| 683 } | |
| 684 | |
| 685 NTPSnippetsFetcher::RequestBuilder& | |
| 686 NTPSnippetsFetcher::RequestBuilder::SetTickClock(base::TickClock* tick_clock) { | |
| 687 tick_clock_ = tick_clock; | |
| 688 return *this; | |
| 689 } | |
| 690 | |
| 691 NTPSnippetsFetcher::RequestBuilder& NTPSnippetsFetcher::RequestBuilder::SetUrl( | |
| 692 const GURL& url) { | |
| 693 url_ = url; | |
| 694 return *this; | |
| 695 } | |
| 696 | |
| 697 NTPSnippetsFetcher::RequestBuilder& | |
| 698 NTPSnippetsFetcher::RequestBuilder::SetUrlRequestContextGetter( | |
| 699 const scoped_refptr<net::URLRequestContextGetter>& context_getter) { | |
| 700 url_request_context_getter_ = context_getter; | |
| 701 return *this; | |
| 702 } | |
| 703 | |
| 704 NTPSnippetsFetcher::RequestBuilder& | |
| 705 NTPSnippetsFetcher::RequestBuilder::SetUserClassifier( | |
| 706 const UserClassifier& user_classifier) { | |
| 707 if (IsSendingUserClassEnabled()) { | |
| 708 user_class_ = GetUserClassString(user_classifier.GetUserClass()); | |
| 709 } | |
| 710 return *this; | |
| 711 } | |
| 712 | |
| 713 std::string NTPSnippetsFetcher::RequestBuilder::BuildHeaders() const { | |
| 714 net::HttpRequestHeaders headers; | |
| 715 headers.SetHeader("Content-Type", "application/json; charset=UTF-8"); | |
| 716 if (!auth_header_.empty()) { | |
| 717 headers.SetHeader("Authorization", auth_header_); | |
| 718 } | |
| 719 // Add X-Client-Data header with experiment IDs from field trials. | |
| 720 // Note: It's fine to pass in |is_signed_in| false, which does not affect | |
| 721 // transmission of experiment ids coming from the variations server. | |
| 722 bool is_signed_in = false; | |
| 723 variations::AppendVariationHeaders(url_, | |
| 724 false, // incognito | |
| 725 false, // uma_enabled | |
| 726 is_signed_in, &headers); | |
| 727 return headers.ToString(); | |
| 728 } | |
| 729 | |
| 730 std::string NTPSnippetsFetcher::RequestBuilder::BuildBody() const { | |
| 731 auto request = base::MakeUnique<base::DictionaryValue>(); | |
| 732 std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code); | |
| 733 switch (fetch_api_) { | |
| 734 case NTPSnippetsFetcher::CHROME_READER_API: { | |
| 735 auto content_params = base::MakeUnique<base::DictionaryValue>(); | |
| 736 content_params->SetBoolean("only_return_personalized_results", | |
| 737 ReturnOnlyPersonalizedResults()); | |
| 738 | |
| 739 auto content_restricts = base::MakeUnique<base::ListValue>(); | |
| 740 for (const auto* metadata : {"TITLE", "SNIPPET", "THUMBNAIL"}) { | |
| 741 auto entry = base::MakeUnique<base::DictionaryValue>(); | |
| 742 entry->SetString("type", "METADATA"); | |
| 743 entry->SetString("value", metadata); | |
| 744 content_restricts->Append(std::move(entry)); | |
| 745 } | |
| 746 | |
| 747 auto local_scoring_params = base::MakeUnique<base::DictionaryValue>(); | |
| 748 local_scoring_params->Set("content_params", std::move(content_params)); | |
| 749 local_scoring_params->Set("content_restricts", | |
| 750 std::move(content_restricts)); | |
| 751 | |
| 752 auto global_scoring_params = base::MakeUnique<base::DictionaryValue>(); | |
| 753 global_scoring_params->SetInteger("num_to_return", | |
| 754 params_.count_to_fetch); | |
| 755 global_scoring_params->SetInteger("sort_type", 1); | |
| 756 | |
| 757 auto advanced = base::MakeUnique<base::DictionaryValue>(); | |
| 758 advanced->Set("local_scoring_params", std::move(local_scoring_params)); | |
| 759 advanced->Set("global_scoring_params", std::move(global_scoring_params)); | |
| 760 | |
| 761 request->SetString("response_detail_level", "STANDARD"); | |
| 762 request->Set("advanced_options", std::move(advanced)); | |
| 763 if (!obfuscated_gaia_id_.empty()) { | |
| 764 request->SetString("obfuscated_gaia_id", obfuscated_gaia_id_); | |
| 765 } | |
| 766 if (!user_locale.empty()) { | |
| 767 request->SetString("user_locale", user_locale); | |
| 768 } | |
| 769 break; | |
| 770 } | |
| 771 | |
| 772 case NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API: { | |
| 773 if (!user_locale.empty()) { | |
| 774 request->SetString("uiLanguage", user_locale); | |
| 775 } | |
| 776 | |
| 777 request->SetString("priority", params_.interactive_request | |
| 778 ? "USER_ACTION" | |
| 779 : "BACKGROUND_PREFETCH"); | |
| 780 | |
| 781 auto excluded = base::MakeUnique<base::ListValue>(); | |
| 782 for (const auto& id : params_.excluded_ids) { | |
| 783 excluded->AppendString(id); | |
| 784 if (excluded->GetSize() >= kMaxExcludedIds) { | |
| 785 break; | |
| 786 } | |
| 787 } | |
| 788 request->Set("excludedSuggestionIds", std::move(excluded)); | |
| 789 | |
| 790 if (!user_class_.empty()) { | |
| 791 request->SetString("userActivenessClass", user_class_); | |
| 792 } | |
| 793 | |
| 794 translate::LanguageModel::LanguageInfo ui_language; | |
| 795 translate::LanguageModel::LanguageInfo other_top_language; | |
| 796 PrepareLanguages(&ui_language, &other_top_language); | |
| 797 | |
| 798 if (ui_language.frequency == 0 && other_top_language.frequency == 0) { | |
| 799 break; | |
| 800 } | |
| 801 | |
| 802 auto language_list = base::MakeUnique<base::ListValue>(); | |
| 803 if (ui_language.frequency > 0) { | |
| 804 AppendLanguageInfoToList(language_list.get(), ui_language); | |
| 805 } | |
| 806 if (other_top_language.frequency > 0) { | |
| 807 AppendLanguageInfoToList(language_list.get(), other_top_language); | |
| 808 } | |
| 809 request->Set("topLanguages", std::move(language_list)); | |
| 810 | |
| 811 // TODO(sfiera): Support only_return_personalized_results. | |
| 812 // TODO(sfiera): Support count_to_fetch. | |
| 813 break; | |
| 814 } | |
| 815 } | |
| 816 | |
| 817 std::string request_json; | |
| 818 bool success = base::JSONWriter::WriteWithOptions( | |
| 819 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json); | |
| 820 DCHECK(success); | |
| 821 return request_json; | |
| 822 } | |
| 823 | |
| 824 std::unique_ptr<net::URLFetcher> | |
| 825 NTPSnippetsFetcher::RequestBuilder::BuildURLFetcher( | |
| 826 net::URLFetcherDelegate* delegate, | |
| 827 const std::string& headers, | |
| 828 const std::string& body) const { | |
| 829 std::unique_ptr<net::URLFetcher> url_fetcher = | |
| 830 net::URLFetcher::Create(url_, net::URLFetcher::POST, delegate); | |
| 831 url_fetcher->SetRequestContext(url_request_context_getter_.get()); | |
| 832 url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 833 net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 834 data_use_measurement::DataUseUserData::AttachToFetcher( | |
| 835 url_fetcher.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS); | |
| 836 | |
| 837 url_fetcher->SetExtraRequestHeaders(headers); | |
| 838 url_fetcher->SetUploadData("application/json", body); | |
| 839 | |
| 840 // Fetchers are sometimes cancelled because a network change was detected. | |
| 841 url_fetcher->SetAutomaticallyRetryOnNetworkChanges(3); | |
| 842 url_fetcher->SetMaxRetriesOn5xx( | |
| 843 Get5xxRetryCount(params_.interactive_request)); | |
| 844 return url_fetcher; | |
| 845 } | |
| 846 | |
| 847 void NTPSnippetsFetcher::RequestBuilder::PrepareLanguages( | |
| 848 translate::LanguageModel::LanguageInfo* ui_language, | |
| 849 translate::LanguageModel::LanguageInfo* other_top_language) const { | |
| 850 // TODO(jkrcal): Add language model factory for iOS and add fakes to tests so | |
| 851 // that |language_model| is never nullptr. Remove this check and add a DCHECK | |
| 852 // into the constructor. | |
| 853 if (!language_model_ || !IsSendingTopLanguagesEnabled()) { | |
| 854 return; | |
| 855 } | |
| 856 | |
| 857 // TODO(jkrcal): Is this back-and-forth converting necessary? | |
| 858 ui_language->language_code = ISO639FromPosixLocale( | |
| 859 PosixLocaleFromBCP47Language(params_.language_code)); | |
| 860 ui_language->frequency = | |
| 861 language_model_->GetLanguageFrequency(ui_language->language_code); | |
| 862 | |
| 863 std::vector<LanguageModel::LanguageInfo> top_languages = | |
| 864 language_model_->GetTopLanguages(); | |
| 865 for (const LanguageModel::LanguageInfo& info : top_languages) { | |
| 866 if (info.language_code != ui_language->language_code) { | |
| 867 *other_top_language = info; | |
| 868 | |
| 869 // Report to UMA how important the UI language is. | |
| 870 DCHECK_GT(other_top_language->frequency, 0) | |
| 871 << "GetTopLanguages() should not return languages with 0 frequency"; | |
| 872 float ratio_ui_in_both_languages = | |
| 873 ui_language->frequency / | |
| 874 (ui_language->frequency + other_top_language->frequency); | |
| 875 UMA_HISTOGRAM_PERCENTAGE( | |
| 876 "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages", | |
| 877 ratio_ui_in_both_languages * 100); | |
| 878 break; | |
| 879 } | |
| 880 } | |
| 881 } | |
| 882 | |
| 883 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated( | 362 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated( |
| 884 RequestBuilder builder, | 363 NTPSnippetsJsonRequest::Builder builder, |
| 885 SnippetsAvailableCallback callback) { | 364 SnippetsAvailableCallback callback) { |
| 886 // When not providing OAuth token, we need to pass the Google API key. | 365 // When not providing OAuth token, we need to pass the Google API key. |
| 887 builder.SetUrl( | 366 builder.SetUrl( |
| 888 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, | 367 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, |
| 889 fetch_url_.spec().c_str(), api_key_.c_str()))); | 368 fetch_url_.spec().c_str(), api_key_.c_str()))); |
| 890 StartRequest(std::move(builder), std::move(callback)); | 369 StartRequest(std::move(builder), std::move(callback)); |
| 891 } | 370 } |
| 892 | 371 |
| 893 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( | 372 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( |
| 894 RequestBuilder builder, | 373 NTPSnippetsJsonRequest::Builder builder, |
| 895 SnippetsAvailableCallback callback, | 374 SnippetsAvailableCallback callback, |
| 896 const std::string& account_id, | 375 const std::string& account_id, |
| 897 const std::string& oauth_access_token) { | 376 const std::string& oauth_access_token) { |
| 898 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. | 377 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. |
| 899 builder.SetUrl(fetch_url_) | 378 builder.SetUrl(fetch_url_) |
| 900 .SetAuthentication(account_id, | 379 .SetAuthentication(account_id, |
| 901 base::StringPrintf(kAuthorizationRequestHeaderFormat, | 380 base::StringPrintf(kAuthorizationRequestHeaderFormat, |
| 902 oauth_access_token.c_str())); | 381 oauth_access_token.c_str())); |
| 903 StartRequest(std::move(builder), std::move(callback)); | 382 StartRequest(std::move(builder), std::move(callback)); |
| 904 } | 383 } |
| 905 | 384 |
| 906 void NTPSnippetsFetcher::StartRequest(RequestBuilder builder, | 385 void NTPSnippetsFetcher::StartRequest(NTPSnippetsJsonRequest::Builder builder, |
| 907 SnippetsAvailableCallback callback) { | 386 SnippetsAvailableCallback callback) { |
| 908 std::unique_ptr<JsonRequest> request = builder.Build(); | 387 std::unique_ptr<NTPSnippetsJsonRequest> request = builder.Build(); |
| 909 JsonRequest* raw_request = request.get(); | 388 NTPSnippetsJsonRequest* raw_request = request.get(); |
| 910 raw_request->Start(base::BindOnce(&NTPSnippetsFetcher::JsonRequestDone, | 389 raw_request->Start(base::BindOnce(&NTPSnippetsFetcher::JsonRequestDone, |
| 911 base::Unretained(this), std::move(request), | 390 base::Unretained(this), std::move(request), |
| 912 std::move(callback))); | 391 std::move(callback))); |
| 913 } | 392 } |
| 914 | 393 |
| 915 void NTPSnippetsFetcher::StartTokenRequest() { | 394 void NTPSnippetsFetcher::StartTokenRequest() { |
| 916 OAuth2TokenService::ScopeSet scopes; | 395 OAuth2TokenService::ScopeSet scopes; |
| 917 scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API | 396 scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API |
| 918 ? kContentSuggestionsApiScope | 397 ? kContentSuggestionsApiScope |
| 919 : kChromeReaderApiScope); | 398 : kChromeReaderApiScope); |
| 920 oauth_request_ = token_service_->StartRequest( | 399 oauth_request_ = token_service_->StartRequest( |
| 921 signin_manager_->GetAuthenticatedAccountId(), scopes, this); | 400 signin_manager_->GetAuthenticatedAccountId(), scopes, this); |
| 922 } | 401 } |
| 923 | 402 |
| 924 //////////////////////////////////////////////////////////////////////////////// | 403 //////////////////////////////////////////////////////////////////////////////// |
| 925 // OAuth2TokenService::Consumer overrides | 404 // OAuth2TokenService::Consumer overrides |
| 926 void NTPSnippetsFetcher::OnGetTokenSuccess( | 405 void NTPSnippetsFetcher::OnGetTokenSuccess( |
| 927 const OAuth2TokenService::Request* request, | 406 const OAuth2TokenService::Request* request, |
| 928 const std::string& access_token, | 407 const std::string& access_token, |
| 929 const base::Time& expiration_time) { | 408 const base::Time& expiration_time) { |
| 930 // Delete the request after we leave this method. | 409 // Delete the request after we leave this method. |
| 931 std::unique_ptr<OAuth2TokenService::Request> oauth_request( | 410 std::unique_ptr<OAuth2TokenService::Request> oauth_request( |
| 932 std::move(oauth_request_)); | 411 std::move(oauth_request_)); |
| 933 DCHECK_EQ(oauth_request.get(), request) | 412 DCHECK_EQ(oauth_request.get(), request) |
| 934 << "Got tokens from some previous request"; | 413 << "Got tokens from some previous request"; |
| 935 | 414 |
| 936 while (!pending_requests_.empty()) { | 415 while (!pending_requests_.empty()) { |
| 937 std::pair<RequestBuilder, SnippetsAvailableCallback> builder_and_callback = | 416 std::pair<NTPSnippetsJsonRequest::Builder, SnippetsAvailableCallback> |
| 938 std::move(pending_requests_.front()); | 417 builder_and_callback = std::move(pending_requests_.front()); |
| 939 pending_requests_.pop(); | 418 pending_requests_.pop(); |
| 940 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), | 419 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), |
| 941 std::move(builder_and_callback.second), | 420 std::move(builder_and_callback.second), |
| 942 oauth_request->GetAccountId(), access_token); | 421 oauth_request->GetAccountId(), access_token); |
| 943 } | 422 } |
| 944 } | 423 } |
| 945 | 424 |
| 946 void NTPSnippetsFetcher::OnGetTokenFailure( | 425 void NTPSnippetsFetcher::OnGetTokenFailure( |
| 947 const OAuth2TokenService::Request* request, | 426 const OAuth2TokenService::Request* request, |
| 948 const GoogleServiceAuthError& error) { | 427 const GoogleServiceAuthError& error) { |
| 949 oauth_request_.reset(); | 428 oauth_request_.reset(); |
| 950 | 429 |
| 951 if (!oauth_token_retried_ && | 430 if (!oauth_token_retried_ && |
| 952 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { | 431 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { |
| 953 // The request (especially on startup) can get reset by loading the refresh | 432 // The request (especially on startup) can get reset by loading the refresh |
| 954 // token - do it one more time. | 433 // token - do it one more time. |
| 955 oauth_token_retried_ = true; | 434 oauth_token_retried_ = true; |
| 956 StartTokenRequest(); | 435 StartTokenRequest(); |
| 957 return; | 436 return; |
| 958 } | 437 } |
| 959 | 438 |
| 960 DLOG(ERROR) << "Unable to get token: " << error.ToString(); | 439 DLOG(ERROR) << "Unable to get token: " << error.ToString(); |
| 961 while (!pending_requests_.empty()) { | 440 while (!pending_requests_.empty()) { |
| 962 std::pair<RequestBuilder, SnippetsAvailableCallback> builder_and_callback = | 441 std::pair<NTPSnippetsJsonRequest::Builder, SnippetsAvailableCallback> |
| 963 std::move(pending_requests_.front()); | 442 builder_and_callback = std::move(pending_requests_.front()); |
| 964 | 443 |
| 965 FetchFinished(OptionalFetchedCategories(), | 444 FetchFinished(OptionalFetchedCategories(), |
| 966 std::move(builder_and_callback.second), | 445 std::move(builder_and_callback.second), |
| 967 FetchResult::OAUTH_TOKEN_ERROR, | 446 FetchResult::OAUTH_TOKEN_ERROR, |
| 968 /*error_details=*/base::StringPrintf( | 447 /*error_details=*/base::StringPrintf( |
| 969 " (%s)", error.ToString().c_str())); | 448 " (%s)", error.ToString().c_str())); |
| 970 pending_requests_.pop(); | 449 pending_requests_.pop(); |
| 971 } | 450 } |
| 972 } | 451 } |
| 973 | 452 |
| 974 //////////////////////////////////////////////////////////////////////////////// | 453 //////////////////////////////////////////////////////////////////////////////// |
| 975 // OAuth2TokenService::Observer overrides | 454 // OAuth2TokenService::Observer overrides |
| 976 void NTPSnippetsFetcher::OnRefreshTokenAvailable( | 455 void NTPSnippetsFetcher::OnRefreshTokenAvailable( |
| 977 const std::string& account_id) { | 456 const std::string& account_id) { |
| 978 // Only react on tokens for the account the user has signed in with. | 457 // Only react on tokens for the account the user has signed in with. |
| 979 if (account_id != signin_manager_->GetAuthenticatedAccountId()) { | 458 if (account_id != signin_manager_->GetAuthenticatedAccountId()) { |
| 980 return; | 459 return; |
| 981 } | 460 } |
| 982 | 461 |
| 983 token_service_->RemoveObserver(this); | 462 token_service_->RemoveObserver(this); |
| 984 waiting_for_refresh_token_ = false; | 463 waiting_for_refresh_token_ = false; |
| 985 oauth_token_retried_ = false; | 464 oauth_token_retried_ = false; |
| 986 StartTokenRequest(); | 465 StartTokenRequest(); |
| 987 } | 466 } |
| 988 | 467 |
| 989 void NTPSnippetsFetcher::JsonRequestDone(std::unique_ptr<JsonRequest> request, | 468 void NTPSnippetsFetcher::JsonRequestDone( |
| 990 SnippetsAvailableCallback callback, | 469 std::unique_ptr<NTPSnippetsJsonRequest> request, |
| 991 std::unique_ptr<base::Value> result, | 470 SnippetsAvailableCallback callback, |
| 992 FetchResult status_code, | 471 std::unique_ptr<base::Value> result, |
| 993 const std::string& error_details) { | 472 FetchResult status_code, |
| 473 const std::string& error_details) { |
| 994 DCHECK(request); | 474 DCHECK(request); |
| 995 last_fetch_json_ = request->GetResponseString(); | 475 last_fetch_json_ = request->GetResponseString(); |
| 996 | 476 |
| 997 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", | 477 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", |
| 998 request->GetFetchDuration()); | 478 request->GetFetchDuration()); |
| 999 | 479 |
| 1000 if (!result) { | 480 if (!result) { |
| 1001 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, | 481 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, |
| 1002 error_details); | 482 error_details); |
| 1003 return; | 483 return; |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1120 interactive_request); | 600 interactive_request); |
| 1121 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: | 601 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: |
| 1122 return request_throttler_active_suggestions_consumer_ | 602 return request_throttler_active_suggestions_consumer_ |
| 1123 .DemandQuotaForRequest(interactive_request); | 603 .DemandQuotaForRequest(interactive_request); |
| 1124 } | 604 } |
| 1125 NOTREACHED(); | 605 NOTREACHED(); |
| 1126 return false; | 606 return false; |
| 1127 } | 607 } |
| 1128 | 608 |
| 1129 bool NTPSnippetsFetcher::NeedsAuthentication() const { | 609 bool NTPSnippetsFetcher::NeedsAuthentication() const { |
| 1130 return (personalization_ == Personalization::kPersonal || | 610 return (personalization_ == internal::Personalization::kPersonal || |
| 1131 personalization_ == Personalization::kBoth); | 611 personalization_ == internal::Personalization::kBoth); |
| 1132 } | 612 } |
| 1133 | 613 |
| 1134 } // namespace ntp_snippets | 614 } // namespace ntp_snippets |
| OLD | NEW |