blob: 2d801ac25b858ac06204bfdd88b74fcdd02a8a5e [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_
#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "components/ntp_snippets/remote/ntp_snippet.h"
#include "components/ntp_snippets/remote/request_throttler.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
class PrefService;
class SigninManagerBase;
namespace base {
class Value;
} // namespace base
namespace ntp_snippets {
// Fetches snippet data for the NTP from the server.
class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
public OAuth2TokenService::Observer,
public net::URLFetcherDelegate {
public:
// Callbacks for JSON parsing, needed because the parsing is platform-
// dependent.
using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
using ErrorCallback = base::Callback<void(const std::string&)>;
using ParseJSONCallback = base::Callback<
void(const std::string&, const SuccessCallback&, const ErrorCallback&)>;
struct FetchedCategory {
Category category;
base::string16 localized_title; // Ignored for non-server categories.
NTPSnippet::PtrVector snippets;
explicit FetchedCategory(Category c);
FetchedCategory(FetchedCategory&&); // = default, in .cc
~FetchedCategory(); // = default, in .cc
FetchedCategory& operator=(FetchedCategory&&); // = default, in .cc
};
using FetchedCategoriesVector = std::vector<FetchedCategory>;
using OptionalSnippets = base::Optional<FetchedCategoriesVector>;
// |snippets| contains parsed snippets if a fetch succeeded. If problems
// occur, |snippets| contains no value (no actual vector in base::Optional).
// Error details can be retrieved using last_status().
using SnippetsAvailableCallback =
base::Callback<void(OptionalSnippets snippets)>;
// Enumeration listing all possible outcomes for fetch attempts. Used for UMA
// histograms, so do not change existing values. Insert new values at the end,
// and update the histogram definition.
enum class FetchResult {
SUCCESS,
EMPTY_HOSTS,
URL_REQUEST_STATUS_ERROR,
HTTP_ERROR,
JSON_PARSE_ERROR,
INVALID_SNIPPET_CONTENT_ERROR,
OAUTH_TOKEN_ERROR,
INTERACTIVE_QUOTA_ERROR,
NON_INTERACTIVE_QUOTA_ERROR,
RESULT_MAX
};
// Enumeration listing all possible variants of dealing with personalization.
enum class Personalization {
kPersonal,
kNonPersonal,
kBoth
};
NTPSnippetsFetcher(
SigninManagerBase* signin_manager,
OAuth2TokenService* token_service,
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
PrefService* pref_service,
CategoryFactory* category_factory,
const ParseJSONCallback& parse_json_callback,
const std::string& api_key);
~NTPSnippetsFetcher() override;
// Set a callback that is called when a new set of snippets are downloaded,
// overriding any previously set callback.
void SetCallback(const SnippetsAvailableCallback& callback);
// Fetches snippets from the server. |hosts| restricts the results to a set of
// hosts, e.g. "www.google.com". If host restrictions are enabled, an empty
// host set produces an error without issuing a fetch.
//
// |excluded_ids| will be reported to the server; the server should not return
// suggestions with those IDs.
//
// If an ongoing fetch exists, it will be cancelled and a new one started,
// without triggering an additional callback (i.e. not noticeable by
// subscriber of SetCallback()).
//
// Fetches snippets only if the daily quota not exceeded, unless
// |interactive_request| is set to true (use only for user-initiated fetches).
void FetchSnippetsFromHosts(const std::set<std::string>& hosts,
const std::string& language_code,
const std::set<std::string>& excluded_ids,
int count,
bool interactive_request);
// Debug string representing the status/result of the last fetch attempt.
const std::string& last_status() const { return last_status_; }
// Returns the last JSON fetched from the server.
const std::string& last_json() const {
return last_fetch_json_;
}
// Returns the personalization setting of the fetcher.
Personalization personalization() const { return personalization_; }
// Returns the URL endpoint used by the fetcher.
GURL fetch_url() const { return fetch_url_; }
// Does the fetcher use host restrictions?
bool UsesHostRestrictions() const;
// Does the fetcher use authentication to get personalized results?
bool UsesAuthentication() const;
// Overrides internal clock for testing purposes.
void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock) {
tick_clock_ = std::move(tick_clock);
}
void SetPersonalizationForTesting(Personalization personalization) {
personalization_ = personalization;
}
private:
FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestAuthenticated);
FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestUnauthenticated);
FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestExcludedIds);
enum FetchAPI {
CHROME_READER_API,
CHROME_CONTENT_SUGGESTIONS_API,
};
struct RequestParams {
FetchAPI fetch_api;
std::string obfuscated_gaia_id;
bool only_return_personalized_results;
std::string user_locale;
std::set<std::string> host_restricts;
std::set<std::string> excluded_ids;
int count_to_fetch;
bool interactive_request;
RequestParams();
~RequestParams();
std::string BuildRequest();
};
void FetchSnippetsImpl(const GURL& url,
const std::string& auth_header,
const std::string& request);
void FetchSnippetsNonAuthenticated();
void FetchSnippetsAuthenticated(const std::string& account_id,
const std::string& oauth_access_token);
void StartTokenRequest();
// OAuth2TokenService::Consumer overrides:
void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) override;
void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) override;
// OAuth2TokenService::Observer overrides:
void OnRefreshTokenAvailable(const std::string& account_id) override;
// URLFetcherDelegate implementation.
void OnURLFetchComplete(const net::URLFetcher* source) override;
bool JsonToSnippets(const base::Value& parsed,
FetchedCategoriesVector* categories);
void OnJsonParsed(std::unique_ptr<base::Value> parsed);
void OnJsonError(const std::string& error);
void FetchFinished(OptionalSnippets snippets,
FetchResult result,
const std::string& extra_message);
// Authorization for signed-in users.
SigninManagerBase* signin_manager_;
OAuth2TokenService* token_service_;
std::unique_ptr<OAuth2TokenService::Request> oauth_request_;
bool waiting_for_refresh_token_;
// Holds the URL request context.
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
CategoryFactory* const category_factory_;
const ParseJSONCallback parse_json_callback_;
base::TimeTicks fetch_start_time_;
std::string last_status_;
std::string last_fetch_json_;
// Hosts to restrict the snippets to.
std::set<std::string> hosts_;
// Snippets to exclude from the results.
std::set<std::string> excluded_ids_;
// Count of snippets to fetch.
int count_to_fetch_;
// Language code to restrict to for personalized results.
std::string locale_;
// API endpoint for fetching snippets.
const GURL fetch_url_;
// Which API to use
const FetchAPI fetch_api_;
// The fetcher for downloading the snippets.
std::unique_ptr<net::URLFetcher> url_fetcher_;
// The callback to notify when new snippets get fetched.
SnippetsAvailableCallback snippets_available_callback_;
// API key to use for non-authenticated requests.
const std::string api_key_;
// The variant of the fetching to use, loaded from variation parameters.
Personalization personalization_;
// Should we apply host restriction? It is loaded from variation parameters.
bool use_host_restriction_;
// Is the request user initiated?
bool interactive_request_;
// Allow for an injectable tick clock for testing.
std::unique_ptr<base::TickClock> tick_clock_;
// Request throttler for limiting requests.
RequestThrottler request_throttler_;
// When a token request gets canceled, we want to retry once.
bool oauth_token_retried_;
base::WeakPtrFactory<NTPSnippetsFetcher> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NTPSnippetsFetcher);
};
} // namespace ntp_snippets
#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_