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