1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
|
// 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.
#ifndef CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_CACHING_STORAGE_H_
#define CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_CACHING_STORAGE_H_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "content/browser/interest_group/for_debugging_only_report_util.h"
#include "content/browser/interest_group/interest_group_storage.h"
#include "content/browser/interest_group/interest_group_update.h"
#include "content/browser/interest_group/storage_interest_group.h"
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
#include "url/origin.h"
namespace content {
struct DebugReportLockoutAndCooldowns;
class StorageInterestGroups;
// SingleStorageInterestGroup ensures that pointers to values inside
// StorageInterestGroups are accompanied by a
// scoped_refptr<StorageInterestGroups> to prevent dangling pointers and
// ensures the scoped_refptr<StorageInterestGroups> and
// raw_ptr<StorageInterestGroup> are destructed in the correct order.
class CONTENT_EXPORT SingleStorageInterestGroup {
public:
explicit SingleStorageInterestGroup(
scoped_refptr<StorageInterestGroups> storage_interest_groups_for_owner,
const StorageInterestGroup* storage_interest_group);
SingleStorageInterestGroup(const SingleStorageInterestGroup& other);
// Create a SingleStorageInterestGroup from scratch, including generating a
// StorageInterestGroups featuring just `interest_group`.
explicit SingleStorageInterestGroup(StorageInterestGroup&& interest_group);
~SingleStorageInterestGroup();
SingleStorageInterestGroup& operator=(SingleStorageInterestGroup&& other) =
default;
const StorageInterestGroup* operator->() const;
const StorageInterestGroup& operator*() const;
private:
scoped_refptr<StorageInterestGroups> storage_interest_groups_for_owner;
raw_ptr<const StorageInterestGroup> storage_interest_group;
};
// StorageInterestGroups is needed for InterestGroupCachingStorage
// because it requires weak pointers and ref counted pointers to
// std::vector<StorageInterestGroup>.
class CONTENT_EXPORT StorageInterestGroups
: public base::RefCounted<StorageInterestGroups> {
public:
explicit StorageInterestGroups(
std::vector<StorageInterestGroup>&& interest_groups);
StorageInterestGroups(const StorageInterestGroup& other) = delete;
base::WeakPtr<StorageInterestGroups> GetWeakPtr();
size_t size() { return storage_interest_groups_.size(); }
std::vector<SingleStorageInterestGroup> GetInterestGroups() {
std::vector<SingleStorageInterestGroup> storage_interest_groups;
for (const StorageInterestGroup& interest_group :
storage_interest_groups_) {
storage_interest_groups.emplace_back(this, &interest_group);
}
return storage_interest_groups;
}
std::optional<SingleStorageInterestGroup> FindGroup(std::string_view name);
bool IsExpired() { return expiry_ < base::Time::Now(); }
private:
friend class RefCounted<StorageInterestGroups>;
friend class InterestGroupCachingStorage;
~StorageInterestGroups();
std::vector<StorageInterestGroup> storage_interest_groups_;
base::Time expiry_;
base::WeakPtrFactory<StorageInterestGroups> weak_ptr_factory_{this};
};
// InterestGroupCachingStorage controls access to the Interest Group Database
// through its owned InterestGroupStorage. InterestGroupStorage should
// not be accessed outside of this class. InterestGroupCachingStorage provides a
// pointer to in-memory values for GetInterestGroupsForOwner when available and
// invalidates the cached values when necessary (when an update to the values
// occurs). It also provides cached values of the owner and bidding signals
// origins so that they can be prefetched before loading interest groups.
class CONTENT_EXPORT InterestGroupCachingStorage {
public:
static constexpr base::TimeDelta kMinimumCacheHoldTime = base::Seconds(10);
// The most the entry will be kept in the cache, even if people keep using
// it. (Only effective if clickiness is on, since that requires this for
// some degree of freshness).
static constexpr base::TimeDelta kMaximumCacheHoldTime = base::Seconds(120);
struct CONTENT_EXPORT CachedOriginsInfo {
CachedOriginsInfo();
explicit CachedOriginsInfo(const blink::InterestGroup& group);
CachedOriginsInfo(const CachedOriginsInfo& other) = delete;
CachedOriginsInfo& operator=(const CachedOriginsInfo& other) = delete;
CachedOriginsInfo(CachedOriginsInfo&& other);
CachedOriginsInfo& operator=(CachedOriginsInfo&& other);
~CachedOriginsInfo();
// The name of an owner's latest expiring interest group (of the interest
// groups encountered by the cache via a join or load).
std::string interest_group_name;
// The expiry of the interest group.
base::Time expiry = base::Time::Min();
// The bidding signals origin of the interest group, if it's non-null and
// different from the owner.
std::optional<url::Origin> bidding_signals_origin;
};
explicit InterestGroupCachingStorage(const base::FilePath& path,
bool in_memory);
~InterestGroupCachingStorage();
InterestGroupCachingStorage(const InterestGroupCachingStorage& other) =
delete;
InterestGroupCachingStorage& operator=(
const InterestGroupCachingStorage& other) = delete;
// Gets a list of all interest groups with their bidding information
// associated with the provided owner. If the result is cached,
// a pointer to the in-memory StorageInterestGroups is returned. Otherwise, it
// is loaded fresh from the database or the request is combined with an
// outstanding database call (if an outstanding call exists and the cache has
// not been invalidated since that call).
void GetInterestGroupsForOwner(
const url::Origin& owner,
base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)> callback);
// For a given `owner`, return whether the owner origin and bidding signal
// origin were cached in-memory via UpdateCachedOriginsIfEnabled.
// If the `owner` origin was cached, update `signals_origin` to the one that
// was cached -- or set to nullopt if no bidding signals origin was cached or
// if it would be the same as the owner origin. The cache includes at most one
// entry per origin, and may not reflect the results of interest group
// updates. It's intended to be used for best-effort preconnecting, and should
// not be considered authoritative. It is guaranteed not to contain interest
// groups that have are beyond the max expiration time limit, so preconnecting
// should not leak data the bidder would otherwise have access to, if it so
// desired. That is, manual voluntarily removing or expiring of an interest
// group may not be reflected in the result, but hitting the the global
// interest group lifetime cap will be respected.
bool GetCachedOwnerAndSignalsOrigins(
const url::Origin& owner,
std::optional<url::Origin>& signals_origin);
// Update the cached owner and signal origins for an owner's interest groups
// if kFledgeUsePreconnectCache or kFledgeStartAnticipatoryProcesses are
// enabled and the owner's IGs are still in memory.
void UpdateCachedOriginsIfEnabled(const url::Origin& owner);
// Joins an interest group. If the interest group does not exist, a new one
// is created based on the provided group information. If the interest group
// exists, the existing interest group is overwritten. In either case a join
// record for this interest group is created. Returns the necessary
// information for a k-anon update if the join was successful, or nullopt if
// not.
void JoinInterestGroup(
const blink::InterestGroup& group,
const GURL& main_frame_joining_url,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback);
// Remove the interest group if it exists.
void LeaveInterestGroup(const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
base::OnceClosure callback);
// Removes all interest groups owned by `owner` joined from
// `main_frame_origin` except `interest_groups_to_keep`, if they exist.
void 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);
// Updates the interest group `name` of `owner` with the populated fields of
// `update`.
//
// If it fails for any reason (e.g., the interest group does not exist, or the
// data in `update` is not valid), returns nullopt. Otherwise, returns the
// information required a k-anon update.
void UpdateInterestGroup(
const blink::InterestGroupKey& group_key,
InterestGroupUpdate update,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback);
// Allows the interest group specified by `group_key` to be updated if it was
// last updated before `update_if_older_than`.
void AllowUpdateIfOlderThan(blink::InterestGroupKey group_key,
base::TimeDelta update_if_older_than);
// Report that updating of the interest group with owner `owner` and name
// `name` failed. With the exception of parse failures, the rate limit
// duration for failed updates is shorter than for those that succeed -- for
// successes, UpdateInterestGroup() automatically updates the rate limit
// duration.
void ReportUpdateFailed(const blink::InterestGroupKey& group_key,
bool parse_failure);
// Adds an entry to the bidding history for these interest groups.
void RecordInterestGroupBids(const blink::InterestGroupSet& groups);
// Adds an entry to the win history for this interest group. `ad_json` is a
// piece of opaque data to identify the winning ad.
void RecordInterestGroupWin(const blink::InterestGroupKey& group_key,
const std::string& ad_json);
// Adds an entry to forDebuggingOnly report lockout table if the table is
// empty. Otherwise replaces the existing entry.
void RecordDebugReportLockout(base::Time starting_time,
base::TimeDelta duration);
// Adds an entry to forDebuggingOnly report cooldown table for `origin` if it
// does not exist, otherwise replaces the existing entry.
void RecordDebugReportCooldown(const url::Origin& origin,
base::Time cooldown_start,
DebugReportCooldownType cooldown_type);
// Records a view or a click event. Aggregate time bucketed view and click
// information is provided to bidder's browsing signals in generateBid().
void RecordViewClick(network::AdAuctionEventRecord event_record);
// Invokes `callback` with whether the database has a record of click/view
// events for given combination of provider & eligible origins.
//
// nullopt is passed in in case of an error.
void CheckViewClickInfoInDbForTesting(
url::Origin provider_origin,
url::Origin eligible_origin,
base::OnceCallback<void(std::optional<bool>)> callback);
// Records a K-anonymity update for an interest group. If
// `replace_existing_values` is true, this update will store the new
// `update_time` and `positive_hashed_values`, replacing the interest
// group's existing update time and keys. If `replace_existing_values` is
// false, `positive_hashed_keys` will be added to the existing positive keys
// without updating the stored update time. No value is stored if
// `update_time` is older than the `update_time` already stored in the
// database.
void UpdateKAnonymity(const blink::InterestGroupKey& interest_group_key,
const std::vector<std::string>& positive_hashed_keys,
const base::Time update_time,
bool replace_existing_values = true);
// Gets the last time that the key was reported to the k-anonymity server.
void GetLastKAnonymityReported(
const std::string& hashed_key,
base::OnceCallback<void(std::optional<base::Time>)> callback);
// Updates the last time that the key was reported to the k-anonymity server.
void UpdateLastKAnonymityReported(const std::string& hashed_key);
// Gets a single interest group.
void GetInterestGroup(
const blink::InterestGroupKey& group_key,
base::OnceCallback<void(std::optional<SingleStorageInterestGroup>)>
callback);
// Gets a list of all interest group owners. Each owner will only appear
// once.
void GetAllInterestGroupOwners(
base::OnceCallback<void(std::vector<url::Origin>)> callback);
// For a given owner, gets interest group keys along with their update urls.
// `groups_limit` sets a limit on the maximum number of interest group keys
// that may be returned.
void GetInterestGroupsForUpdate(
const url::Origin& owner,
int groups_limit,
base::OnceCallback<void(std::vector<InterestGroupUpdateParameter>)>
callback);
// Gets lockout and cooldowns of `origins` for sending forDebuggingOnly
// reports.
void GetDebugReportLockoutAndCooldowns(
base::flat_set<url::Origin> origins,
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback);
// Gets lockout and all cooldowns for sending forDebuggingOnly reports.
void GetDebugReportLockoutAndAllCooldowns(
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback);
// Gets a list of all interest group joining origins. Each joining origin
// will only appear once.
void GetAllInterestGroupJoiningOrigins(
base::OnceCallback<void(std::vector<url::Origin>)> callback);
void GetAllInterestGroupOwnerJoinerPairs(
base::OnceCallback<void(std::vector<std::pair<url::Origin, url::Origin>>)>
callback);
void RemoveInterestGroupsMatchingOwnerAndJoiner(url::Origin owner,
url::Origin joining_origin,
base::OnceClosure callback);
// Clear out storage for the matching owning storage key.
void DeleteInterestGroupData(
StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
bool user_initiated_deletion,
base::OnceClosure callback);
// Clear out all interest group storage including k-anonymity store.
void DeleteAllInterestGroupData(base::OnceClosure callback);
// Update the interest group priority.
void SetInterestGroupPriority(const blink::InterestGroupKey& group_key,
double priority);
// Merges `update_priority_signals_overrides` with the currently stored
// priority signals of `group`. Doesn't take the cached overrides from the
// caller, which may already have them, in favor of reading them from the
// database, as the values may have been updated on disk since they were read
// by the caller.
void UpdateInterestGroupPriorityOverrides(
const blink::InterestGroupKey& group_key,
base::flat_map<std::string,
auction_worklet::mojom::PrioritySignalsDoublePtr>
update_priority_signals_overrides);
// Update B&A keys for a coordinator. This function will overwrite any
// existing keys for the coordinator.
void SetBiddingAndAuctionServerKeys(const url::Origin& coordinator,
std::string serialized_keys,
base::Time expiration);
// Load stored B&A server keys for a coordinator along with the keys'
// expiration.
void GetBiddingAndAuctionServerKeys(
const url::Origin& coordinator,
base::OnceCallback<void(std::pair<base::Time, std::string>)> callback);
// Writes all of these keys to the cache, the first vector with
// `is_kanon = true`, and the second vector with `is_kanon = false`.
void WriteHashedKAnonymityKeysToCache(
const std::vector<std::string>& positive_hashed_keys,
const std::vector<std::string>& negative_hashed_keys,
base::Time time_fetched);
// Takes a vector of keys to lookup from the cache. Calls a callback that
// provides two vectors of keys: the first a vector that includes those
// unexpired keys for which it was found in the cache that that key is
// k-anonymous, the second a vector that includes all keys not found in the
// cache.
void LoadPositiveHashedKAnonymityKeysFromCache(
const std::vector<std::string>& keys,
base::Time min_valid_time,
base::OnceCallback<void(InterestGroupStorage::KAnonymityCacheResponse)>
callback);
void GetLastMaintenanceTimeForTesting(
base::RepeatingCallback<void(base::Time)> callback) const;
private:
// Once JoinInterestGroup completes successfully, maybe update the cached
// origins and run the callback.
void OnJoinInterestGroup(
const url::Origin& owner,
CachedOriginsInfo cached_origins_info,
base::OnceCallback<void(std::optional<InterestGroupKanonUpdateParameter>)>
callback,
std::optional<InterestGroupKanonUpdateParameter> update);
// After the async call to load interest groups from storage, cache the result
// in a StorageInterestGroups. Also call
// callbacks in outstanding_interest_group_for_owner_callbacks_ with a
// pointer to the just-stored result if the callbacks reference the same
// version.
void OnLoadInterestGroupsForOwner(
const url::Origin& owner,
uint32_t version,
std::vector<StorageInterestGroup> interest_groups);
void InvalidateCachedInterestGroupsForOwner(const url::Origin& owner);
void InvalidateAllCachedInterestGroups();
void MarkOutstandingInterestGroupLoadResultOutdated(const url::Origin& owner);
// Start a timer that holds a reference to `groups` so that it stays in memory
// for a minimum amount of time (kMinimumCacheHoldTime). If such a timer
// already exists, restart it. Staying in memory is relevant because
// `cached_interest_groups_` contains weak pointers.
void StartTimerForInterestGroupHold(
const url::Origin& owner,
scoped_refptr<StorageInterestGroups> groups);
// Callback for the timers in `timed_holds_of_interest_groups_` in
// order to keep `groups` in memory for a minimum amount of time
// (kMinimumCacheHoldTime). When a timer in `timed_holds_of_interest_groups_`
// is done, make sure to delete the timer.
void OnMinimumCacheHoldTimeCompleted(
const url::Origin& owner,
scoped_refptr<StorageInterestGroups> groups) {
timed_holds_of_interest_groups_.erase(owner);
}
base::SequenceBound<InterestGroupStorage> interest_group_storage_;
// Used to retrieve interest groups that are still in memory (e.g. because
// they're bidding in an auction).
std::map<url::Origin, base::WeakPtr<StorageInterestGroups>>
cached_interest_groups_;
// Holds timers that have references to StorageInterestGroups so that the
// StorageInterestGroups stay in memory for a minimum amount of time
// (kMinimumCacheHoldTime). The timers can also be cancelled early upon cache
// invalidation.
std::map<url::Origin, std::unique_ptr<base::OneShotTimer>>
timed_holds_of_interest_groups_;
// Holds callbacks to be run once a load from the database
// (GetInterestGroupsForOwner) is complete. Callbacks are keyed by version
// number in addition to owner so that OnLoadInterestGroupsForOwner does not
// load callbacks asking for a later version of the interest groups.
std::map<std::pair<url::Origin, uint32_t>,
base::queue<
base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)>>>
interest_groups_sequenced_callbacks_;
// For each owner, store the current data version for interest group results.
// A version is incremented when an owner's interest group results are
// invalidated. The versions are reset when
// interest_groups_sequenced_callbacks_ becomes empty.
std::map<url::Origin, uint32_t> valid_interest_group_versions_;
// For each owner for which we've run UpdateCachedOriginsIfEnabled,
// hold onto the owner origin and the origin of the bidding signals url for
// the purpose of preconnecting to them or starting worklets for them in later
// auctions. CachedOriginsInfo tracks the latest expiring interest group that
// we know about to prevent preconnecting to origins no longer in the
// database. Owners may be cleared from the map if the corresponding interest
// group is left or expired. A flat map is used because the number of interest
// group owners is expected to be relatively small.
base::flat_map<url::Origin, CachedOriginsInfo>
cached_owners_and_signals_origins_;
base::WeakPtrFactory<InterestGroupCachingStorage> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_CACHING_STORAGE_H_
|