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/remote_suggestions_fetcher.h" | 5 #include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" |
6 | 6 |
7 #include <cstdlib> | 7 #include <cstdlib> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
14 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
16 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
18 #include "base/time/default_clock.h" | 18 #include "base/time/default_clock.h" |
19 #include "base/time/time.h" | 19 #include "base/time/time.h" |
20 #include "base/values.h" | 20 #include "base/values.h" |
21 #include "components/data_use_measurement/core/data_use_user_data.h" | 21 #include "components/data_use_measurement/core/data_use_user_data.h" |
22 #include "components/ntp_snippets/category.h" | 22 #include "components/ntp_snippets/category.h" |
23 #include "components/ntp_snippets/features.h" | 23 #include "components/ntp_snippets/features.h" |
24 #include "components/ntp_snippets/ntp_snippets_constants.h" | 24 #include "components/ntp_snippets/ntp_snippets_constants.h" |
25 #include "components/ntp_snippets/remote/request_params.h" | 25 #include "components/ntp_snippets/remote/request_params.h" |
26 #include "components/ntp_snippets/user_classifier.h" | 26 #include "components/ntp_snippets/user_classifier.h" |
| 27 #include "components/signin/core/browser/access_token_fetcher.h" |
27 #include "components/signin/core/browser/signin_manager.h" | 28 #include "components/signin/core/browser/signin_manager.h" |
28 #include "components/signin/core/browser/signin_manager_base.h" | 29 #include "components/signin/core/browser/signin_manager_base.h" |
29 #include "components/strings/grit/components_strings.h" | 30 #include "components/strings/grit/components_strings.h" |
30 #include "components/variations/variations_associated_data.h" | 31 #include "components/variations/variations_associated_data.h" |
31 #include "net/url_request/url_fetcher.h" | 32 #include "net/url_request/url_fetcher.h" |
32 #include "ui/base/l10n/l10n_util.h" | 33 #include "ui/base/l10n/l10n_util.h" |
33 | 34 |
34 using net::URLFetcher; | 35 using net::URLFetcher; |
35 using net::URLRequestContextGetter; | 36 using net::URLRequestContextGetter; |
36 using net::HttpRequestHeaders; | 37 using net::HttpRequestHeaders; |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 RemoteSuggestionsFetcher::RemoteSuggestionsFetcher( | 237 RemoteSuggestionsFetcher::RemoteSuggestionsFetcher( |
237 SigninManagerBase* signin_manager, | 238 SigninManagerBase* signin_manager, |
238 OAuth2TokenService* token_service, | 239 OAuth2TokenService* token_service, |
239 scoped_refptr<URLRequestContextGetter> url_request_context_getter, | 240 scoped_refptr<URLRequestContextGetter> url_request_context_getter, |
240 PrefService* pref_service, | 241 PrefService* pref_service, |
241 LanguageModel* language_model, | 242 LanguageModel* language_model, |
242 const ParseJSONCallback& parse_json_callback, | 243 const ParseJSONCallback& parse_json_callback, |
243 const GURL& api_endpoint, | 244 const GURL& api_endpoint, |
244 const std::string& api_key, | 245 const std::string& api_key, |
245 const UserClassifier* user_classifier) | 246 const UserClassifier* user_classifier) |
246 : OAuth2TokenService::Consumer("ntp_snippets"), | 247 : signin_manager_(signin_manager), |
247 signin_manager_(signin_manager), | |
248 token_service_(token_service), | 248 token_service_(token_service), |
249 url_request_context_getter_(std::move(url_request_context_getter)), | 249 url_request_context_getter_(std::move(url_request_context_getter)), |
250 language_model_(language_model), | 250 language_model_(language_model), |
251 parse_json_callback_(parse_json_callback), | 251 parse_json_callback_(parse_json_callback), |
252 fetch_url_(api_endpoint), | 252 fetch_url_(api_endpoint), |
253 api_key_(api_key), | 253 api_key_(api_key), |
254 clock_(new base::DefaultClock()), | 254 clock_(new base::DefaultClock()), |
255 user_classifier_(user_classifier) {} | 255 user_classifier_(user_classifier) {} |
256 | 256 |
257 RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() { | 257 RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() = default; |
258 if (waiting_for_refresh_token_) { | |
259 token_service_->RemoveObserver(this); | |
260 } | |
261 } | |
262 | 258 |
263 void RemoteSuggestionsFetcher::FetchSnippets( | 259 void RemoteSuggestionsFetcher::FetchSnippets( |
264 const RequestParams& params, | 260 const RequestParams& params, |
265 SnippetsAvailableCallback callback) { | 261 SnippetsAvailableCallback callback) { |
266 if (!params.interactive_request) { | 262 if (!params.interactive_request) { |
267 UMA_HISTOGRAM_SPARSE_SLOWLY( | 263 UMA_HISTOGRAM_SPARSE_SLOWLY( |
268 "NewTabPage.Snippets.FetchTimeLocal", | 264 "NewTabPage.Snippets.FetchTimeLocal", |
269 GetMinuteOfTheDay(/*local_time=*/true, | 265 GetMinuteOfTheDay(/*local_time=*/true, |
270 /*reduced_resolution=*/true, clock_.get())); | 266 /*reduced_resolution=*/true, clock_.get())); |
271 UMA_HISTOGRAM_SPARSE_SLOWLY( | 267 UMA_HISTOGRAM_SPARSE_SLOWLY( |
272 "NewTabPage.Snippets.FetchTimeUTC", | 268 "NewTabPage.Snippets.FetchTimeUTC", |
273 GetMinuteOfTheDay(/*local_time=*/false, | 269 GetMinuteOfTheDay(/*local_time=*/false, |
274 /*reduced_resolution=*/true, clock_.get())); | 270 /*reduced_resolution=*/true, clock_.get())); |
275 } | 271 } |
276 | 272 |
277 JsonRequest::Builder builder; | 273 JsonRequest::Builder builder; |
278 builder.SetLanguageModel(language_model_) | 274 builder.SetLanguageModel(language_model_) |
279 .SetParams(params) | 275 .SetParams(params) |
280 .SetParseJsonCallback(parse_json_callback_) | 276 .SetParseJsonCallback(parse_json_callback_) |
281 .SetClock(clock_.get()) | 277 .SetClock(clock_.get()) |
282 .SetUrlRequestContextGetter(url_request_context_getter_) | 278 .SetUrlRequestContextGetter(url_request_context_getter_) |
283 .SetUserClassifier(*user_classifier_); | 279 .SetUserClassifier(*user_classifier_); |
284 | 280 |
285 if (signin_manager_->IsAuthenticated()) { | 281 if (signin_manager_->IsAuthenticated() || signin_manager_->AuthInProgress()) { |
286 // Signed-in: get OAuth token --> fetch suggestions. | 282 // Signed-in: get OAuth token --> fetch suggestions. |
287 oauth_token_retried_ = false; | |
288 pending_requests_.emplace(std::move(builder), std::move(callback)); | 283 pending_requests_.emplace(std::move(builder), std::move(callback)); |
289 StartTokenRequest(); | 284 StartTokenRequest(); |
290 } else if (signin_manager_->AuthInProgress()) { | |
291 // Currently signing in: wait for auth to finish (the refresh token) --> | |
292 // get OAuth token --> fetch suggestions. | |
293 pending_requests_.emplace(std::move(builder), std::move(callback)); | |
294 if (!waiting_for_refresh_token_) { | |
295 // Wait until we get a refresh token. | |
296 waiting_for_refresh_token_ = true; | |
297 token_service_->AddObserver(this); | |
298 } | |
299 } else { | 285 } else { |
300 // Not signed in: fetch suggestions (without authentication). | 286 // Not signed in: fetch suggestions (without authentication). |
301 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); | 287 FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); |
302 } | 288 } |
303 } | 289 } |
304 | 290 |
305 void RemoteSuggestionsFetcher::FetchSnippetsNonAuthenticated( | 291 void RemoteSuggestionsFetcher::FetchSnippetsNonAuthenticated( |
306 JsonRequest::Builder builder, | 292 JsonRequest::Builder builder, |
307 SnippetsAvailableCallback callback) { | 293 SnippetsAvailableCallback callback) { |
308 if (api_key_.empty()) { | 294 if (api_key_.empty()) { |
309 // If we don't have an API key, don't even try. | 295 // If we don't have an API key, don't even try. |
310 FetchFinished(OptionalFetchedCategories(), std::move(callback), | 296 FetchFinished(OptionalFetchedCategories(), std::move(callback), |
311 FetchResult::MISSING_API_KEY, std::string()); | 297 FetchResult::MISSING_API_KEY, std::string()); |
312 return; | 298 return; |
313 } | 299 } |
314 // When not providing OAuth token, we need to pass the Google API key. | 300 // When not providing OAuth token, we need to pass the Google API key. |
315 builder.SetUrl( | 301 builder.SetUrl( |
316 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, | 302 GURL(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, |
317 fetch_url_.spec().c_str(), api_key_.c_str()))); | 303 fetch_url_.spec().c_str(), api_key_.c_str()))); |
318 StartRequest(std::move(builder), std::move(callback)); | 304 StartRequest(std::move(builder), std::move(callback)); |
319 } | 305 } |
320 | 306 |
321 void RemoteSuggestionsFetcher::FetchSnippetsAuthenticated( | 307 void RemoteSuggestionsFetcher::FetchSnippetsAuthenticated( |
322 JsonRequest::Builder builder, | 308 JsonRequest::Builder builder, |
323 SnippetsAvailableCallback callback, | 309 SnippetsAvailableCallback callback, |
324 const std::string& account_id, | |
325 const std::string& oauth_access_token) { | 310 const std::string& oauth_access_token) { |
326 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. | 311 // TODO(jkrcal, treib): Add unit-tests for authenticated fetches. |
327 builder.SetUrl(fetch_url_) | 312 builder.SetUrl(fetch_url_) |
328 .SetAuthentication(account_id, | 313 .SetAuthentication(signin_manager_->GetAuthenticatedAccountId(), |
329 base::StringPrintf(kAuthorizationRequestHeaderFormat, | 314 base::StringPrintf(kAuthorizationRequestHeaderFormat, |
330 oauth_access_token.c_str())); | 315 oauth_access_token.c_str())); |
331 StartRequest(std::move(builder), std::move(callback)); | 316 StartRequest(std::move(builder), std::move(callback)); |
332 } | 317 } |
333 | 318 |
334 void RemoteSuggestionsFetcher::StartRequest( | 319 void RemoteSuggestionsFetcher::StartRequest( |
335 JsonRequest::Builder builder, | 320 JsonRequest::Builder builder, |
336 SnippetsAvailableCallback callback) { | 321 SnippetsAvailableCallback callback) { |
337 std::unique_ptr<JsonRequest> request = builder.Build(); | 322 std::unique_ptr<JsonRequest> request = builder.Build(); |
338 JsonRequest* raw_request = request.get(); | 323 JsonRequest* raw_request = request.get(); |
339 raw_request->Start(base::BindOnce(&RemoteSuggestionsFetcher::JsonRequestDone, | 324 raw_request->Start(base::BindOnce(&RemoteSuggestionsFetcher::JsonRequestDone, |
340 base::Unretained(this), std::move(request), | 325 base::Unretained(this), std::move(request), |
341 std::move(callback))); | 326 std::move(callback))); |
342 } | 327 } |
343 | 328 |
344 void RemoteSuggestionsFetcher::StartTokenRequest() { | 329 void RemoteSuggestionsFetcher::StartTokenRequest() { |
345 OAuth2TokenService::ScopeSet scopes; | 330 // If there is already an ongoing token request, just wait for that. |
346 scopes.insert(kContentSuggestionsApiScope); | 331 if (token_fetcher_) { |
347 oauth_request_ = token_service_->StartRequest( | 332 return; |
348 signin_manager_->GetAuthenticatedAccountId(), scopes, this); | 333 } |
| 334 |
| 335 OAuth2TokenService::ScopeSet scopes{kContentSuggestionsApiScope}; |
| 336 token_fetcher_ = base::MakeUnique<AccessTokenFetcher>( |
| 337 "ntp_snippets", signin_manager_, token_service_, scopes, |
| 338 base::BindOnce(&RemoteSuggestionsFetcher::AccessTokenFetchFinished, |
| 339 base::Unretained(this))); |
349 } | 340 } |
350 | 341 |
351 //////////////////////////////////////////////////////////////////////////////// | 342 void RemoteSuggestionsFetcher::AccessTokenFetchFinished( |
352 // OAuth2TokenService::Consumer overrides | 343 const GoogleServiceAuthError& error, |
353 void RemoteSuggestionsFetcher::OnGetTokenSuccess( | 344 const std::string& access_token) { |
354 const OAuth2TokenService::Request* request, | 345 // Delete the fetcher only after we leave this method (which is called from |
355 const std::string& access_token, | 346 // the fetcher itself). |
356 const base::Time& expiration_time) { | 347 DCHECK(token_fetcher_); |
357 // Delete the request after we leave this method. | 348 std::unique_ptr<AccessTokenFetcher> token_fetcher_deleter( |
358 std::unique_ptr<OAuth2TokenService::Request> oauth_request( | 349 std::move(token_fetcher_)); |
359 std::move(oauth_request_)); | 350 |
360 DCHECK_EQ(oauth_request.get(), request) | 351 if (error.state() != GoogleServiceAuthError::NONE) { |
361 << "Got tokens from some previous request"; | 352 AccessTokenError(error); |
| 353 return; |
| 354 } |
| 355 |
| 356 DCHECK(!access_token.empty()); |
362 | 357 |
363 while (!pending_requests_.empty()) { | 358 while (!pending_requests_.empty()) { |
364 std::pair<JsonRequest::Builder, SnippetsAvailableCallback> | 359 std::pair<JsonRequest::Builder, SnippetsAvailableCallback> |
365 builder_and_callback = std::move(pending_requests_.front()); | 360 builder_and_callback = std::move(pending_requests_.front()); |
366 pending_requests_.pop(); | 361 pending_requests_.pop(); |
367 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), | 362 FetchSnippetsAuthenticated(std::move(builder_and_callback.first), |
368 std::move(builder_and_callback.second), | 363 std::move(builder_and_callback.second), |
369 oauth_request->GetAccountId(), access_token); | 364 access_token); |
370 } | 365 } |
371 } | 366 } |
372 | 367 |
373 void RemoteSuggestionsFetcher::OnGetTokenFailure( | 368 void RemoteSuggestionsFetcher::AccessTokenError( |
374 const OAuth2TokenService::Request* request, | |
375 const GoogleServiceAuthError& error) { | 369 const GoogleServiceAuthError& error) { |
376 oauth_request_.reset(); | 370 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); |
377 | |
378 if (!oauth_token_retried_ && | |
379 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { | |
380 // The request (especially on startup) can get reset by loading the refresh | |
381 // token - do it one more time. | |
382 oauth_token_retried_ = true; | |
383 StartTokenRequest(); | |
384 return; | |
385 } | |
386 | 371 |
387 DLOG(ERROR) << "Unable to get token: " << error.ToString(); | 372 DLOG(ERROR) << "Unable to get token: " << error.ToString(); |
| 373 |
388 while (!pending_requests_.empty()) { | 374 while (!pending_requests_.empty()) { |
389 std::pair<JsonRequest::Builder, SnippetsAvailableCallback> | 375 std::pair<JsonRequest::Builder, SnippetsAvailableCallback> |
390 builder_and_callback = std::move(pending_requests_.front()); | 376 builder_and_callback = std::move(pending_requests_.front()); |
391 | 377 |
392 FetchFinished(OptionalFetchedCategories(), | 378 FetchFinished(OptionalFetchedCategories(), |
393 std::move(builder_and_callback.second), | 379 std::move(builder_and_callback.second), |
394 FetchResult::OAUTH_TOKEN_ERROR, | 380 FetchResult::OAUTH_TOKEN_ERROR, |
395 /*error_details=*/ | 381 /*error_details=*/ |
396 base::StringPrintf(" (%s)", error.ToString().c_str())); | 382 base::StringPrintf(" (%s)", error.ToString().c_str())); |
397 pending_requests_.pop(); | 383 pending_requests_.pop(); |
398 } | 384 } |
399 } | 385 } |
400 | 386 |
401 //////////////////////////////////////////////////////////////////////////////// | |
402 // OAuth2TokenService::Observer overrides | |
403 void RemoteSuggestionsFetcher::OnRefreshTokenAvailable( | |
404 const std::string& account_id) { | |
405 // Only react on tokens for the account the user has signed in with. | |
406 if (account_id != signin_manager_->GetAuthenticatedAccountId()) { | |
407 return; | |
408 } | |
409 | |
410 token_service_->RemoveObserver(this); | |
411 waiting_for_refresh_token_ = false; | |
412 oauth_token_retried_ = false; | |
413 StartTokenRequest(); | |
414 } | |
415 | |
416 void RemoteSuggestionsFetcher::JsonRequestDone( | 387 void RemoteSuggestionsFetcher::JsonRequestDone( |
417 std::unique_ptr<JsonRequest> request, | 388 std::unique_ptr<JsonRequest> request, |
418 SnippetsAvailableCallback callback, | 389 SnippetsAvailableCallback callback, |
419 std::unique_ptr<base::Value> result, | 390 std::unique_ptr<base::Value> result, |
420 FetchResult status_code, | 391 FetchResult status_code, |
421 const std::string& error_details) { | 392 const std::string& error_details) { |
422 DCHECK(request); | 393 DCHECK(request); |
423 // Record the time when request for fetching remote content snippets finished. | 394 // Record the time when request for fetching remote content snippets finished. |
424 const base::Time fetch_time = clock_->Now(); | 395 const base::Time fetch_time = clock_->Now(); |
425 | 396 |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 category, BuildRemoteCategoryInfo(base::UTF8ToUTF16(utf8_title), | 490 category, BuildRemoteCategoryInfo(base::UTF8ToUTF16(utf8_title), |
520 allow_fetching_more_results))); | 491 allow_fetching_more_results))); |
521 } | 492 } |
522 categories->back().suggestions = std::move(suggestions); | 493 categories->back().suggestions = std::move(suggestions); |
523 } | 494 } |
524 | 495 |
525 return true; | 496 return true; |
526 } | 497 } |
527 | 498 |
528 } // namespace ntp_snippets | 499 } // namespace ntp_snippets |
OLD | NEW |