Index: components/ntp_snippets/remote/remote_suggestions_provider.h |
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider.h b/components/ntp_snippets/remote/remote_suggestions_provider.h |
index e743b42951a75c8322bf0bcf98ac7a4fd16d3fbb..8b818b5fb6a383f3a9cf318590e249706e8bda37 100644 |
--- a/components/ntp_snippets/remote/remote_suggestions_provider.h |
+++ b/components/ntp_snippets/remote/remote_suggestions_provider.h |
@@ -5,448 +5,56 @@ |
#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_ |
#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_ |
-#include <cstddef> |
-#include <deque> |
-#include <map> |
#include <memory> |
-#include <set> |
-#include <string> |
-#include <utility> |
-#include <vector> |
#include "base/callback_forward.h" |
-#include "base/gtest_prod_util.h" |
#include "base/macros.h" |
-#include "base/time/clock.h" |
-#include "base/time/time.h" |
-#include "components/image_fetcher/image_fetcher_delegate.h" |
-#include "components/ntp_snippets/category.h" |
-#include "components/ntp_snippets/category_status.h" |
-#include "components/ntp_snippets/content_suggestion.h" |
#include "components/ntp_snippets/content_suggestions_provider.h" |
-#include "components/ntp_snippets/remote/ntp_snippet.h" |
-#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" |
-#include "components/ntp_snippets/remote/ntp_snippets_request_params.h" |
-#include "components/ntp_snippets/remote/ntp_snippets_scheduler.h" |
-#include "components/ntp_snippets/remote/remote_suggestions_status_service.h" |
-#include "components/ntp_snippets/remote/request_throttler.h" |
- |
-class PrefRegistrySimple; |
-class PrefService; |
- |
-namespace gfx { |
-class Image; |
-} // namespace gfx |
- |
-namespace image_fetcher { |
-class ImageDecoder; |
-class ImageFetcher; |
-} // namespace image_fetcher |
namespace ntp_snippets { |
-class RemoteSuggestionsDatabase; |
-class CategoryRanker; |
-class UserClassifier; |
- |
-// CachedImageFetcher takes care of fetching images from the network and caching |
-// them in the database. |
-// TODO(tschumann): Move into a separate library and inject the |
-// CachedImageFetcher into the RemoteSuggestionsProvider. This allows us to get |
-// rid of exposing this member for testing and lets us test the caching logic |
-// separately. |
-class CachedImageFetcher : public image_fetcher::ImageFetcherDelegate { |
- public: |
- // |pref_service| and |database| need to outlive the created image fetcher |
- // instance. |
- CachedImageFetcher(std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, |
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, |
- PrefService* pref_service, |
- RemoteSuggestionsDatabase* database); |
- ~CachedImageFetcher() override; |
- |
- // Fetches the image for a suggestion. The fetcher will first issue a lookup |
- // to the underlying cache with a fallback to the network. |
- void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, |
- const GURL& image_url, |
- const ImageFetchedCallback& callback); |
- |
- private: |
- // image_fetcher::ImageFetcherDelegate implementation. |
- void OnImageDataFetched(const std::string& id_within_category, |
- const std::string& image_data) override; |
- void OnImageDecodingDone(const ImageFetchedCallback& callback, |
- const std::string& id_within_category, |
- const gfx::Image& image); |
- void OnSnippetImageFetchedFromDatabase( |
- const ImageFetchedCallback& callback, |
- const ContentSuggestion::ID& suggestion_id, |
- const GURL& image_url, |
- // SnippetImageCallback requires nonconst reference |
- std::string data); |
- void OnSnippetImageDecodedFromDatabase( |
- const ImageFetchedCallback& callback, |
- const ContentSuggestion::ID& suggestion_id, |
- const GURL& url, |
- const gfx::Image& image); |
- void FetchSnippetImageFromNetwork(const ContentSuggestion::ID& suggestion_id, |
- const GURL& url, |
- const ImageFetchedCallback& callback); |
- |
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_; |
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_; |
- RemoteSuggestionsDatabase* database_; |
- // Request throttler for limiting requests to thumbnail images. |
- RequestThrottler thumbnail_requests_throttler_; |
- |
- DISALLOW_COPY_AND_ASSIGN(CachedImageFetcher); |
-}; |
+class NTPSnippetsFetcher; |
// Retrieves fresh content data (articles) from the server, stores them and |
// provides them as content suggestions. |
-// This class is final because it does things in its constructor which make it |
-// unsafe to derive from it. |
-// TODO(treib): Introduce two-phase initialization and make the class not final? |
-class RemoteSuggestionsProvider final : public ContentSuggestionsProvider { |
+class RemoteSuggestionsProvider : public ContentSuggestionsProvider { |
public: |
- // |application_language_code| should be a ISO 639-1 compliant string, e.g. |
- // 'en' or 'en-US'. Note that this code should only specify the language, not |
- // the locale, so 'en_US' (English language with US locale) and 'en-GB_US' |
- // (British English person in the US) are not language codes. |
- RemoteSuggestionsProvider( |
- Observer* observer, |
- PrefService* pref_service, |
- const std::string& application_language_code, |
- CategoryRanker* category_ranker, |
- const UserClassifier* user_classifier, |
- NTPSnippetsScheduler* scheduler, |
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, |
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, |
- std::unique_ptr<RemoteSuggestionsDatabase> database, |
- std::unique_ptr<RemoteSuggestionsStatusService> status_service); |
+ // TODO(jkrcal): Would be nice to get rid of this another level of statuses. |
+ // Maybe possible while refactoring the RemoteSuggestionsStatusService? (and |
+ // letting it notify both the SchedulingRemoteSuggestionsProvider and |
+ // RemoteSuggestionsProviderImpl or just the scheduling one). |
+ enum class ProviderStatus { ACTIVE, INACTIVE }; |
+ using ProviderStatusCallback = |
+ base::RepeatingCallback<void(ProviderStatus status)>; |
+ |
+ // Callback to notify with the result of a fetch. |
+ // TODO(jkrcal): Change to OnceCallback? A OnceCallback does only have a |
+ // move-constructor which seems problematic for google mock. |
+ using FetchStatusCallback = base::Callback<void(Status status_code)>; |
~RemoteSuggestionsProvider() override; |
- static void RegisterProfilePrefs(PrefRegistrySimple* registry); |
- |
- // Returns whether the service is ready. While this is false, the list of |
- // snippets will be empty, and all modifications to it (fetch, dismiss, etc) |
- // will be ignored. |
- bool ready() const { return state_ == State::READY; } |
- |
- // Returns whether the service is initialized. While this is false, some |
- // calls may trigger DCHECKs. |
- bool initialized() const { return ready() || state_ == State::DISABLED; } |
- |
- // Fetchs content suggestions from the Content Suggestions server for all |
- // remote categories in the background. |
- void FetchSnippetsInTheBackground(); |
- |
- // Fetchs content suggestions from the Content Suggestions server for all |
- // remote categories. The request to the server is performed as an interactive |
- // request. Interactive requests are used for actions triggered by the user |
- // and request lower latency processing. |
- void FetchSnippetsForAllCategories(); |
- |
- // Only used in tests and for debugging in snippets-internal/. |
- // TODO(fhorschig): Remove this getter when there is an interface for the |
- // fetcher that allows better mocks. |
- const NTPSnippetsFetcher* snippets_fetcher() const { |
- return snippets_fetcher_.get(); |
- } |
- |
- // (Re)schedules the periodic fetching of snippets. If |force| is true, the |
- // tasks will be re-scheduled even if they already exist and have the correct |
- // periods. |
- void RescheduleFetching(bool force); |
- |
- // ContentSuggestionsProvider implementation |
- CategoryStatus GetCategoryStatus(Category category) override; |
- CategoryInfo GetCategoryInfo(Category category) override; |
- void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; |
- void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, |
- const ImageFetchedCallback& callback) override; |
- void Fetch(const Category& category, |
- const std::set<std::string>& known_suggestion_ids, |
- const FetchDoneCallback& callback) override; |
- void ClearHistory( |
- base::Time begin, |
- base::Time end, |
- const base::Callback<bool(const GURL& url)>& filter) override; |
- void ClearCachedSuggestions(Category category) override; |
- void OnSignInStateChanged() override; |
- void GetDismissedSuggestionsForDebugging( |
- Category category, |
- const DismissedSuggestionsCallback& callback) override; |
- void ClearDismissedSuggestionsForDebugging(Category category) override; |
- |
- // Returns the maximum number of snippets that will be shown at once. |
- static int GetMaxSnippetCountForTesting(); |
- |
- // Available snippets, only for unit tests. |
- // TODO(treib): Get rid of this. Tests should use a fake observer instead. |
- const NTPSnippet::PtrVector& GetSnippetsForTesting(Category category) const { |
- return category_contents_.find(category)->second.snippets; |
- } |
- |
- // Dismissed snippets, only for unit tests. |
- const NTPSnippet::PtrVector& GetDismissedSnippetsForTesting( |
- Category category) const { |
- return category_contents_.find(category)->second.dismissed; |
- } |
- |
- // Overrides internal clock for testing purposes. |
- void SetClockForTesting(std::unique_ptr<base::Clock> clock) { |
- clock_ = std::move(clock); |
- } |
- |
- // TODO(tschumann): remove this method as soon as we inject the fetcher into |
- // the constructor. |
- CachedImageFetcher& GetImageFetcherForTesting() { return image_fetcher_; } |
- |
- private: |
- friend class RemoteSuggestionsProviderTest; |
- |
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, |
- DontNotifyIfNotAvailable); |
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, |
- RemoveExpiredDismissedContent); |
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, |
- RescheduleOnStateChange); |
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, StatusChanges); |
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, |
- SuggestionsFetchedOnSignInAndSignOut); |
- |
- // Possible state transitions: |
- // NOT_INITED --------+ |
- // / \ | |
- // v v | |
- // READY <--> DISABLED | |
- // \ / | |
- // v v | |
- // ERROR_OCCURRED <-----+ |
- enum class State { |
- // The service has just been created. Can change to states: |
- // - DISABLED: After the database is done loading, |
- // GetStateForDependenciesStatus can identify the next state to |
- // be DISABLED. |
- // - READY: if GetStateForDependenciesStatus returns it, after the database |
- // is done loading. |
- // - ERROR_OCCURRED: when an unrecoverable error occurred. |
- NOT_INITED, |
- |
- // The service registered observers, timers, etc. and is ready to answer to |
- // queries, fetch snippets... Can change to states: |
- // - DISABLED: when the global Chrome state changes, for example after |
- // |OnStateChanged| is called and sync is disabled. |
- // - ERROR_OCCURRED: when an unrecoverable error occurred. |
- READY, |
- |
- // The service is disabled and unregistered the related resources. |
- // Can change to states: |
- // - READY: when the global Chrome state changes, for example after |
- // |OnStateChanged| is called and sync is enabled. |
- // - ERROR_OCCURRED: when an unrecoverable error occurred. |
- DISABLED, |
- |
- // The service or one of its dependencies encountered an unrecoverable error |
- // and the service can't be used anymore. |
- ERROR_OCCURRED, |
- |
- COUNT |
- }; |
- |
- struct CategoryContent { |
- // The current status of the category. |
- CategoryStatus status = CategoryStatus::INITIALIZING; |
- |
- // The additional information about a category. |
- CategoryInfo info; |
- |
- // True iff the server returned results in this category in the last fetch. |
- // We never remove categories that the server still provides, but if the |
- // server stops providing a category, we won't yet report it as NOT_PROVIDED |
- // while we still have non-expired snippets in it. |
- bool included_in_last_server_response = true; |
- |
- // All currently active suggestions (excl. the dismissed ones). |
- NTPSnippet::PtrVector snippets; |
- |
- // All previous suggestions that we keep around in memory because they can |
- // be on some open NTP. We do not persist this list so that on a new start |
- // of Chrome, this is empty. |
- // |archived| is a FIFO buffer with a maximum length. |
- std::deque<std::unique_ptr<NTPSnippet>> archived; |
- |
- // Suggestions that the user dismissed. We keep these around until they |
- // expire so we won't re-add them to |snippets| on the next fetch. |
- NTPSnippet::PtrVector dismissed; |
- |
- // Returns a non-dismissed snippet with the given |id_within_category|, or |
- // null if none exist. |
- const NTPSnippet* FindSnippet(const std::string& id_within_category) const; |
- |
- explicit CategoryContent(const CategoryInfo& info); |
- CategoryContent(CategoryContent&&); |
- ~CategoryContent(); |
- CategoryContent& operator=(CategoryContent&&); |
- }; |
- |
- // Fetches snippets from the server and replaces old snippets by the new ones. |
- // Requests can be marked more important by setting |interactive_request| to |
- // true (such request might circumvent the daily quota for requests, etc.) |
- // Useful for requests triggered by the user. |
- void FetchSnippets(bool interactive_request); |
- |
- // Returns the URL of the image of a snippet if it is among the current or |
- // among the archived snippets in the matching category. Returns an empty URL |
- // otherwise. |
- GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const; |
- |
- // Callbacks for the RemoteSuggestionsDatabase. |
- void OnDatabaseLoaded(NTPSnippet::PtrVector snippets); |
- void OnDatabaseError(); |
- |
- // Callback for fetch-more requests with the NTPSnippetsFetcher. |
- void OnFetchMoreFinished( |
- const FetchDoneCallback& fetching_callback, |
- Status status, |
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); |
- |
- // Callback for regular fetch requests with the NTPSnippetsFetcher. |
- void OnFetchFinished( |
- bool interactive_request, |
- Status status, |
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); |
- |
- // Moves all snippets from |to_archive| into the archive of the |content|. |
- // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this |
- // function will also delete images from the database in case the associated |
- // snippet gets evicted from the archive. |
- void ArchiveSnippets(CategoryContent* content, |
- NTPSnippet::PtrVector* to_archive); |
- |
- // Sanitizes newly fetched snippets -- e.g. adding missing dates and filtering |
- // out incomplete results or dismissed snippets (indicated by |dismissed|). |
- void SanitizeReceivedSnippets(const NTPSnippet::PtrVector& dismissed, |
- NTPSnippet::PtrVector* snippets); |
- |
- // Adds newly available suggestions to |content|. |
- void IntegrateSnippets(CategoryContent* content, |
- NTPSnippet::PtrVector new_snippets); |
- |
- // Dismisses a snippet within a given category content. |
- // Note that this modifies the snippet datastructures of |content| |
- // invalidating iterators. |
- void DismissSuggestionFromCategoryContent( |
- CategoryContent* content, |
- const std::string& id_within_category); |
- |
- // Removes expired dismissed snippets from the service and the database. |
- void ClearExpiredDismissedSnippets(); |
- |
- // Removes images from the DB that are not referenced from any known snippet. |
- // Needs to iterate the whole snippet database -- so do it often enough to |
- // keep it small but not too often as it still iterates over the file system. |
- void ClearOrphanedImages(); |
- |
- // Clears all stored snippets and updates the observer. |
- void NukeAllSnippets(); |
- |
- // Completes the initialization phase of the service, registering the last |
- // observers. This is done after construction, once the database is loaded. |
- void FinishInitialization(); |
- |
- // Triggers a state transition depending on the provided status. This method |
- // is called when a change is detected by |status_service_|. |
- void OnStatusChanged(RemoteSuggestionsStatus old_status, |
- RemoteSuggestionsStatus new_status); |
- |
- // Verifies state transitions (see |State|'s documentation) and applies them. |
- // Also updates the provider status. Does nothing except updating the provider |
- // status if called with the current state. |
- void EnterState(State state); |
- |
- // Enables the service. Do not call directly, use |EnterState| instead. |
- void EnterStateReady(); |
- |
- // Disables the service. Do not call directly, use |EnterState| instead. |
- void EnterStateDisabled(); |
- |
- // Disables the service permanently because an unrecoverable error occurred. |
- // Do not call directly, use |EnterState| instead. |
- void EnterStateError(); |
- |
- // Converts the cached snippets in the given |category| to content suggestions |
- // and notifies the observer. |
- void NotifyNewSuggestions(Category category, const CategoryContent& content); |
- |
- // Updates the internal status for |category| to |category_status_| and |
- // notifies the content suggestions observer if it changed. |
- void UpdateCategoryStatus(Category category, CategoryStatus status); |
- // Calls UpdateCategoryStatus() for all provided categories. |
- void UpdateAllCategoryStatus(CategoryStatus status); |
- |
- // Updates the category info for |category|. If a corresponding |
- // CategoryContent object does not exist, it will be created. |
- // Returns the existing or newly created object. |
- CategoryContent* UpdateCategoryInfo(Category category, |
- const CategoryInfo& info); |
- |
- void RestoreCategoriesFromPrefs(); |
- void StoreCategoriesToPrefs(); |
- |
- NTPSnippetsRequestParams BuildFetchParams() const; |
- |
- void MarkEmptyCategoriesAsLoading(); |
- |
- State state_; |
- |
- PrefService* pref_service_; |
- |
- const Category articles_category_; |
- |
- std::map<Category, CategoryContent, Category::CompareByID> category_contents_; |
- |
- // The ISO 639-1 code of the language used by the application. |
- const std::string application_language_code_; |
- |
- // Ranker that orders the categories. Not owned. |
- CategoryRanker* category_ranker_; |
- |
- // Classifier that tells us how active the user is. Not owned. |
- const UserClassifier* user_classifier_; |
- |
- // Scheduler for fetching snippets. Not owned. |
- NTPSnippetsScheduler* scheduler_; |
- |
- // The snippets fetcher. |
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_; |
- |
- // The database for persisting snippets. |
- std::unique_ptr<RemoteSuggestionsDatabase> database_; |
- base::TimeTicks database_load_start_; |
- |
- // The image fetcher. |
- CachedImageFetcher image_fetcher_; |
- |
- // The service that provides events and data about the signin and sync state. |
- std::unique_ptr<RemoteSuggestionsStatusService> status_service_; |
- |
- // Set to true if FetchSnippets is called while the service isn't ready. |
- // The fetch will be executed once the service enters the READY state. |
- bool fetch_when_ready_; |
- |
- // Set to true if NukeAllSnippets is called while the service isn't ready. |
- // The nuke will be executed once the service finishes initialization or |
- // enters the READY state. |
- bool nuke_when_initialized_; |
- |
- // A clock for getting the time. This allows to inject a clock in tests. |
- std::unique_ptr<base::Clock> clock_; |
- |
- DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProvider); |
+ // Set a callback to be notified whenever the status of the provider changes. |
+ // The initial change is also notified (switching from an initial undecided |
+ // status). If the callback is set after the first change, it is called back |
+ // immediately. |
+ virtual void SetProviderStatusCallback( |
+ std::unique_ptr<ProviderStatusCallback> callback) = 0; |
+ |
+ // Fetches snippets from the server for all remote categories and replaces old |
+ // snippets by the new ones. The request to the server is performed as an |
+ // background request. Background requests are used for actions not triggered |
+ // by the user and have lower priority on the server. After the fetch |
+ // finished, the provided |callback| will be triggered with the status of the |
+ // fetch (unless not nullptr). |
+ virtual void RefetchInTheBackground( |
+ std::unique_ptr<FetchStatusCallback> callback) = 0; |
+ |
+ virtual const NTPSnippetsFetcher* snippets_fetcher_for_testing_and_debugging() |
+ const = 0; |
+ |
+ protected: |
+ RemoteSuggestionsProvider(Observer* observer); |
}; |
} // namespace ntp_snippets |