blob: f54a0f7d74885e927faff5d358fd1249a3214918 [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.
#include <set>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/download/download_history.h"
#include "components/ntp_snippets/callbacks.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/offline_pages/core/offline_page_model.h"
#include "content/public/browser/download_manager.h"
class PrefRegistrySimple;
class PrefService;
namespace offline_pages {
struct OfflinePageItem;
namespace base {
class Clock;
// Provides download content suggestions from the offline pages model and the
// download manager (obtaining the data through DownloadManager and each
// DownloadItem). Offline page related downloads are referred to as offline page
// downloads, while the remaining downloads (e.g. images, music, books) are
// called asset downloads. In case either of the data sources is |nullptr|, it
// is ignored.
class DownloadSuggestionsProvider
: public ntp_snippets::ContentSuggestionsProvider,
public offline_pages::OfflinePageModel::Observer,
public content::DownloadManager::Observer,
public download::DownloadItem::Observer,
public DownloadHistory::Observer {
ContentSuggestionsProvider::Observer* observer,
offline_pages::OfflinePageModel* offline_page_model,
content::DownloadManager* download_manager,
DownloadHistory* download_history,
PrefService* pref_service,
base::Clock* clock);
~DownloadSuggestionsProvider() override;
// ContentSuggestionsProvider implementation.
ntp_snippets::CategoryStatus GetCategoryStatus(
ntp_snippets::Category category) override;
ntp_snippets::CategoryInfo GetCategoryInfo(
ntp_snippets::Category category) override;
void DismissSuggestion(
const ntp_snippets::ContentSuggestion::ID& suggestion_id) override;
void FetchSuggestionImage(
const ntp_snippets::ContentSuggestion::ID& suggestion_id,
ntp_snippets::ImageFetchedCallback callback) override;
void FetchSuggestionImageData(
const ntp_snippets::ContentSuggestion::ID& suggestion_id,
ntp_snippets::ImageDataFetchedCallback callback) override;
void Fetch(const ntp_snippets::Category& category,
const std::set<std::string>& known_suggestion_ids,
ntp_snippets::FetchDoneCallback callback) override;
void ClearHistory(
base::Time begin,
base::Time end,
const base::Callback<bool(const GURL& url)>& filter) override;
void ClearCachedSuggestions() override;
void GetDismissedSuggestionsForDebugging(
ntp_snippets::Category category,
ntp_snippets::DismissedSuggestionsCallback callback) override;
void ClearDismissedSuggestionsForDebugging(
ntp_snippets::Category category) override;
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
friend class DownloadSuggestionsProviderTest;
void GetPagesMatchingQueryCallbackForGetDismissedSuggestions(
ntp_snippets::DismissedSuggestionsCallback callback,
const std::vector<offline_pages::OfflinePageItem>& offline_pages) const;
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
void OfflinePageAdded(
offline_pages::OfflinePageModel* model,
const offline_pages::OfflinePageItem& added_page) override;
void OfflinePageDeleted(
const offline_pages::OfflinePageModel::DeletedPageInfo& page_info)
// content::DownloadManager::Observer implementation.
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override;
void ManagerGoingDown(content::DownloadManager* manager) override;
// download::DownloadItem::Observer implementation.
void OnDownloadUpdated(download::DownloadItem* item) override;
void OnDownloadOpened(download::DownloadItem* item) override;
void OnDownloadRemoved(download::DownloadItem* item) override;
void OnDownloadDestroyed(download::DownloadItem* item) override;
// DownloadHistory::Observer implementation.
void OnHistoryQueryComplete() override;
void OnDownloadHistoryDestroyed() override;
// Updates the |category_status_| of the |provided_category_| and notifies the
// |observer_|, if necessary.
void NotifyStatusChanged(ntp_snippets::CategoryStatus new_status);
// Requests all offline pages and after asynchronously obtaining the result,
// prunes dismissed IDs and caches some most recent items. If |notify| is
// true, notifies |ContentSuggestionsProvider::Observer| about them.
void AsynchronouslyFetchOfflinePagesDownloads(bool notify);
// Retrieves all asset downloads, prunes dismissed IDs and caches some most
// recent items, but does not notify |ContentSuggestionsProvider::Observer|
// about them.
void FetchAssetsDownloads();
// Retrieves both offline page and asset downloads, updates the internal cache
// and notifies |ContentSuggestionsProvider::Observer|.
void AsynchronouslyFetchAllDownloadsAndSubmitSuggestions();
// Takes |kMaxSuggestionsCount| the most recent cached suggestions and
// notifies |ContentSuggestionsProvider::Observer| about them.
void SubmitContentSuggestions();
// Converts an OfflinePageItem to a ContentSuggestion for the
// |provided_category_|.
ntp_snippets::ContentSuggestion ConvertOfflinePage(
const offline_pages::OfflinePageItem& offline_page) const;
// Converts DownloadItem to a ContentSuggestion for the |provided_category_|.
ntp_snippets::ContentSuggestion ConvertDownloadItem(
const download::DownloadItem& download_item) const;
// Returns true if a download published and last visited times are considered
// too old for the download to be shown.
bool IsDownloadOutdated(const base::Time& published_time,
const base::Time& last_visited_time);
// Adds |item| to the internal asset download cache if all of the following
// holds:
// - the download is completed;
// - its suggestion has not been dismissed;
// - there are less than |kMaxSuggestionsCount| items cached or the oldest
// cached item is older than the current item (the oldest item is removed
// then);
// - the item is not present in the cache yet.
// Returns |true| if the item has been added.
bool CacheAssetDownloadIfNeeded(const download::DownloadItem* item);
// Removes item corresponding to |suggestion_id| either from offline pages or
// asset download cache (depends on the |suggestion_id|). Returns |false| if
// there is no corresponding item in the cache and so nothing has been
// removed.
bool RemoveSuggestionFromCacheIfPresent(
const ntp_snippets::ContentSuggestion::ID& suggestion_id);
// Removes item corresponding to |suggestion_id| from cache and fetches all
// corresponding downloads to update the cache if there may be any not in
// cache.
void RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(
const ntp_snippets::ContentSuggestion::ID& suggestion_id);
// Processes a list of offline pages (assuming that these are all the download
// offline pages that currently exist), prunes dismissed IDs and updates
// internal cache. If |notify| is true, notifies
// |ContentSuggestionsProvider::Observer|.
void UpdateOfflinePagesCache(
bool notify,
const std::vector<offline_pages::OfflinePageItem>&
// Fires the |OnSuggestionInvalidated| event for the suggestion corresponding
// to the given |id_within_category| and clears it from the dismissed IDs
// list, if necessary.
void InvalidateSuggestion(const std::string& id_within_category);
// Reads dismissed IDs related to asset downloads from prefs.
std::set<std::string> ReadAssetDismissedIDsFromPrefs() const;
// Writes |dismissed_ids| into prefs for asset downloads.
void StoreAssetDismissedIDsToPrefs(
const std::set<std::string>& dismissed_ids);
// Reads dismissed IDs related to offline page downloads from prefs.
std::set<std::string> ReadOfflinePageDismissedIDsFromPrefs() const;
// Writes |dismissed_ids| into prefs for offline page downloads.
void StoreOfflinePageDismissedIDsToPrefs(
const std::set<std::string>& dismissed_ids);
// Reads from prefs dismissed IDs related to either offline page or asset
// downloads (given by |for_offline_page_downloads|).
std::set<std::string> ReadDismissedIDsFromPrefs(
bool for_offline_page_downloads) const;
// Writes |dismissed_ids| into prefs for either offline page or asset
// downloads (given by |for_offline_page_downloads|).
void StoreDismissedIDsToPrefs(bool for_offline_page_downloads,
const std::set<std::string>& dismissed_ids);
void UnregisterDownloadItemObservers();
ntp_snippets::CategoryStatus category_status_;
const ntp_snippets::Category provided_category_;
offline_pages::OfflinePageModel* offline_page_model_;
content::DownloadManager* download_manager_;
DownloadHistory* download_history_;
PrefService* pref_service_;
base::Clock* clock_;
// Cached offline page downloads. If there are not enough asset downloads, all
// of these could be shown (they are the most recently visited, not dismissed
// and not invalidated). Order is undefined. If the model has less than
// |kMaxSuggestionsCount| offline pages, then all of them which satisfy the
// criteria above are cached, otherwise only |kMaxSuggestionsCount|.
std::vector<offline_pages::OfflinePageItem> cached_offline_page_downloads_;
// Cached asset downloads. If there are not enough offline page downloads, all
// of these could be shown (they are the most recently downloaded, not
// dismissed and not invalidated). Order is undefined. If the model has less
// than |kMaxSuggestionsCount| asset downloads, then all of them which satisfy
// the criteria above are cached, otherwise only |kMaxSuggestionsCount|.
std::vector<const download::DownloadItem*> cached_asset_downloads_;
bool is_asset_downloads_initialization_complete_;
base::WeakPtrFactory<DownloadSuggestionsProvider> weak_ptr_factory_;