blob: 717dbbcb8e726343fb6f3ff2d9f0d834740b33ec [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "weblayer/browser/favicon/favicon_backend_wrapper.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "components/favicon/core/favicon_backend.h"
#include "components/favicon/core/favicon_database.h"
namespace weblayer {
// Removing out of date entries can be costly. To avoid blocking the thread
// this code runs on, the work is potentially throttled. Specifically at
// most |kMaxNumberOfEntriesToRemoveAtATime| are removed during a single call.
// If |kMaxNumberOfEntriesToRemoveAtATime| are removed, then there may be more
// entries that can be removed, so the timer is restarted with a shorter time
// out (|kTimeDeltaForRunningExpireWithRemainingWork|).
constexpr base::TimeDelta kTimeDeltaForRunningExpireNoRemainingWork =
base::Hours(1);
constexpr int kMaxNumberOfEntriesToRemoveAtATime = 100;
FaviconBackendWrapper::FaviconBackendWrapper(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: base::RefCountedDeleteOnSequence<FaviconBackendWrapper>(task_runner),
task_runner_(task_runner) {}
void FaviconBackendWrapper::Init(const base::FilePath& db_path) {
db_path_ = db_path;
favicon_backend_ = favicon::FaviconBackend::Create(db_path, this);
if (!favicon_backend_) {
LOG(WARNING) << "Could not initialize the favicon database.";
// The favicon db is not critical. On failure initializing try deleting
// the file and repeating. Note that FaviconDatabase already tries to
// initialize twice.
base::DeleteFile(db_path);
favicon_backend_ = favicon::FaviconBackend::Create(db_path, this);
if (!favicon_backend_) {
LOG(WARNING) << "Could not initialize db second time, giving up.";
return;
}
}
expire_timer_.Start(FROM_HERE, kTimeDeltaForRunningExpireWithRemainingWork,
this, &FaviconBackendWrapper::OnExpireTimerFired);
}
void FaviconBackendWrapper::Shutdown() {
// Ensures there isn't a reference to this in the task runner (by way of the
// task the timer posts).
commit_timer_.Stop();
expire_timer_.Stop();
}
void FaviconBackendWrapper::DeleteAndRecreateDatabase() {
Shutdown();
favicon_backend_.reset();
base::DeleteFile(db_path_);
Init(db_path_);
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::GetFaviconsForUrl(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
const std::vector<int>& desired_sizes) {
if (!favicon_backend_)
return {};
return favicon_backend_->GetFaviconsForUrl(page_url, icon_types,
desired_sizes,
/* fallback_to_host */ false);
}
favicon_base::FaviconRawBitmapResult
FaviconBackendWrapper::GetLargestFaviconForUrl(
const GURL& page_url,
const std::vector<favicon_base::IconTypeSet>& icon_types_list,
int minimum_size_in_pixels) {
if (!favicon_backend_)
return {};
return favicon_backend_->GetLargestFaviconForUrl(page_url, icon_types_list,
minimum_size_in_pixels);
}
void FaviconBackendWrapper::SetFaviconsOutOfDateForPage(const GURL& page_url) {
if (favicon_backend_ &&
favicon_backend_->SetFaviconsOutOfDateForPage(page_url)) {
ScheduleCommit();
}
}
void FaviconBackendWrapper::SetFavicons(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps) {
if (favicon_backend_ &&
favicon_backend_
->SetFavicons(page_urls, icon_type, icon_url, bitmaps,
favicon::FaviconBitmapType::ON_VISIT)
.did_change_database()) {
ScheduleCommit();
}
}
void FaviconBackendWrapper::CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write) {
if (!favicon_backend_)
return;
std::set<GURL> changed_urls = favicon_backend_->CloneFaviconMappingsForPages(
{page_url_to_read}, icon_types, page_urls_to_write);
if (!changed_urls.empty())
ScheduleCommit();
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::GetFavicon(const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes) {
return UpdateFaviconMappingsAndFetch({}, icon_url, icon_type, desired_sizes);
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::UpdateFaviconMappingsAndFetch(
const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes) {
if (!favicon_backend_)
return {};
auto result = favicon_backend_->UpdateFaviconMappingsAndFetch(
page_urls, icon_url, icon_type, desired_sizes);
if (!result.updated_page_urls.empty())
ScheduleCommit();
return result.bitmap_results;
}
void FaviconBackendWrapper::DeleteFaviconMappings(
const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type) {
if (!favicon_backend_)
return;
auto deleted_page_urls =
favicon_backend_->DeleteFaviconMappings(page_urls, icon_type);
if (!deleted_page_urls.empty())
ScheduleCommit();
}
std::vector<GURL> FaviconBackendWrapper::GetCachedRecentRedirectsForPage(
const GURL& page_url) {
// By only returning |page_url| this code won't set the favicon on redirects.
// If that becomes necessary, we would need this class to know about
// redirects. Chrome does this by way of HistoryService remembering redirects
// for recent pages. See |HistoryBackend::recent_redirects_|.
return {page_url};
}
FaviconBackendWrapper::~FaviconBackendWrapper() = default;
void FaviconBackendWrapper::ScheduleCommit() {
if (!commit_timer_.IsRunning()) {
// 10 seconds matches that of HistoryBackend.
commit_timer_.Start(FROM_HERE, base::Seconds(10), this,
&FaviconBackendWrapper::Commit);
}
}
void FaviconBackendWrapper::Commit() {
if (favicon_backend_)
favicon_backend_->Commit();
}
void FaviconBackendWrapper::OnExpireTimerFired() {
if (!favicon_backend_)
return;
// See comments above |kTimeDeltaForRunningExpireNoRemainingWork| for a
// description of this logic.
favicon::FaviconDatabase* db = favicon_backend_->db();
auto icon_ids = db->GetFaviconsLastUpdatedBefore(
base::Time::Now() - kTimeDeltaWhenEntriesAreRemoved,
kMaxNumberOfEntriesToRemoveAtATime);
for (favicon_base::FaviconID icon_id : icon_ids) {
db->DeleteFavicon(icon_id);
db->DeleteIconMappingsForFaviconId(icon_id);
}
if (!icon_ids.empty())
Commit();
const base::TimeDelta delta =
icon_ids.size() == kMaxNumberOfEntriesToRemoveAtATime
? kTimeDeltaForRunningExpireWithRemainingWork
: kTimeDeltaForRunningExpireNoRemainingWork;
expire_timer_.Start(FROM_HERE, delta, this,
&FaviconBackendWrapper::OnExpireTimerFired);
}
} // namespace weblayer