blob: 6e8058585639ae411e4b9e82b006c7e5197c395b [file] [log] [blame]
// Copyright 2018 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_OMNIBOX_BROWSER_FAVICON_CACHE_H_
#define COMPONENTS_OMNIBOX_BROWSER_FAVICON_CACHE_H_
#include <list>
#include <map>
#include "base/callback_forward.h"
#include "base/callback_list.h"
#include "base/containers/mru_cache.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/cancelable_task_tracker.h"
#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/history/core/browser/history_types.h"
namespace favicon {
class FaviconService;
}
namespace gfx {
class Image;
}
class GURL;
typedef base::OnceCallback<void(const gfx::Image& favicon)>
FaviconFetchedCallback;
// This caches favicons by both page URL and icon URL. We cache a small number
// of them so we can synchronously deliver them to the UI to prevent flicker as
// the user types.
//
// This class also observes the HistoryService, and invalidates cached favicons
// and null responses when matching favicons are updated.
class FaviconCache : public history::HistoryServiceObserver {
public:
FaviconCache(favicon::FaviconService* favicon_service,
history::HistoryService* history_service);
~FaviconCache() override;
FaviconCache(const FaviconCache&) = delete;
FaviconCache& operator=(const FaviconCache&) = delete;
// These methods fetch favicons by the |page_url| or |icon_url| respectively.
// If the correct favicon is already cached, these methods return the image
// synchronously.
//
// If the correct favicon is not cached, we return an empty gfx::Image and
// forward the request to FaviconService. |on_favicon_fetched| is stored in a
// pending callback list, and subsequent identical requests are added to the
// same pending list without issuing duplicate calls to FaviconService.
//
// If FaviconService responds with a non-empty image, we fulfill all the
// matching |on_favicon_fetched| callbacks in the pending list, and cache the
// result so that future matching requests can be fulfilled synchronously.
//
// If FaviconService responds with an empty image (because the correct favicon
// isn't in our database), we simply erase all the pending callbacks, and also
// cache the result.
//
// Therefore, |on_favicon_fetched| may or may not be called asynchronously
// later, but will never be called with an empty result. It will also never
// be called synchronously.
//
// Note that GetFaviconForPageUrl and GetLargestFaviconForPageUrl should not
// be used interchangeably. These methods use the same |page_url| key for
// caching favicons and as a result may return favicons with the wrong size if
// called with the same |page_url|.
gfx::Image GetFaviconForPageUrl(const GURL& page_url,
FaviconFetchedCallback on_favicon_fetched);
gfx::Image GetLargestFaviconForPageUrl(
const GURL& page_url,
FaviconFetchedCallback on_favicon_fetched);
gfx::Image GetFaviconForIconUrl(const GURL& icon_url,
FaviconFetchedCallback on_favicon_fetched);
private:
FRIEND_TEST_ALL_PREFIXES(FaviconCacheTest, ClearIconsWithHistoryDeletions);
FRIEND_TEST_ALL_PREFIXES(FaviconCacheTest, ExpireNullFaviconsByHistory);
FRIEND_TEST_ALL_PREFIXES(FaviconCacheTest, ObserveFaviconsChanged);
enum class RequestType {
BY_PAGE_URL,
BY_ICON_URL,
RAW_BY_PAGE_URL,
};
struct Request {
RequestType type;
GURL url;
// This operator is defined to support using Request as a key of std::map.
bool operator<(const Request& rhs) const;
};
// Internal method backing GetFaviconForPageUrl and GetFaviconForIconUrl.
gfx::Image GetFaviconInternal(const Request& request,
FaviconFetchedCallback on_favicon_fetched);
// These are the callbacks passed to the underlying FaviconService. When these
// are called, all the pending requests that match |request| will be called.
void OnFaviconFetched(const Request& request,
const favicon_base::FaviconImageResult& result);
void OnFaviconRawBitmapFetched(
const Request& request,
const favicon_base::FaviconRawBitmapResult& bitmap_result);
// Invokes all the pending requests that match |request| with |image|.
void InvokeRequestCallbackWithFavicon(const Request& request,
const gfx::Image& image);
// Removes cached favicons and null responses that match |request| from the
// cache. Subsequent matching requests pull fresh data from FaviconService.
void InvalidateCachedRequests(const Request& request);
// history::HistoryServiceObserver:
void OnURLVisited(history::HistoryService* history_service,
ui::PageTransition transition,
const history::URLRow& row,
const history::RedirectList& redirects,
base::Time visit_time) override;
void OnURLsDeleted(history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) override;
void OnFaviconsChanged(const std::set<GURL>& page_urls, const GURL& icon_url);
// Non-owning pointer to a KeyedService.
favicon::FaviconService* favicon_service_;
base::ScopedObservation<history::HistoryService,
history::HistoryServiceObserver>
history_observation_{this};
base::CancelableTaskTracker task_tracker_;
std::map<Request, std::list<FaviconFetchedCallback>> pending_requests_;
base::MRUCache<Request, gfx::Image> mru_cache_;
// Keep responses with empty favicons in a separate list, to prevent a
// response with an empty favicon from ever evicting an existing favicon.
// The value is always set to true and has no meaning.
base::MRUCache<Request, bool> responses_without_favicons_;
// Subscription for notifications of changes to favicons.
std::unique_ptr<
history::HistoryService::FaviconsChangedCallbackList::Subscription>
favicons_changed_subscription_;
base::WeakPtrFactory<FaviconCache> weak_factory_{this};
};
#endif // COMPONENTS_OMNIBOX_BROWSER_FAVICON_CACHE_H_