blob: 979ba8717c6c6eb22f54f48269d43f9999072d9d [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/history/content/browser/content_visit_delegate.h"
#include <utility>
#include "base/check.h"
#include "base/memory/ref_counted.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_database.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/visitedlink/browser/partitioned_visitedlink_writer.h"
#include "components/visitedlink/browser/visitedlink_writer.h"
#include "components/visitedlink/core/visited_link.h"
#include "net/base/schemeful_site.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
namespace history {
namespace {
// URLIterator from std::vector<GURL>
class URLIteratorFromURLs : public visitedlink::VisitedLinkWriter::URLIterator {
public:
explicit URLIteratorFromURLs(const std::vector<GURL>& urls)
: itr_(urls.begin()), end_(urls.end()) {}
URLIteratorFromURLs(const URLIteratorFromURLs&) = delete;
URLIteratorFromURLs& operator=(const URLIteratorFromURLs&) = delete;
// visitedlink::VisitedLinkWriter::URLIterator implementation.
const GURL& NextURL() override { return *(itr_++); }
bool HasNextURL() const override { return itr_ != end_; }
private:
std::vector<GURL>::const_iterator itr_;
std::vector<GURL>::const_iterator end_;
};
// Creates a VisitedLinkIterator from std::vector<VisitedLink>. Allows us to
// efficiently delete a list of VisitedLinks from the partitioned hashtable.
class VisitedLinkIteratorFromLinks
: public visitedlink::PartitionedVisitedLinkWriter::VisitedLinkIterator {
public:
explicit VisitedLinkIteratorFromLinks(const std::vector<VisitedLink>& links)
: itr_(links.begin()), end_(links.end()) {}
VisitedLinkIteratorFromLinks(const VisitedLinkIteratorFromLinks&) = delete;
VisitedLinkIteratorFromLinks& operator=(const VisitedLinkIteratorFromLinks&) =
delete;
// visitedlink::PartitionedVisitedLinkWriter::VisitedLinkIterator
// implementation.
const VisitedLink& NextVisitedLink() override { return *(itr_++); }
bool HasNextVisitedLink() const override { return itr_ != end_; }
private:
std::vector<VisitedLink>::const_iterator itr_;
std::vector<VisitedLink>::const_iterator end_;
};
// IterateUrlsDBTask bridge HistoryBackend::URLEnumerator to
// visitedlink::VisitedLinkDelegate::URLEnumerator.
class IterateUrlsDBTask : public HistoryDBTask {
public:
explicit IterateUrlsDBTask(const scoped_refptr<
visitedlink::VisitedLinkDelegate::URLEnumerator>& enumerator);
IterateUrlsDBTask(const IterateUrlsDBTask&) = delete;
IterateUrlsDBTask& operator=(const IterateUrlsDBTask&) = delete;
~IterateUrlsDBTask() override;
private:
// Implementation of HistoryDBTask.
bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override;
void DoneRunOnMainThread() override;
scoped_refptr<visitedlink::VisitedLinkDelegate::URLEnumerator> enumerator_;
};
IterateUrlsDBTask::IterateUrlsDBTask(const scoped_refptr<
visitedlink::VisitedLinkDelegate::URLEnumerator>& enumerator)
: enumerator_(enumerator) {
}
IterateUrlsDBTask::~IterateUrlsDBTask() {
}
bool IterateUrlsDBTask::RunOnDBThread(HistoryBackend* backend,
HistoryDatabase* db) {
bool success = false;
if (db) {
HistoryDatabase::URLEnumerator iter;
if (db->InitURLEnumeratorForEverything(&iter)) {
URLRow row;
while (iter.GetNextURL(&row))
enumerator_->OnURL(row.url());
success = true;
}
}
enumerator_->OnComplete(success);
return true;
}
void IterateUrlsDBTask::DoneRunOnMainThread() {
}
// IterateVisitedLinkDBTask bridges HistoryBackend::VisitedLinkEnumerator to
// VisitedLinkDelegate::VisitedLinkEnumerator.
class IterateVisitedLinkDBTask : public HistoryDBTask {
public:
explicit IterateVisitedLinkDBTask(
const scoped_refptr<
visitedlink::VisitedLinkDelegate::VisitedLinkEnumerator>& enumerator);
IterateVisitedLinkDBTask(const IterateVisitedLinkDBTask&) = delete;
IterateVisitedLinkDBTask& operator=(const IterateVisitedLinkDBTask&) = delete;
~IterateVisitedLinkDBTask() override;
private:
// Implementation of HistoryDBTask.
bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) override;
void DoneRunOnMainThread() override;
scoped_refptr<visitedlink::VisitedLinkDelegate::VisitedLinkEnumerator>
enumerator_;
};
IterateVisitedLinkDBTask::IterateVisitedLinkDBTask(
const scoped_refptr<
visitedlink::VisitedLinkDelegate::VisitedLinkEnumerator>& enumerator)
: enumerator_(enumerator) {}
IterateVisitedLinkDBTask::~IterateVisitedLinkDBTask() = default;
bool IterateVisitedLinkDBTask::RunOnDBThread(HistoryBackend* backend,
HistoryDatabase* db) {
// Begin iterating through the VisitedLinkDatabase.
bool success = false;
if (db) {
HistoryDatabase::VisitedLinkEnumerator iter;
if (db->InitVisitedLinkEnumeratorForEverything(iter)) {
VisitedLinkRow row;
while (iter.GetNextVisitedLink(row)) {
URLRow url_info;
// We must obtain the link url from the ID we're given.
if (db->GetURLRow(row.link_url_id, &url_info)) {
net::SchemefulSite top_level_site(row.top_level_url);
url::Origin frame_origin = url::Origin::Create(row.frame_url);
enumerator_->OnVisitedLink(url_info.url(), top_level_site,
frame_origin);
}
}
success = true;
}
}
enumerator_->OnVisitedLinkComplete(success);
return true;
}
void IterateVisitedLinkDBTask::DoneRunOnMainThread() {}
} // namespace
ContentVisitDelegate::ContentVisitDelegate(
content::BrowserContext* browser_context)
: history_service_(nullptr) {
// To keep the partitioned visited links experiments performant, we will only
// construct and initialize one writer (partitioned or unpartitioned) at a
// time. Callers of either `partitioned_writer_` or `visitedlink_writer_`
// should ensure the pointer is not null before use.
if (base::FeatureList::IsEnabled(
blink::features::kPartitionVisitedLinkDatabaseWithSelfLinks)) {
partitioned_writer_ =
std::make_unique<visitedlink::PartitionedVisitedLinkWriter>(
browser_context, this);
} else {
visitedlink_writer_ = std::make_unique<visitedlink::VisitedLinkWriter>(
browser_context, this, true);
}
}
ContentVisitDelegate::~ContentVisitDelegate() {
}
bool ContentVisitDelegate::Init(HistoryService* history_service) {
DCHECK(history_service);
history_service_ = history_service;
// To keep the partitioned visited links experiments performant, we will only
// construct and initialize one writer (partitioned or unpartitioned) at a
// time. Callers of either `partitioned_writer_` or `visitedlink_writer_`
// should ensure the pointer is not null before use.
if (base::FeatureList::IsEnabled(
blink::features::kPartitionVisitedLinkDatabaseWithSelfLinks)) {
DCHECK(partitioned_writer_);
return partitioned_writer_->Init();
}
DCHECK(visitedlink_writer_);
return visitedlink_writer_->Init();
}
void ContentVisitDelegate::AddURL(const GURL& url) {
// Not all callers of AddURL will have partitioning disabled. We should
// only add URLs when the unpartitioned table is available.
if (visitedlink_writer_) {
visitedlink_writer_->AddURL(url);
}
}
void ContentVisitDelegate::AddURLs(const std::vector<GURL>& urls) {
// Not all callers of AddURLs will have partitioning disabled. We should
// only add URLs when the unpartitioned table is available.
if (visitedlink_writer_) {
visitedlink_writer_->AddURLs(urls);
}
}
void ContentVisitDelegate::DeleteURLs(const std::vector<GURL>& urls) {
// Not all callers of DeleteURLs will have partitioning disabled. We should
// only delete URLs when the unpartitioned table is available.
if (visitedlink_writer_) {
URLIteratorFromURLs iterator(urls);
visitedlink_writer_->DeleteURLs(&iterator);
}
}
void ContentVisitDelegate::DeleteAllURLs() {
// Not all callers of DeleteAllURLs will have partitioning disabled. We should
// only delete URLs when the unpartitioned table is available.
if (visitedlink_writer_) {
visitedlink_writer_->DeleteAllURLs();
}
}
void ContentVisitDelegate::AddVisitedLink(const VisitedLink& link) {
// Not all callers of AddVisitedLink will have partitioning enabled. We should
// only add visited links when the partitioned table is available.
if (partitioned_writer_) {
partitioned_writer_->AddVisitedLink(link);
}
}
void ContentVisitDelegate::DeleteVisitedLinks(
const std::vector<VisitedLink>& links) {
// Not all callers of DeleteVisitedLinks will have partitioning enabled. We
// should only delete visited links when the partitioned table is available.
if (partitioned_writer_) {
VisitedLinkIteratorFromLinks iterator(links);
partitioned_writer_->DeleteVisitedLinks(&iterator);
}
}
void ContentVisitDelegate::DeleteAllVisitedLinks() {
// Not all callers of DeleteAllVisitedLinks will have partitioning enabled. We
// should only delete visited links when the partitioned table is available.
if (partitioned_writer_) {
partitioned_writer_->DeleteAllVisitedLinks();
}
}
std::optional<uint64_t> ContentVisitDelegate::GetOrAddOriginSalt(
const url::Origin& origin) {
// This function should only be called when
// `kPartitionVisitedLinkDatabaseWithSelfLinks` is enabled, meaning
// partitioned_writer_ should be constructed and init.
if (partitioned_writer_) {
return partitioned_writer_->GetOrAddOriginSalt(origin);
}
return std::nullopt;
}
void ContentVisitDelegate::RebuildTable(
const scoped_refptr<URLEnumerator>& enumerator) {
DCHECK(history_service_);
std::unique_ptr<HistoryDBTask> task(new IterateUrlsDBTask(enumerator));
history_service_->ScheduleDBTask(FROM_HERE, std::move(task), &task_tracker_);
}
void ContentVisitDelegate::BuildVisitedLinkTable(
const scoped_refptr<VisitedLinkEnumerator>& enumerator) {
DCHECK(history_service_);
std::unique_ptr<HistoryDBTask> task(new IterateVisitedLinkDBTask(enumerator));
history_service_->ScheduleDBTask(FROM_HERE, std::move(task), &task_tracker_);
}
} // namespace history