blob: 0a126c07fd7d80295cae1c1352a07c18fafed31e [file] [log] [blame]
// Copyright 2021 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_MANAGER_IMPL_H_
#define CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_MANAGER_IMPL_H_
#include <list>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/observer_list.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/browser/interest_group/auction_process_manager.h"
#include "content/browser/interest_group/interest_group_k_anonymity_manager.h"
#include "content/browser/interest_group/interest_group_permissions_checker.h"
#include "content/browser/interest_group/interest_group_update.h"
#include "content/browser/interest_group/interest_group_update_manager.h"
#include "content/browser/interest_group/storage_interest_group.h"
#include "content/common/content_export.h"
#include "content/public/browser/interest_group_manager.h"
#include "content/public/browser/k_anonymity_service_delegate.h"
#include "content/public/browser/storage_partition.h"
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom-forward.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
#include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.h"
#include "url/origin.h"
namespace base {
class FilePath;
} // namespace base
namespace content {
class InterestGroupStorage;
// InterestGroupManager is a per-StoragePartition class that owns shared
// state needed to run FLEDGE auctions. It lives on the UI thread.
//
// It acts as a proxy to access an InterestGroupStorage, which lives off-thread
// as it performs blocking file IO when backed by on-disk storage.
class CONTENT_EXPORT InterestGroupManagerImpl : public InterestGroupManager {
public:
// Controls how auction worklets will be run. kDedicated will use
// fully-isolated utility processes solely for worklet. kInRenderer will
// re-use regular renderers following the normal site isolation policy.
enum class ProcessMode { kDedicated, kInRenderer };
// Creates an interest group manager using the provided directory path for
// persistent storage. If `in_memory` is true the path is ignored and only
// in-memory storage is used.
explicit InterestGroupManagerImpl(
const base::FilePath& path,
bool in_memory,
ProcessMode process_mode,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
KAnonymityServiceDelegate* k_anonymity_service);
~InterestGroupManagerImpl() override;
InterestGroupManagerImpl(const InterestGroupManagerImpl& other) = delete;
InterestGroupManagerImpl& operator=(const InterestGroupManagerImpl& other) =
delete;
class CONTENT_EXPORT InterestGroupObserverInterface
: public base::CheckedObserver {
public:
enum AccessType { kJoin, kLeave, kUpdate, kLoaded, kBid, kWin };
virtual void OnInterestGroupAccessed(const base::Time& access_time,
AccessType type,
const std::string& owner_origin,
const std::string& name) = 0;
};
// InterestGroupManager overrides:
void GetAllInterestGroupJoiningOrigins(
base::OnceCallback<void(std::vector<url::Origin>)> callback) override;
void GetAllInterestGroupDataKeys(
base::OnceCallback<void(std::vector<InterestGroupDataKey>)> callback)
override;
void RemoveInterestGroupsByDataKey(InterestGroupDataKey data_key,
base::OnceClosure callback) override;
/******** Proxy function calls to InterestGroupsStorage **********/
// Checks if `frame_origin` can join the specified InterestGroup, performing
// .well-known fetches if needed. If joining is allowed, then joins the
// interest group.
//
// `network_isolation_key` must be the NetworkIsolationKey associated with
// `url_loader_factory`.
//
// `url_loader_factory` is the factory for renderer frame where
// navigator.joinInterestGroup() was invoked, and will be used for the
// .well-known fetch if one is needed.
//
// `report_result_only`, if true, results in calling `callback` with the
// result of the permissions check, but not actually joining the interest
// group, regardless of success or failure. This is used to avoid leaking
// fingerprinting information if the join operation is disallowed by the
// browser configuration (e.g., 3P cookie blocking).
//
// See JoinInterestGroup() for more details on how the join operation is
// performed.
void CheckPermissionsAndJoinInterestGroup(
blink::InterestGroup group,
const GURL& joining_url,
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
bool report_result_only,
network::mojom::URLLoaderFactory& url_loader_factory,
blink::mojom::AdAuctionService::JoinInterestGroupCallback callback);
// Same as CheckPermissionsAndJoinInterestGroup(), except for a leave
// operation.
void CheckPermissionsAndLeaveInterestGroup(
const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
bool report_result_only,
network::mojom::URLLoaderFactory& url_loader_factory,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback);
// 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.
void JoinInterestGroup(blink::InterestGroup group, const GURL& joining_url);
// Remove the interest group if it exists.
void LeaveInterestGroup(const blink::InterestGroupKey& group_key,
const url::Origin& main_frame);
// Loads all interest groups owned by `owner`, then updates their definitions
// by fetching their `dailyUpdateUrl`. Interest group updates that fail to
// load or validate are skipped, but other updates will proceed.
void UpdateInterestGroupsOfOwner(
const url::Origin& owner,
network::mojom::ClientSecurityStatePtr client_security_state);
// Like UpdateInterestGroupsOfOwner(), but handles multiple interest group
// owners.
//
// The list is shuffled in-place to ensure fairness.
void UpdateInterestGroupsOfOwners(
base::span<url::Origin> owners,
network::mojom::ClientSecurityStatePtr client_security_state);
// For testing *only*; changes the maximum amount of time that the update
// process can run before it gets cancelled for taking too long.
void set_max_update_round_duration_for_testing(base::TimeDelta delta) {
update_manager_.set_max_update_round_duration_for_testing(
delta); // IN-TEST
}
// For testing *only*; changes the maximum number of groups that can be
// updated at the same time.
void set_max_parallel_updates_for_testing(int max_parallel_updates) {
update_manager_.set_max_parallel_updates_for_testing( // IN-TEST
max_parallel_updates);
}
// Adds an entry to the bidding history for this interest group.
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);
// Reports the ad keys to the k-anonymity service. Should be called when
// FLEDGE selects an ad.
void RegisterAdKeysAsJoined(base::flat_set<std::string> keys);
// Gets a single interest group.
void GetInterestGroup(
const blink::InterestGroupKey& group_key,
base::OnceCallback<void(absl::optional<StorageInterestGroup>)> callback);
// Gets a single interest group.
void GetInterestGroup(
const url::Origin& owner,
const std::string& name,
base::OnceCallback<void(absl::optional<StorageInterestGroup>)> 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);
// Gets a list of all interest groups with their bidding information
// associated with the provided owner.
void GetInterestGroupsForOwner(
const url::Origin& owner,
base::OnceCallback<void(std::vector<StorageInterestGroup>)> callback);
// Clear out storage for the matching owning storage key. If the matcher is
// empty then apply to all storage keys.
void DeleteInterestGroupData(
StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
base::OnceClosure completion_callback);
// Completely delete all interest group data, including k-anonymity data that
// is not cleared by DeleteInterestGroupData.
void DeleteAllInterestGroupData(base::OnceClosure completion_callback);
// Get the last maintenance time from the underlying InterestGroupStorage.
void GetLastMaintenanceTimeForTesting(
base::RepeatingCallback<void(base::Time)> callback) const;
// Enqueues report requests. Using `client_security_state` when fetching
// report URLs from the network.
void EnqueueReports(
const std::vector<GURL>& report_urls,
const std::vector<GURL>& debug_win_report_urls,
const std::vector<GURL>& debug_loss_report_urls,
const url::Origin& frame_origin,
network::mojom::ClientSecurityStatePtr client_security_state,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
// Update the interest group priority.
void SetInterestGroupPriority(const blink::InterestGroupKey& group,
double priority);
// Merges `update_priority_signals_overrides` with the previous priority
// signals of `group`.
void UpdateInterestGroupPriorityOverrides(
const blink::InterestGroupKey& group_key,
base::flat_map<std::string,
auction_worklet::mojom::PrioritySignalsDoublePtr>
update_priority_signals_overrides);
// Clears the InterestGroupPermissionsChecker's cache of the results of
// .well-known fetches.
void ClearPermissionsCache();
AuctionProcessManager& auction_process_manager() {
return *auction_process_manager_;
}
void AddInterestGroupObserver(InterestGroupObserverInterface* observer) {
observers_.AddObserver(observer);
}
void RemoveInterestGroupObserver(InterestGroupObserverInterface* observer) {
observers_.RemoveObserver(observer);
}
// Allows the AuctionProcessManager to be overridden in unit tests, both to
// allow not creating a new process, and mocking out the Mojo service
// interface.
void set_auction_process_manager_for_testing(
std::unique_ptr<AuctionProcessManager> auction_process_manager) {
auction_process_manager_ = std::move(auction_process_manager);
}
// For testing *only*; changes the maximum number of active report requests
// at a time.
void set_max_active_report_requests_for_testing(
int max_active_report_requests) {
max_active_report_requests_ = max_active_report_requests;
}
// For testing *only*; changes the maximum number of report requests that can
// be stored in `report_requests_` queue.
void set_max_report_queue_length_for_testing(int max_queue_length) {
max_report_queue_length_ = max_queue_length;
}
// For testing *only*; changes `max_reporting_round_duration_`.
void set_max_reporting_round_duration_for_testing(
base::TimeDelta max_reporting_round_duration) {
max_reporting_round_duration_ = max_reporting_round_duration;
}
// For testing *only*; changes the time interval to wait before sending the
// next report after sending one.
void set_reporting_interval_for_testing(base::TimeDelta interval) {
reporting_interval_ = interval;
}
size_t report_queue_length_for_testing() const {
return report_requests_.size();
}
// Handles daily k-anonymity updates for the interest group. Triggers an
// update request for the k-anonymity of all parts of the interest group
// (including ads). Also reports membership in the interest group to the
// k-anonymity of interest-group service.
void QueueKAnonymityUpdateForInterestGroup(const StorageInterestGroup& group);
// Records K-anonymity to the database.
void UpdateKAnonymity(const StorageInterestGroup::KAnonymityData& data);
// Gets the last time that the key was reported to the k-anonymity server.
void GetLastKAnonymityReported(
const std::string& key,
base::OnceCallback<void(absl::optional<base::Time>)> callback);
// Updates the last time that the key was reported to the k-anonymity server.
void UpdateLastKAnonymityReported(const std::string& key);
InterestGroupPermissionsChecker& permissions_checker_for_testing() {
return permissions_checker_;
}
void set_k_anonymity_manager_for_testing(
std::unique_ptr<InterestGroupKAnonymityManager> k_anonymity_manager) {
k_anonymity_manager_ = std::move(k_anonymity_manager);
}
private:
// InterestGroupUpdateManager calls private members to write updates to the
// database.
friend class InterestGroupUpdateManager;
struct ReportRequest {
ReportRequest();
~ReportRequest();
// Used to fetch the report URL.
std::unique_ptr<network::SimpleURLLoader> simple_url_loader;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
// Used for Uma histograms. These are build-time constants contained within
// the binary, so no need for anything to own them.
const char* name;
int request_url_size_bytes;
};
// Callbacks for CheckPermissionsAndJoinInterestGroup() and
// CheckPermissionsAndLeaveInterestGroup(), respectively. Call
// JoinInterestGroup() and LeaveInterestGroup() if the results of the
// permissions check allows it.
void OnJoinInterestGroupPermissionsChecked(
blink::InterestGroup group,
const GURL& joining_url,
bool report_result_only,
blink::mojom::AdAuctionService::JoinInterestGroupCallback callback,
bool can_join);
void OnLeaveInterestGroupPermissionsChecked(
const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
bool report_result_only,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback,
bool can_leave);
// Like GetInterestGroupsForOwner(), but doesn't return any interest groups
// that are currently rate-limited for updates. Additionally, this will update
// the `next_update_after` field such that a subsequent
// GetInterestGroupsForUpdate() call with the same `owner` won't return
// anything until after the success rate limit period passes.
//
// `groups_limit` sets a limit on the maximum number of interest groups that
// may be returned.
//
// To be called only by `update_manager_`.
void GetInterestGroupsForUpdate(
const url::Origin& owner,
int groups_limit,
base::OnceCallback<void(std::vector<StorageInterestGroup>)> callback);
// Updates the interest group of the same name based on the information in
// the provided group. This does not update the interest group expiration
// time or user bidding signals.
//
// `callback` is invoked asynchronously on completion, with a bool indicating
// success or failure.
//
// To be called only by `update_manager_`.
void UpdateInterestGroup(const blink::InterestGroupKey& group_key,
InterestGroupUpdate update,
base::OnceCallback<void(bool)> callback);
// Modifies the update rate limits stored in the database, with a longer delay
// for parse failure.
//
// To be called only by `update_manager_`.
void ReportUpdateFailed(const blink::InterestGroupKey& group_key,
bool parse_failure);
void NotifyInterestGroupAccessed(
InterestGroupObserverInterface::AccessType type,
const std::string& owner_origin,
const std::string& name);
void OnGetInterestGroupsComplete(
base::OnceCallback<void(std::vector<StorageInterestGroup>)> callback,
std::vector<StorageInterestGroup> groups);
// Enqueues each of `report_urls` to the `report_requests_` queue.
void EnqueueReportsInternal(
const std::vector<GURL>& report_urls,
const url::Origin& frame_origin,
const network::mojom::ClientSecurityStatePtr& client_security_state,
const char* name,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
// Dequeues and sends the first report request in `report_requests_` queue,
// if the queue is not empty.
void TrySendingOneReport();
// Invoked when a report request completed.
void OnOneReportSent(
std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
scoped_refptr<net::HttpResponseHeaders> response_headers);
// Clears `report_requests_`. Does not abort currently pending requests.
void TimeoutReports();
// A version of QueueKAnonymityUpdateForInterestGroup() called from
// JoinInterestGroup which passes the group in an optional (the group must
// always exist). Called from JoinInterestGroup.
void QueueKAnonymityUpdateForInterestGroupFromJoinInterestGroup(
absl::optional<StorageInterestGroup> maybe_group);
// Owns and manages access to the InterestGroupStorage living on a different
// thread.
base::SequenceBound<InterestGroupStorage> impl_;
// Stored as pointer so that tests can override it.
std::unique_ptr<AuctionProcessManager> auction_process_manager_;
base::ObserverList<InterestGroupObserverInterface> observers_;
// Manages the logic required to support UpdateInterestGroupsOfOwner().
//
// InterestGroupUpdateManager keeps a pointer to this InterestGroupManagerImpl
// to make database writes via calls to GetInterestGroupsForUpdate(),
// UpdateInterestGroup(), and ReportUpdateFailed().
//
// Therefore, `update_manager_` *must* be declared after fields used by those
// methods so that updates are cancelled before those fields are destroyed.
InterestGroupUpdateManager update_manager_;
// Manages the logic required to support k-anonymity updates.
//
// InterestGroupKAnonymityManager keeps a pointer to this
// InterestGroupManagerImpl to make database reads and writes....
//
// Therefore, `k_anonymity_manager_` *must* be declared after fields used by
// those methods so that k-anonymity operations are cancelled before those
// fields are destroyed.
// Stored as pointer so that tests can override it.
std::unique_ptr<InterestGroupKAnonymityManager> k_anonymity_manager_;
// Checks if a frame can join or leave an interest group. Global so that
// pending operations can continue after a page has been navigate away from.
InterestGroupPermissionsChecker permissions_checker_;
// The queue of report requests. Empty the queue if it's size is larger than
// `max_report_queue_length` at the time of adding new entries.
base::circular_deque<std::unique_ptr<ReportRequest>> report_requests_;
// Current number of active report requests. Includes requests that completed
// within the last `kReportingInterval`, each of which should have a pending
// delayed task to invoke TrySendingOneReport().
int num_active_ = 0;
// The maximum number of active report requests at a time.
//
// Should *only* be changed by tests.
int max_active_report_requests_;
// The maximum number of report requests that can be stored in queue
// `report_requests_`.
//
// Should *only* be changed by tests.
int max_report_queue_length_;
// The time interval to wait before sending the next report request after
// sending one.
//
// Should *only* be changed by tests.
base::TimeDelta reporting_interval_;
// The maximum amount of time that the reporting process can run before the
// report queue is cleared due to taking too long.
//
// Should *only* be changed by tests.
base::TimeDelta max_reporting_round_duration_;
// Used to clear all pending reports in the queue if reporting takes too long.
// Started when sending reports starts. Stopped once all reports are sent.
// When the timer triggers, all reports are aborted.
//
// The resulting behavior is that if reports are continuously being sent for
// too long, possibly from multiple auctions, all reports are timed out.
base::OneShotTimer timeout_timer_;
base::WeakPtrFactory<InterestGroupManagerImpl> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_MANAGER_IMPL_H_