blob: 0797e794c98b1bd21639877db099c704d3d336a0 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/interest_group/interest_group_caching_storage.h"
#include <algorithm>
#include <cstdint>
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "content/browser/interest_group/bidding_and_auction_server_key_fetcher.h"
#include "content/browser/interest_group/for_debugging_only_report_util.h"
#include "content/browser/interest_group/interest_group_features.h"
#include "content/browser/interest_group/interest_group_manager_impl.h"
#include "content/browser/interest_group/interest_group_storage.h"
#include "content/browser/interest_group/storage_interest_group.h"
#include "content/common/features.h"
#include "content/public/common/content_features.h"
#include "url/origin.h"
namespace {
std::optional<content::SingleStorageInterestGroup>
ConvertOptionalGroupToSingleStorageInterestGroup(
std::optional<content::StorageInterestGroup> possible_group) {
if (possible_group) {
return content::SingleStorageInterestGroup(
std::move(possible_group.value()));
}
return std::nullopt;
}
} // namespace
namespace content {
SingleStorageInterestGroup::SingleStorageInterestGroup(
scoped_refptr<StorageInterestGroups> storage_interest_groups_for_owner,
const StorageInterestGroup* storage_interest_group)
: storage_interest_groups_for_owner(storage_interest_groups_for_owner),
storage_interest_group(storage_interest_group) {}
SingleStorageInterestGroup::SingleStorageInterestGroup(
const SingleStorageInterestGroup& other) = default;
SingleStorageInterestGroup::SingleStorageInterestGroup(
StorageInterestGroup&& interest_group) {
std::vector<StorageInterestGroup> storage_interest_groups_vec;
storage_interest_groups_vec.push_back(std::move(interest_group));
storage_interest_groups_for_owner =
base::MakeRefCounted<StorageInterestGroups>(
std::move(storage_interest_groups_vec));
storage_interest_group =
storage_interest_groups_for_owner->GetInterestGroups()[0]
.storage_interest_group;
}
SingleStorageInterestGroup::~SingleStorageInterestGroup() = default;
const StorageInterestGroup* SingleStorageInterestGroup::operator->() const {
return storage_interest_group;
}
const StorageInterestGroup& SingleStorageInterestGroup::operator*() const {
return *storage_interest_group;
}
StorageInterestGroups::StorageInterestGroups(
std::vector<StorageInterestGroup>&& interest_groups)
: storage_interest_groups_(std::move(interest_groups)) {
expiry_ = base::Time::Max();
if (base::FeatureList::IsEnabled(blink::features::kFledgeClickiness)) {
expiry_ =
base::Time::Now() + InterestGroupCachingStorage::kMaximumCacheHoldTime;
}
for (const StorageInterestGroup& group : storage_interest_groups_) {
expiry_ = std::min(expiry_, group.interest_group.expiry);
}
}
std::optional<SingleStorageInterestGroup> StorageInterestGroups::FindGroup(
std::string_view name) {
for (const StorageInterestGroup& interest_group : storage_interest_groups_) {
if (interest_group.interest_group.name == name) {
SingleStorageInterestGroup output(this, &interest_group);
return output;
}
}
return std::nullopt;
}
StorageInterestGroups::~StorageInterestGroups() = default;
base::WeakPtr<StorageInterestGroups> StorageInterestGroups::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
InterestGroupCachingStorage::CachedOriginsInfo::CachedOriginsInfo() = default;
InterestGroupCachingStorage::CachedOriginsInfo::CachedOriginsInfo(
const blink::InterestGroup& group)
: interest_group_name(group.name), expiry(group.expiry) {
if (group.trusted_bidding_signals_url.has_value()) {
url::Origin signals_origin =
url::Origin::Create(group.trusted_bidding_signals_url.value());
if (signals_origin != group.owner) {
bidding_signals_origin = std::move(signals_origin);
}
}
}
InterestGroupCachingStorage::CachedOriginsInfo::CachedOriginsInfo(
InterestGroupCachingStorage::CachedOriginsInfo&& other) = default;
InterestGroupCachingStorage::CachedOriginsInfo&
InterestGroupCachingStorage::CachedOriginsInfo::operator=(
InterestGroupCachingStorage::CachedOriginsInfo&& other) = default;
InterestGroupCachingStorage::CachedOriginsInfo::~CachedOriginsInfo() = default;
InterestGroupCachingStorage::InterestGroupCachingStorage(
const base::FilePath& path,
bool in_memory)
: interest_group_storage_(
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
in_memory ? base::FilePath() : path) {}
InterestGroupCachingStorage::~InterestGroupCachingStorage() = default;
void InterestGroupCachingStorage::GetInterestGroupsForOwner(
const url::Origin& owner,
base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)> callback) {
// If there is a cache hit, use the in-memory object.
auto cached_groups_it = cached_interest_groups_.find(owner);
if (cached_groups_it != cached_interest_groups_.end()) {
scoped_refptr<StorageInterestGroups> groups =
cached_groups_it->second.get();
if (groups && !groups->IsExpired()) {
StartTimerForInterestGroupHold(owner, groups);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(groups)));
base::UmaHistogramBoolean("Ads.InterestGroup.Auction.LoadGroupsCacheHit",
true);
return;
}
}
base::UmaHistogramBoolean("Ads.InterestGroup.Auction.LoadGroupsCacheHit",
false);
// If there is no cache hit, run
// InterestGroupStorage::GetInterestGroupsForOwner if there are no
// outstanding calls or if the cache has been invalidated since an
// outstanding call. Otherwise, allow the callback to use the result of an
// outstanding call.
base::queue<base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)>>&
callback_queue = interest_groups_sequenced_callbacks_[std::make_pair(
owner, valid_interest_group_versions_[owner])];
if (callback_queue.empty()) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetInterestGroupsForOwner)
.WithArgs(owner)
.Then(base::BindOnce(
&InterestGroupCachingStorage::OnLoadInterestGroupsForOwner,
weak_factory_.GetWeakPtr(), owner,
valid_interest_group_versions_[owner]));
base::UmaHistogramBoolean(
"Ads.InterestGroup.Auction.LoadGroupsUseInProgressLoad", false);
} else {
base::UmaHistogramBoolean(
"Ads.InterestGroup.Auction.LoadGroupsUseInProgressLoad", true);
}
callback_queue.push(std::move(callback));
}
bool InterestGroupCachingStorage::GetCachedOwnerAndSignalsOrigins(
const url::Origin& owner,
std::optional<url::Origin>& signals_origin) {
auto it = cached_owners_and_signals_origins_.find(owner);
if (it == cached_owners_and_signals_origins_.end()) {
return false;
}
if (it->second.expiry < base::Time::Now()) {
cached_owners_and_signals_origins_.erase(it);
return false;
}
signals_origin = it->second.bidding_signals_origin;
return true;
}
void InterestGroupCachingStorage::UpdateCachedOriginsIfEnabled(
const url::Origin& owner) {
if (!base::FeatureList::IsEnabled(features::kFledgeUsePreconnectCache) &&
!base::FeatureList::IsEnabled(
features::kFledgeStartAnticipatoryProcesses)) {
return;
}
auto cached_groups_it = cached_interest_groups_.find(owner);
if (cached_groups_it == cached_interest_groups_.end()) {
return;
}
scoped_refptr<StorageInterestGroups> groups = cached_groups_it->second.get();
if (!groups || groups->IsExpired() || groups->size() == 0) {
return;
}
CachedOriginsInfo cached_origins_info;
for (const StorageInterestGroup& group : groups->storage_interest_groups_) {
if (group.interest_group.expiry > cached_origins_info.expiry) {
cached_origins_info = CachedOriginsInfo(group.interest_group);
}
}
cached_owners_and_signals_origins_[owner] = std::move(cached_origins_info);
}
void InterestGroupCachingStorage::JoinInterestGroup(
const blink::InterestGroup& group,
const GURL& main_frame_joining_url,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback) {
InvalidateCachedInterestGroupsForOwner(group.owner);
interest_group_storage_.AsyncCall(&InterestGroupStorage::JoinInterestGroup)
.WithArgs(std::move(group), std::move(main_frame_joining_url))
.Then(base::BindOnce(&InterestGroupCachingStorage::OnJoinInterestGroup,
weak_factory_.GetWeakPtr(), group.owner,
CachedOriginsInfo(group), std::move(callback)));
}
void InterestGroupCachingStorage::LeaveInterestGroup(
const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
base::OnceClosure callback) {
InvalidateCachedInterestGroupsForOwner(group_key.owner);
auto it = cached_owners_and_signals_origins_.find(group_key.owner);
if (it != cached_owners_and_signals_origins_.end() &&
(it->second.interest_group_name == group_key.name ||
it->second.expiry < base::Time::Now())) {
cached_owners_and_signals_origins_.erase(it);
}
interest_group_storage_.AsyncCall(&InterestGroupStorage::LeaveInterestGroup)
.WithArgs(group_key, main_frame)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::ClearOriginJoinedInterestGroups(
const url::Origin& owner,
const std::set<std::string>& interest_groups_to_keep,
const url::Origin& main_frame_origin,
base::OnceCallback<void(std::vector<std::string>)> callback) {
InvalidateCachedInterestGroupsForOwner(owner);
auto it = cached_owners_and_signals_origins_.find(owner);
if (it != cached_owners_and_signals_origins_.end() &&
(!interest_groups_to_keep.contains(it->second.interest_group_name) ||
it->second.expiry < base::Time::Now())) {
cached_owners_and_signals_origins_.erase(it);
}
interest_group_storage_
.AsyncCall(&InterestGroupStorage::ClearOriginJoinedInterestGroups)
.WithArgs(std::move(owner), std::move(interest_groups_to_keep),
std::move(main_frame_origin))
.Then(std::move(callback));
}
void InterestGroupCachingStorage::UpdateInterestGroup(
const blink::InterestGroupKey& group_key,
InterestGroupUpdate update,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback) {
InvalidateCachedInterestGroupsForOwner(group_key.owner);
if (update.trusted_bidding_signals_url) {
auto it = cached_owners_and_signals_origins_.find(group_key.owner);
if (it != cached_owners_and_signals_origins_.end() &&
((it->second.interest_group_name == group_key.name &&
it->second.bidding_signals_origin !=
url::Origin::Create(*update.trusted_bidding_signals_url)) ||
it->second.expiry < base::Time::Now())) {
// Instead of modifying the existing cache entry, erase it in case the
// update is invalid or doesn't succeed.
cached_owners_and_signals_origins_.erase(it);
}
}
interest_group_storage_.AsyncCall(&InterestGroupStorage::UpdateInterestGroup)
.WithArgs(group_key, std::move(update))
.Then(std::move(callback));
}
void InterestGroupCachingStorage::AllowUpdateIfOlderThan(
blink::InterestGroupKey group_key,
base::TimeDelta update_if_older_than) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::AllowUpdateIfOlderThan)
.WithArgs(std::move(group_key), update_if_older_than);
}
void InterestGroupCachingStorage::ReportUpdateFailed(
const blink::InterestGroupKey& group_key,
bool parse_failure) {
interest_group_storage_.AsyncCall(&InterestGroupStorage::ReportUpdateFailed)
.WithArgs(group_key, parse_failure);
}
void InterestGroupCachingStorage::RecordInterestGroupBids(
const blink::InterestGroupSet& groups) {
// Update the cached objects with the new bid counts instead of invalidating
// the cache.
std::set<url::Origin> bidding_owners;
for (const blink::InterestGroupKey& group_key : groups) {
bidding_owners.emplace(group_key.owner);
}
for (const url::Origin& owner : bidding_owners) {
MarkOutstandingInterestGroupLoadResultOutdated(owner);
auto cached_groups_it = cached_interest_groups_.find(owner);
if (cached_groups_it == cached_interest_groups_.end() ||
!cached_groups_it->second.get()) {
continue;
}
for (StorageInterestGroup& cached_group :
cached_groups_it->second->storage_interest_groups_) {
if (groups.contains(blink::InterestGroupKey(
owner, cached_group.interest_group.name))) {
cached_group.bidding_browser_signals->bid_count += 1;
}
}
}
interest_group_storage_
.AsyncCall(&InterestGroupStorage::RecordInterestGroupBids)
.WithArgs(groups);
}
void InterestGroupCachingStorage::RecordInterestGroupWin(
const blink::InterestGroupKey& group_key,
const std::string& ad_json) {
InvalidateCachedInterestGroupsForOwner(group_key.owner);
interest_group_storage_
.AsyncCall(&InterestGroupStorage::RecordInterestGroupWin)
.WithArgs(group_key, std::move(ad_json));
}
void InterestGroupCachingStorage::RecordDebugReportLockout(
base::Time starting_time,
base::TimeDelta duration) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::RecordDebugReportLockout)
.WithArgs(starting_time, duration);
}
void InterestGroupCachingStorage::RecordDebugReportCooldown(
const url::Origin& origin,
base::Time cooldown_start,
DebugReportCooldownType cooldown_type) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::RecordDebugReportCooldown)
.WithArgs(origin, cooldown_start, cooldown_type);
}
void InterestGroupCachingStorage::RecordViewClick(
network::AdAuctionEventRecord event_record) {
// Cached interest groups containing stale view / click counts are
// intentionally not evicted -- views especially occur frequently, and would
// result in many evictions, limiting the usefulness of this cache. So, for
// performance, it is better to return view / click data that's slightly
// stale.
//
// TODO(crbug.com/394108643): Cap the time duration of this staleness with a
// new timer that evicts groups loaded more than say 120 seconds ago. Without
// this, in the rare case that auctions that each load a given IG are running
// constantly, back-to-back, the view click data for that IG could become
// arbitrarily stale.
interest_group_storage_.AsyncCall(&InterestGroupStorage::RecordViewClick)
.WithArgs(std::move(event_record));
}
void InterestGroupCachingStorage::CheckViewClickInfoInDbForTesting(
url::Origin provider_origin,
url::Origin eligible_origin,
base::OnceCallback<void(std::optional<bool>)> callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::
CheckViewClickCountsForProviderAndEligibleInDbForTesting)
.WithArgs(std::move(provider_origin), std::move(eligible_origin))
.Then(std::move(callback));
}
void InterestGroupCachingStorage::UpdateKAnonymity(
const blink::InterestGroupKey& interest_group_key,
const std::vector<std::string>& positive_hashed_keys,
const base::Time update_time,
bool replace_existing_values) {
// We do not know the affected owners without looking them up from the
// database or calculating k-anon keys for all the ads in the cache. Both are
// expensive, and this function will run many times per interest group, so
// prefer to over-delete data here.
InvalidateAllCachedInterestGroups();
interest_group_storage_.AsyncCall(&InterestGroupStorage::UpdateKAnonymity)
.WithArgs(interest_group_key, positive_hashed_keys, update_time,
replace_existing_values);
}
void InterestGroupCachingStorage::GetLastKAnonymityReported(
const std::string& hashed_key,
base::OnceCallback<void(std::optional<base::Time>)> callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetLastKAnonymityReported)
.WithArgs(hashed_key)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::UpdateLastKAnonymityReported(
const std::string& hashed_key) {
// We don't need to invalidate any cached objects here because this value is
// not loaded in GetInterestGroupsForOwner.
interest_group_storage_
.AsyncCall(&InterestGroupStorage::UpdateLastKAnonymityReported)
.WithArgs(hashed_key);
}
void InterestGroupCachingStorage::GetInterestGroup(
const blink::InterestGroupKey& group_key,
base::OnceCallback<void(std::optional<SingleStorageInterestGroup>)>
callback) {
auto cached_groups_it = cached_interest_groups_.find(group_key.owner);
if (cached_groups_it != cached_interest_groups_.end()) {
scoped_refptr<StorageInterestGroups> groups =
cached_groups_it->second.get();
if (groups) {
std::optional<SingleStorageInterestGroup> output =
groups->FindGroup(group_key.name);
if (output && output.value()->interest_group.expiry < base::Time::Now()) {
output.reset();
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(output)));
base::UmaHistogramBoolean("Ads.InterestGroup.GetInterestGroupCacheHit",
true);
return;
}
}
base::UmaHistogramBoolean("Ads.InterestGroup.GetInterestGroupCacheHit",
false);
interest_group_storage_.AsyncCall(&InterestGroupStorage::GetInterestGroup)
.WithArgs(group_key)
.Then(base::BindOnce(&ConvertOptionalGroupToSingleStorageInterestGroup)
.Then(std::move(callback)));
}
void InterestGroupCachingStorage::GetAllInterestGroupOwners(
base::OnceCallback<void(std::vector<url::Origin>)> callback) {
return interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetAllInterestGroupOwners)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetInterestGroupsForUpdate(
const url::Origin& owner,
int groups_limit,
base::OnceCallback<void(std::vector<InterestGroupUpdateParameter>)>
callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetInterestGroupsForUpdate)
.WithArgs(owner, groups_limit)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetDebugReportLockoutAndCooldowns(
base::flat_set<url::Origin> origins,
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback) {
return interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetDebugReportLockoutAndCooldowns)
.WithArgs(std::move(origins))
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetDebugReportLockoutAndAllCooldowns(
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback) {
return interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetDebugReportLockoutAndAllCooldowns)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetAllInterestGroupJoiningOrigins(
base::OnceCallback<void(std::vector<url::Origin>)> callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetAllInterestGroupJoiningOrigins)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetAllInterestGroupOwnerJoinerPairs(
base::OnceCallback<void(std::vector<std::pair<url::Origin, url::Origin>>)>
callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetAllInterestGroupOwnerJoinerPairs)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::RemoveInterestGroupsMatchingOwnerAndJoiner(
url::Origin owner,
url::Origin joining_origin,
base::OnceClosure callback) {
InvalidateCachedInterestGroupsForOwner(owner);
interest_group_storage_
.AsyncCall(
&InterestGroupStorage::RemoveInterestGroupsMatchingOwnerAndJoiner)
.WithArgs(owner, joining_origin)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::DeleteInterestGroupData(
StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
bool user_initiated_deletion,
base::OnceClosure callback) {
// Clear all owners because storage_key_matcher can match on joining_origin,
// which we do not have stored in cached_interest_groups_.
InvalidateAllCachedInterestGroups();
interest_group_storage_
.AsyncCall(&InterestGroupStorage::DeleteInterestGroupData)
.WithArgs(std::move(storage_key_matcher), user_initiated_deletion)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::DeleteAllInterestGroupData(
base::OnceClosure callback) {
InvalidateAllCachedInterestGroups();
cached_owners_and_signals_origins_.clear();
interest_group_storage_
.AsyncCall(&InterestGroupStorage::DeleteAllInterestGroupData)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::SetInterestGroupPriority(
const blink::InterestGroupKey& group_key,
double priority) {
InvalidateCachedInterestGroupsForOwner(group_key.owner);
interest_group_storage_
.AsyncCall(&InterestGroupStorage::SetInterestGroupPriority)
.WithArgs(group_key, priority);
}
void InterestGroupCachingStorage::UpdateInterestGroupPriorityOverrides(
const blink::InterestGroupKey& group_key,
base::flat_map<std::string,
auction_worklet::mojom::PrioritySignalsDoublePtr>
update_priority_signals_overrides) {
InvalidateCachedInterestGroupsForOwner(group_key.owner);
interest_group_storage_
.AsyncCall(&InterestGroupStorage::UpdateInterestGroupPriorityOverrides)
.WithArgs(group_key, std::move(update_priority_signals_overrides));
}
void InterestGroupCachingStorage::SetBiddingAndAuctionServerKeys(
const url::Origin& coordinator,
std::string serialized_keys,
base::Time expiration) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::SetBiddingAndAuctionServerKeys)
.WithArgs(coordinator, std::move(serialized_keys), expiration);
}
void InterestGroupCachingStorage::GetBiddingAndAuctionServerKeys(
const url::Origin& coordinator,
base::OnceCallback<void(std::pair<base::Time, std::string>)> callback) {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetBiddingAndAuctionServerKeys)
.WithArgs(coordinator)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::WriteHashedKAnonymityKeysToCache(
const std::vector<std::string>& positive_hashed_keys,
const std::vector<std::string>& negative_hashed_keys,
base::Time time_fetched) {
interest_group_storage_
.AsyncCall(base::IgnoreResult(
&InterestGroupStorage::WriteHashedKAnonymityKeysToCache))
.WithArgs(positive_hashed_keys, negative_hashed_keys, time_fetched);
}
void InterestGroupCachingStorage::LoadPositiveHashedKAnonymityKeysFromCache(
const std::vector<std::string>& keys,
base::Time min_valid_time,
base::OnceCallback<void(InterestGroupStorage::KAnonymityCacheResponse)>
callback) {
interest_group_storage_
.AsyncCall(
&InterestGroupStorage::LoadPositiveHashedKAnonymityKeysFromCache)
.WithArgs(keys, min_valid_time)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::GetLastMaintenanceTimeForTesting(
base::RepeatingCallback<void(base::Time)> callback) const {
interest_group_storage_
.AsyncCall(&InterestGroupStorage::GetLastMaintenanceTimeForTesting)
.Then(std::move(callback));
}
void InterestGroupCachingStorage::OnJoinInterestGroup(
const url::Origin& owner,
CachedOriginsInfo cached_origins_info,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback,
std::optional<InterestGroupKanonUpdateParameter> update) {
if (update) {
auto it = cached_owners_and_signals_origins_.find(owner);
if (it != cached_owners_and_signals_origins_.end()) {
if (it->second.interest_group_name ==
cached_origins_info.interest_group_name) {
it->second = std::move(cached_origins_info);
}
}
}
std::move(callback).Run(std::move(update));
}
void InterestGroupCachingStorage::OnLoadInterestGroupsForOwner(
const url::Origin& owner,
uint32_t version,
std::vector<StorageInterestGroup> interest_groups) {
auto outstanding_callbacks_it =
interest_groups_sequenced_callbacks_.find(std::make_pair(owner, version));
if (outstanding_callbacks_it == interest_groups_sequenced_callbacks_.end()) {
return;
}
scoped_refptr<StorageInterestGroups> interest_groups_ptr =
base::MakeRefCounted<StorageInterestGroups>(std::move(interest_groups));
// Cache the result only if it's still valid.
if (version == valid_interest_group_versions_[owner]) {
cached_interest_groups_[owner] = interest_groups_ptr->GetWeakPtr();
StartTimerForInterestGroupHold(owner, interest_groups_ptr);
}
while (!outstanding_callbacks_it->second.empty()) {
std::move(outstanding_callbacks_it->second.front())
.Run(interest_groups_ptr);
outstanding_callbacks_it->second.pop();
}
interest_groups_sequenced_callbacks_.erase(outstanding_callbacks_it);
if (interest_groups_sequenced_callbacks_.empty()) {
// Reset the versions so that we don't need to have all owners in memory.
valid_interest_group_versions_.clear();
}
}
void InterestGroupCachingStorage::InvalidateCachedInterestGroupsForOwner(
const url::Origin& owner) {
cached_interest_groups_.erase(owner);
MarkOutstandingInterestGroupLoadResultOutdated(owner);
timed_holds_of_interest_groups_.erase(owner);
}
void InterestGroupCachingStorage::InvalidateAllCachedInterestGroups() {
cached_interest_groups_.clear();
timed_holds_of_interest_groups_.clear();
for (const auto& [owner_version, _] : interest_groups_sequenced_callbacks_) {
MarkOutstandingInterestGroupLoadResultOutdated(owner_version.first);
}
}
void InterestGroupCachingStorage::
MarkOutstandingInterestGroupLoadResultOutdated(const url::Origin& owner) {
auto it = valid_interest_group_versions_.find(owner);
if (it != valid_interest_group_versions_.end()) {
it->second = it->second + 1;
}
}
void InterestGroupCachingStorage::StartTimerForInterestGroupHold(
const url::Origin& owner,
scoped_refptr<StorageInterestGroups> groups) {
// Get the existing timer if it exists or create a new one if not.
std::unique_ptr<base::OneShotTimer>& timer =
timed_holds_of_interest_groups_
.insert(std::make_pair(owner, std::make_unique<base::OneShotTimer>()))
.first->second;
if (timer->IsRunning()) {
timer->Stop();
}
timer->Start(
FROM_HERE, kMinimumCacheHoldTime,
base::BindOnce(
&InterestGroupCachingStorage::OnMinimumCacheHoldTimeCompleted,
weak_factory_.GetWeakPtr(), owner, groups));
}
} // namespace content