| 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 |