content_suggestions_service.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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
  3. // found in the LICENSE file.
  4. #ifndef COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTIONS_SERVICE_H_
  5. #define COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTIONS_SERVICE_H_
  6. #include <map>
  7. #include <memory>
  8. #include <set>
  9. #include <string>
  10. #include <vector>
  11. #include "base/callback_forward.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "base/observer_list.h"
  14. #include "base/scoped_observation.h"
  15. #include "base/task/cancelable_task_tracker.h"
  16. #include "base/time/time.h"
  17. #include "components/history/core/browser/history_service.h"
  18. #include "components/history/core/browser/history_service_observer.h"
  19. #include "components/keyed_service/core/keyed_service.h"
  20. #include "components/ntp_snippets/callbacks.h"
  21. #include "components/ntp_snippets/category.h"
  22. #include "components/ntp_snippets/category_rankers/category_ranker.h"
  23. #include "components/ntp_snippets/category_status.h"
  24. #include "components/ntp_snippets/content_suggestions_provider.h"
  25. #include "components/ntp_snippets/remote/remote_suggestions_scheduler.h"
  26. #include "components/ntp_snippets/user_classifier.h"
  27. #include "components/signin/public/identity_manager/identity_manager.h"
  28. #include "third_party/abseil-cpp/absl/types/optional.h"
  29. class PrefService;
  30. class PrefRegistrySimple;
  31. namespace favicon {
  32. class LargeIconService;
  33. } // namespace favicon
  34. namespace favicon_base {
  35. struct LargeIconImageResult;
  36. } // namespace favicon_base
  37. namespace ntp_snippets {
  38. class RemoteSuggestionsProvider;
  39. // Retrieves suggestions from a number of ContentSuggestionsProviders and serves
  40. // them grouped into categories. There can be at most one provider per category.
  41. class ContentSuggestionsService : public KeyedService,
  42. public ContentSuggestionsProvider::Observer,
  43. public signin::IdentityManager::Observer,
  44. public history::HistoryServiceObserver {
  45. public:
  46. class Observer {
  47. public:
  48. // Fired every time the service receives a new set of data for the given
  49. // |category|, replacing any previously available data (though in most cases
  50. // there will be an overlap and only a few changes within the data). The new
  51. // data is then available through |GetSuggestionsForCategory(category)|.
  52. virtual void OnNewSuggestions(Category category) = 0;
  53. // Fired when the status of a suggestions category changed. Note that for
  54. // some status changes, the UI must update immediately (e.g. to remove
  55. // invalidated suggestions). See comments on the individual CategoryStatus
  56. // values for details.
  57. virtual void OnCategoryStatusChanged(Category category,
  58. CategoryStatus new_status) = 0;
  59. // Fired when a suggestion has been invalidated. The UI must immediately
  60. // clear the suggestion even from open NTPs. Invalidation happens, for
  61. // example, when the content that the suggestion refers to is gone.
  62. // Note that this event may be fired even if the corresponding category is
  63. // not currently AVAILABLE, because open UIs may still be showing the
  64. // suggestion that is to be removed. This event may also be fired for
  65. // |suggestion_id|s that never existed and should be ignored in that case.
  66. virtual void OnSuggestionInvalidated(
  67. const ContentSuggestion::ID& suggestion_id) = 0;
  68. // Fired when the previously sent data is not valid anymore and a refresh
  69. // of all the suggestions is required. Called for example when the sign in
  70. // state changes and personalised suggestions have to be shown or discarded.
  71. virtual void OnFullRefreshRequired() = 0;
  72. // Sent when the service is shutting down. After the service has shut down,
  73. // it will not provide any data anymore, though calling the getters is still
  74. // safe.
  75. virtual void ContentSuggestionsServiceShutdown() = 0;
  76. protected:
  77. virtual ~Observer() = default;
  78. };
  79. enum class State {
  80. ENABLED,
  81. DISABLED,
  82. };
  83. ContentSuggestionsService(
  84. State state,
  85. signin::IdentityManager*
  86. identity_manager, // Can be nullptr in unittests.
  87. history::HistoryService* history_service, // Can be nullptr in unittests.
  88. // Can be nullptr in unittests.
  89. favicon::LargeIconService* large_icon_service,
  90. PrefService* pref_service,
  91. std::unique_ptr<CategoryRanker> category_ranker,
  92. std::unique_ptr<UserClassifier> user_classifier,
  93. std::unique_ptr<RemoteSuggestionsScheduler>
  94. remote_suggestions_scheduler); // Can be nullptr in unittests.
  95. ContentSuggestionsService(const ContentSuggestionsService&) = delete;
  96. ContentSuggestionsService& operator=(const ContentSuggestionsService&) =
  97. delete;
  98. ~ContentSuggestionsService() override;
  99. // Inherited from KeyedService.
  100. void Shutdown() override;
  101. static void RegisterProfilePrefs(PrefRegistrySimple* registry);
  102. State state() { return state_; }
  103. // Gets all categories for which a provider is registered. The categories may
  104. // or may not be available, see |GetCategoryStatus()|. The order in which the
  105. // categories are returned is the order in which they should be displayed.
  106. std::vector<Category> GetCategories() const;
  107. // Gets the status of a category.
  108. CategoryStatus GetCategoryStatus(Category category) const;
  109. // Gets the meta information of a category.
  110. absl::optional<CategoryInfo> GetCategoryInfo(Category category) const;
  111. // Gets the available suggestions for a category. The result is empty if the
  112. // category is available and empty, but also if the category is unavailable
  113. // for any reason, see |GetCategoryStatus()|.
  114. const std::vector<ContentSuggestion>& GetSuggestionsForCategory(
  115. Category category) const;
  116. // Fetches the image for the suggestion with the given |suggestion_id| and
  117. // runs the |callback|. If that suggestion doesn't exist or the fetch fails,
  118. // the callback gets an empty image. The callback will not be called
  119. // synchronously.
  120. void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id,
  121. ImageFetchedCallback callback);
  122. // Fetches the image data for the suggestion with the given |suggestion_id|
  123. // and runs the |callback|. If that suggestion doesn't exist or the fetch
  124. // fails, the callback gets empty data. The callback will not be called
  125. // synchronously.
  126. void FetchSuggestionImageData(const ContentSuggestion::ID& suggestion_id,
  127. ImageDataFetchedCallback callback);
  128. // Fetches the favicon from local cache (if larger than or equal to
  129. // |minimum_size_in_pixel|) or from Google server (if there is no icon in the
  130. // cache) and returns the results in the callback. If that suggestion doesn't
  131. // exist or the fetch fails, the callback gets an empty image. The callback
  132. // will not be called synchronously.
  133. void FetchSuggestionFavicon(const ContentSuggestion::ID& suggestion_id,
  134. int minimum_size_in_pixel,
  135. int desired_size_in_pixel,
  136. ImageFetchedCallback callback);
  137. // Dismisses the suggestion with the given |suggestion_id|, if it exists.
  138. // This will not trigger an update through the observers (i.e. providers must
  139. // not call |Observer::OnNewSuggestions|).
  140. void DismissSuggestion(const ContentSuggestion::ID& suggestion_id);
  141. // Dismisses the given |category|, if it exists.
  142. // This will not trigger an update through the observers.
  143. void DismissCategory(Category category);
  144. // Restores all dismissed categories.
  145. // This will not trigger an update through the observers.
  146. void RestoreDismissedCategories();
  147. // Returns whether |category| is dismissed.
  148. bool IsCategoryDismissed(Category category) const;
  149. // Fetches additional contents for the given |category|. If the fetch was
  150. // completed, the given |callback| is called with the updated content.
  151. // This includes new and old data.
  152. // TODO(jkrcal): Consider either renaming this to FetchMore or unify the ways
  153. // to get suggestions to just this async Fetch() API.
  154. void Fetch(const Category& category,
  155. const std::set<std::string>& known_suggestion_ids,
  156. FetchDoneCallback callback);
  157. // Reloads suggestions from all categories, from all providers. If a provider
  158. // naturally has some ability to generate fresh suggestions, it may provide a
  159. // completely new set of suggestions. If the provider has no ability to
  160. // generate fresh suggestions on demand, it may only fill in any vacant space
  161. // by suggestions that were previously not included due to space limits (there
  162. // may be vacant space because of the user dismissing suggestions in the
  163. // meantime).
  164. void ReloadSuggestions();
  165. // Observer accessors.
  166. void AddObserver(Observer* observer);
  167. void RemoveObserver(Observer* observer);
  168. // Registers a new ContentSuggestionsProvider. It must be ensured that at most
  169. // one provider is registered for every category and that this method is
  170. // called only once per provider.
  171. void RegisterProvider(std::unique_ptr<ContentSuggestionsProvider> provider);
  172. // Removes history from the specified time range where the URL matches the
  173. // |filter| from all providers. The data removed depends on the provider. Note
  174. // that the data outside the time range may be deleted, for example
  175. // suggestions, which are based on history from that time range. Providers
  176. // should immediately clear any data related to history from the specified
  177. // time range where the URL matches the |filter|.
  178. void ClearHistory(
  179. base::Time begin,
  180. base::Time end,
  181. const base::RepeatingCallback<bool(const GURL& url)>& filter);
  182. // Removes all suggestions from all caches or internal stores in all
  183. // providers. It does, however, not remove any suggestions from the provider's
  184. // sources, so if its configuration hasn't changed, it might return the same
  185. // results when it fetches the next time. In particular, calling this method
  186. // will not mark any suggestions as dismissed.
  187. void ClearAllCachedSuggestions();
  188. // Only for debugging use through the internals page.
  189. // Retrieves suggestions of the given |category| that have previously been
  190. // dismissed and are still stored in the respective provider. If the
  191. // provider doesn't store dismissed suggestions, the callback receives an
  192. // empty vector. The callback may be called synchronously.
  193. void GetDismissedSuggestionsForDebugging(
  194. Category category,
  195. DismissedSuggestionsCallback callback);
  196. // Only for debugging use through the internals page. Some providers
  197. // internally store a list of dismissed suggestions to prevent them from
  198. // reappearing. This function clears all suggestions of the given |category|
  199. // from such lists, making dismissed suggestions reappear (if the provider
  200. // supports it).
  201. void ClearDismissedSuggestionsForDebugging(Category category);
  202. // Returns true if the remote suggestions provider is enabled.
  203. bool AreRemoteSuggestionsEnabled() const;
  204. // The reference to the RemoteSuggestionsProvider provider should
  205. // only be set by the factory and only used for debugging.
  206. // TODO(jkrcal) The way we deal with the circular dependency feels wrong.
  207. // Consider swapping the dependencies: first constructing all providers, then
  208. // constructing the service (passing the remote provider as arg), finally
  209. // registering the service as an observer of all providers?
  210. // TODO(jkrcal) Move the getter into the scheduler interface (the setter is
  211. // then not needed any more). crbug.com/695447
  212. void set_remote_suggestions_provider(
  213. RemoteSuggestionsProvider* remote_suggestions_provider) {
  214. remote_suggestions_provider_ = remote_suggestions_provider;
  215. }
  216. RemoteSuggestionsProvider* remote_suggestions_provider_for_debugging() {
  217. return remote_suggestions_provider_;
  218. }
  219. // The interface is suited for informing about external events that have
  220. // influence on scheduling remote fetches. Can be nullptr in tests.
  221. RemoteSuggestionsScheduler* remote_suggestions_scheduler() {
  222. return remote_suggestions_scheduler_.get();
  223. }
  224. // Can be nullptr in tests.
  225. // TODO(jkrcal): The getter is only used from the bridge and from
  226. // snippets-internals. Can we get rid of it with the metrics refactoring?
  227. UserClassifier* user_classifier() { return user_classifier_.get(); }
  228. CategoryRanker* category_ranker() { return category_ranker_.get(); }
  229. private:
  230. friend class ContentSuggestionsServiceTest;
  231. // Implementation of ContentSuggestionsProvider::Observer.
  232. void OnNewSuggestions(ContentSuggestionsProvider* provider,
  233. Category category,
  234. std::vector<ContentSuggestion> suggestions) override;
  235. void OnCategoryStatusChanged(ContentSuggestionsProvider* provider,
  236. Category category,
  237. CategoryStatus new_status) override;
  238. void OnSuggestionInvalidated(
  239. ContentSuggestionsProvider* provider,
  240. const ContentSuggestion::ID& suggestion_id) override;
  241. // signin::IdentityManager::Observer implementation.
  242. void OnPrimaryAccountChanged(
  243. const signin::PrimaryAccountChangeEvent& event_details) override;
  244. // history::HistoryServiceObserver implementation.
  245. void OnURLsDeleted(history::HistoryService* history_service,
  246. const history::DeletionInfo& deletion_info) override;
  247. void HistoryServiceBeingDeleted(
  248. history::HistoryService* history_service) override;
  249. // Registers the given |provider| for the given |category|, unless it is
  250. // already registered. Returns true if the category was newly registered or
  251. // false if it is dismissed or was present before.
  252. bool TryRegisterProviderForCategory(ContentSuggestionsProvider* provider,
  253. Category category);
  254. void RegisterCategory(Category category,
  255. ContentSuggestionsProvider* provider);
  256. void UnregisterCategory(Category category,
  257. ContentSuggestionsProvider* provider);
  258. // Removes a suggestion from the local store |suggestions_by_category_|, if it
  259. // exists. Returns true if a suggestion was removed.
  260. bool RemoveSuggestionByID(const ContentSuggestion::ID& suggestion_id);
  261. // Fires the OnCategoryStatusChanged event for the given |category|.
  262. void NotifyCategoryStatusChanged(Category category);
  263. void OnSignInStateChanged(bool has_signed_in);
  264. // Re-enables a dismissed category, making querying its provider possible.
  265. void RestoreDismissedCategory(Category category);
  266. void RestoreDismissedCategoriesFromPrefs();
  267. void StoreDismissedCategoriesToPrefs();
  268. // Not implemented for articles. For all other categories, destroys its
  269. // provider, deletes all mentions (except from dismissed list) and notifies
  270. // observers that the category is disabled.
  271. void DestroyCategoryAndItsProvider(Category category);
  272. // Get the domain of the suggestion suitable for fetching the favicon.
  273. GURL GetFaviconDomain(const ContentSuggestion::ID& suggestion_id);
  274. // Initiate the fetch of a favicon from the local cache.
  275. void GetFaviconFromCache(const GURL& publisher_url,
  276. int minimum_size_in_pixel,
  277. int desired_size_in_pixel,
  278. ImageFetchedCallback callback,
  279. bool continue_to_google_server);
  280. // Callbacks for fetching favicons.
  281. void OnGetFaviconFromCacheFinished(
  282. const GURL& publisher_url,
  283. int minimum_size_in_pixel,
  284. int desired_size_in_pixel,
  285. ImageFetchedCallback callback,
  286. bool continue_to_google_server,
  287. const favicon_base::LargeIconImageResult& result);
  288. void OnGetFaviconFromGoogleServerFinished(
  289. const GURL& publisher_url,
  290. int minimum_size_in_pixel,
  291. int desired_size_in_pixel,
  292. ImageFetchedCallback callback,
  293. favicon_base::GoogleFaviconServerRequestStatus status);
  294. // Whether the content suggestions feature is enabled.
  295. State state_;
  296. // All registered providers, owned by the service.
  297. std::vector<std::unique_ptr<ContentSuggestionsProvider>> providers_;
  298. // All registered categories and their providers. A provider may be contained
  299. // multiple times, if it provides multiple categories. The keys of this map
  300. // are exactly the entries of |categories_| and the values are a subset of
  301. // |providers_|.
  302. std::map<Category, ContentSuggestionsProvider*, Category::CompareByID>
  303. providers_by_category_;
  304. // All dismissed categories and their providers. These may be restored by
  305. // RestoreDismissedCategories(). The provider can be null if the dismissed
  306. // category has received no updates since initialisation.
  307. // (see RestoreDismissedCategoriesFromPrefs())
  308. std::map<Category, ContentSuggestionsProvider*, Category::CompareByID>
  309. dismissed_providers_by_category_;
  310. // All current suggestion categories in arbitrary order. This vector contains
  311. // exactly the same categories as |providers_by_category_|.
  312. std::vector<Category> categories_;
  313. // All current suggestions grouped by category. This contains an entry for
  314. // every category in |categories_| whose status is an available status. It may
  315. // contain an empty vector if the category is available but empty (or still
  316. // loading).
  317. std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID>
  318. suggestions_by_category_;
  319. // Observer for the IdentityManager. All observers are notified when the
  320. // signin state changes so that they can refresh their list of suggestions.
  321. base::ScopedObservation<signin::IdentityManager,
  322. signin::IdentityManager::Observer>
  323. identity_manager_observation_{this};
  324. // Observer for the HistoryService. All providers are notified when history is
  325. // deleted.
  326. base::ScopedObservation<history::HistoryService,
  327. history::HistoryServiceObserver>
  328. history_service_observation_{this};
  329. base::ObserverList<Observer>::Unchecked observers_;
  330. const std::vector<ContentSuggestion> no_suggestions_;
  331. base::CancelableTaskTracker favicons_task_tracker_;
  332. // Keep a direct reference to this special provider to redirect debugging
  333. // calls to it. If the RemoteSuggestionsProvider is loaded, it is also present
  334. // in |providers_|, otherwise this is a nullptr.
  335. raw_ptr<RemoteSuggestionsProvider> remote_suggestions_provider_;
  336. raw_ptr<favicon::LargeIconService> large_icon_service_;
  337. raw_ptr<PrefService> pref_service_;
  338. // Interface for informing about external events that have influence on
  339. // scheduling remote fetches.
  340. std::unique_ptr<RemoteSuggestionsScheduler> remote_suggestions_scheduler_;
  341. // Classifies the user on the basis of long-term user interactions.
  342. std::unique_ptr<UserClassifier> user_classifier_;
  343. // Provides order for categories.
  344. std::unique_ptr<CategoryRanker> category_ranker_;
  345. };
  346. } // namespace ntp_snippets
  347. #endif // COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTIONS_SERVICE_H_