| // Copyright 2022 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_UPDATE_MANAGER_H_ |
| #define CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_UPDATE_MANAGER_H_ |
| |
| #include <stddef.h> |
| |
| #include <list> |
| #include <memory> |
| |
| #include "base/containers/circular_deque.h" |
| #include "base/containers/flat_map.h" |
| #include "base/containers/span.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "content/browser/interest_group/interest_group_update.h" |
| #include "content/browser/interest_group/storage_interest_group.h" |
| #include "content/common/content_export.h" |
| #include "services/data_decoder/public/cpp/data_decoder.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/mojom/client_security_state.mojom.h" |
| #include "third_party/blink/public/common/interest_group/interest_group.h" |
| #include "url/origin.h" |
| |
| namespace network { |
| |
| class SimpleURLLoader; |
| |
| } // namespace network |
| |
| namespace content { |
| |
| class InterestGroupManagerImpl; |
| |
| // Implements the interest group update functionality of |
| // InterestGroupManagerImpl. |
| // |
| // Updates the interest groups of each owner separately by queueing the owner |
| // origins whose interest group should be updated. |
| // |
| // Updates are rate-limited; interest groups don't get updated if they've been |
| // updated recently. |
| class CONTENT_EXPORT InterestGroupUpdateManager { |
| public: |
| // `manager` should be the InterestGroupManagerImpl that owns this |
| // InterestGroupManager. |
| InterestGroupUpdateManager( |
| InterestGroupManagerImpl* manager, |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); |
| ~InterestGroupUpdateManager(); |
| |
| // 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); |
| |
| // 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); |
| |
| private: |
| using UrlLoadersList = std::list<std::unique_ptr<network::SimpleURLLoader>>; |
| |
| enum class UpdateDelayType { |
| // If the update fails due to a disconnected network, the retry can happen |
| // immediately. |
| kNoInternet, |
| |
| // If the update failed due to another network error, it can be retried |
| // after a small delay (~1 hour). |
| kNetFailure, |
| |
| // If the update failed due to an issue with the response (i.e. bad JSON), |
| // the interest group can only be updated again after a large delay (~1 |
| // day). |
| kParseFailure |
| }; |
| |
| // A queue of interest group owners that require updating, along with the |
| // ClientSecurityState that was used to request those updates. |
| class OwnersToUpdate { |
| public: |
| OwnersToUpdate(); |
| ~OwnersToUpdate(); |
| |
| // Returns true iff there are no more interest group owners to process. |
| bool Empty() const; |
| |
| // Returns the owner origin that is currently being updated. Requires |
| // !Empty(). |
| const url::Origin& FrontOwner() const; |
| |
| // Returns the ClientSecurityState that was used to request the current |
| // owner update. Requires !Empty(). |
| network::mojom::ClientSecurityStatePtr FrontSecurityState() const; |
| |
| // If `owner` isn't already in the queue, enqueues `owner` for processing, |
| // using `client_security_state` when fetching `owner`'s interest group |
| // updates from the network, and returns true. Otherwise, returns false. |
| // |
| // Callers *must* call MaybeContinueUpdatingCurrentOwner() after Enqueue() |
| // to ensure the that `owner` gets processed. |
| bool Enqueue(const url::Origin& owner, |
| network::mojom::ClientSecurityStatePtr client_security_state); |
| |
| // Removes the current `owner` and its associated ClientSecurityState from |
| // the front of the queue. Requires !Empty(). |
| void PopFront(); |
| |
| // Removes all queued interest group owners. |
| void Clear(); |
| |
| private: |
| // The queue of owners whose interest groups need updating. |
| base::circular_deque<url::Origin> owners_to_update_; |
| |
| // For each interest group owner in `owners_to_update_`, we keep the |
| // ClientSecurityState that was used to make the update request. |
| base::flat_map<url::Origin, network::mojom::ClientSecurityStatePtr> |
| security_state_map_; |
| }; |
| |
| // Processes the next set of interest groups to update. |
| // |
| // If existing update work is already in-progress (that is, if |
| // `num_in_flight_updates_` isn't 0, or `waiting_on_db_read_` is true), quits |
| // immediately to avoid duplicating update work. |
| void MaybeContinueUpdatingCurrentOwner(); |
| |
| // 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. |
| void GetInterestGroupsForUpdate( |
| const url::Origin& owner, |
| base::OnceCallback<void(std::vector<StorageInterestGroup>)> callback); |
| |
| void DidUpdateInterestGroupsOfOwnerDbLoad( |
| url::Origin owner, |
| std::vector<StorageInterestGroup> storage_groups); |
| void DidUpdateInterestGroupsOfOwnerNetFetch( |
| UrlLoadersList::iterator simple_url_loader, |
| blink::InterestGroupKey group_key, |
| std::unique_ptr<std::string> fetch_body); |
| void DidUpdateInterestGroupsOfOwnerJsonParse( |
| blink::InterestGroupKey group_key, |
| data_decoder::DataDecoder::ValueOrError result); |
| |
| // Updates the specified interest group with the information in `update`. |
| // On completion, invoked OnUpdateInterestGroupCompleted() asynchronously, |
| // with a bool indicated whether or not the update succeeded. |
| void UpdateInterestGroup(const blink::InterestGroupKey& group_key, |
| InterestGroupUpdate update); |
| |
| // This method finishes the current update and invokes either |
| // ReportUpdateFailed() or OnOneUpdateCompleted(). |
| void OnUpdateInterestGroupCompleted(const blink::InterestGroupKey& group_key, |
| bool success); |
| |
| // Called after a single interest group update finishes. Should be called |
| // after any database operations (if performed) or clearing of |
| // `owners_to_update_`. |
| void OnOneUpdateCompleted(); |
| |
| // Processes update failure, and if `delay_type` isn't kNoInternet, modifies |
| // the update rate limits stored in the database. |
| // |
| // This method finishes the current update and calls OnOneUpdateCompleted(). |
| void ReportUpdateFailed(const blink::InterestGroupKey& group_key, |
| UpdateDelayType delay_type); |
| |
| // An unowned pointer to the InterestGroupManagerImpl that owns this |
| // InterestGroupUpdateManager. Used as an intermediary to talk to the |
| // database. |
| raw_ptr<InterestGroupManagerImpl> manager_; |
| |
| // ClientSecurityState that was used to request those updates. |
| OwnersToUpdate owners_to_update_; |
| |
| // The number of interest group updates for the current interest group owner |
| // (that is, owners_to_update_.front()) that are still in the process of |
| // updating. |
| // |
| // To avoid updating the same interest group twice in succession, this should |
| // only be decremented after a write to the DB, except for network |
| // disconnection, where immediate retry is allowed. |
| // |
| // NOTE: This will be 0 while reading from the database, as at that point, we |
| // don't yet know how many interest groups need to be updated for the current |
| // owner. However, `waiting_on_db_read_` will be true in that situation. |
| int num_in_flight_updates_ = 0; |
| |
| // Will be true while we are waiting for the database to load the set of |
| // interest groups of the current owner that require updating, and false |
| // otherwise. At most one interest group read may happen at a given time. |
| // |
| // By checking `waiting_on_db_read_` and `num_in_flight_updates_`, |
| // MaybeContinueUpdatingCurrentOwner() can check if update work is already |
| // ongoing, to avoid erroroneously having multiple update jobs occurring at |
| // the same time. Although the core update logic in InterestGroupUpdateManager |
| // occurs on the UI thread, tasks like reading from the database, network |
| // fetches, and JSON parsing happen asynchronously, so errors caused by |
| // interleaving are possible without such guarding. |
| // |
| // Note that this is not affected by whether or not we are writing to the |
| // database (including any reads as part of that write), since these are |
| // covered by `num_in_flight_updates_`. Although the actual write to disk |
| // occurs asynchronsously, and we don't wait for completion, DB operations |
| // occur in-order, so the next read should reflect the results of the previous |
| // write. |
| bool waiting_on_db_read_ = false; |
| |
| // The maximum amount of time that the update process can run before it gets |
| // cancelled for taking too long. |
| // |
| // Should *only* be changed by tests. |
| base::TimeDelta max_update_round_duration_; |
| |
| // The maximum number of groups that can be updated at the same time. |
| // |
| // Should *only* be changed by tests. |
| int max_parallel_updates_; |
| |
| // The last time we started a round of updating; used to cancel long-running |
| // updates. |
| base::TimeTicks last_update_started_ = base::TimeTicks::Min(); |
| |
| // Used for fetching interest group update JSON over the network. |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; |
| |
| // All active network requests -- active requests will be cancelled when |
| // destroyed. |
| UrlLoadersList url_loaders_; |
| |
| // TODO(crbug.com/1186444): Do we need to test InterestGroupManager |
| // destruction during update? If so, how? |
| base::WeakPtrFactory<InterestGroupUpdateManager> weak_factory_{this}; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_INTEREST_GROUP_INTEREST_GROUP_UPDATE_MANAGER_H_ |