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

content / browser / interest_group / interest_group_caching_storage.h [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.

#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/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);
  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 in previous calls to
  // GetInterestGroupsForOwner and JoinInterestGroup. 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);

  // 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 last_report_sent_time);
  // 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 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 for sending forDebuggingOnly reports.
  void GetDebugReportLockout(
      base::OnceCallback<void(std::optional<base::Time>)> callback);

  // Gets lockout and cooldown for sending forDebuggingOnly reports.
  void GetDebugReportLockoutAndCooldowns(
      base::flat_set<url::Origin> origins,
      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,
      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,
      const std::vector<BiddingAndAuctionServerKey>& 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::vector<BiddingAndAuctionServerKey>>)>
          callback);

  void GetLastMaintenanceTimeForTesting(
      base::RepeatingCallback<void(base::Time)> callback) const;

 private:
  // Once JoinInterestGroup completes successfully, maybe cache the associated
  // CachedOriginsInfo 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);

  // This callback is used once interest groups are loaded if
  // kFledgeUseInterestGroupCache is disabled. Information may still be cached
  // if kFledgeUsePreconnectCache is enabled.
  void OnLoadInterestGroupsForOwnerNoCachingIGs(
      const url::Origin& owner,
      base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)> callback,
      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.
  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);
  }

  // Update `cached_owners_and_signals_origins_` for an owner's interest groups
  // if kFledgeUsePreconnectCache is enabled.
  void UpdateCachedOriginsIfEnabled(
      const url::Origin& owner,
      const std::vector<StorageInterestGroup>& interest_groups);

  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 loaded or joined interest groups,
  // hold onto the owner origin and origin of the bidding signals url for the
  // purpose of preconnecting to 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_