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 |