| // 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_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_ |
| #define CONTENT_BROWSER_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_ |
| |
| #include <map> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "content/common/content_export.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| extern const char kURNUUIDprefix[]; |
| |
| struct AdAuctionData { |
| url::Origin interest_group_owner; |
| std::string interest_group_name; |
| }; |
| |
| using ReportingMetadata = blink::mojom::FencedFrameReporting; |
| using SharedStorageReportingMap = base::flat_map<std::string, ::GURL>; |
| |
| // Keeps a mapping of fenced frames URN:UUID and URL. Also keeps a set of |
| // pending mapped URN:UUIDs to support asynchronous mapping. See |
| // https://github.com/WICG/fenced-frame/blob/master/explainer/opaque_src.md |
| class CONTENT_EXPORT FencedFrameURLMapping { |
| public: |
| // The metadata for the shared storage runURLSelectionOperation's budget, |
| // which includes the shared storage's origin and the amount of budget to |
| // charge when a fenced frame that originates from the URN is navigating a top |
| // frame. Before the fenced frame results in a top navigation, this |
| // `SharedStorageBudgetMetadata` will be stored/associated with the URN inside |
| // the `FencedFrameURLMapping`. |
| struct CONTENT_EXPORT SharedStorageBudgetMetadata { |
| url::Origin origin; |
| mutable double budget_to_charge = 0; |
| }; |
| |
| // The runURLSelectionOperation's url mapping result. It contains the mapped |
| // url and the `SharedStorageBudgetMetadata`. |
| struct CONTENT_EXPORT SharedStorageURNMappingResult { |
| GURL mapped_url; |
| SharedStorageBudgetMetadata budget_metadata; |
| SharedStorageReportingMap reporting_map; |
| SharedStorageURNMappingResult(); |
| SharedStorageURNMappingResult(GURL mapped_url, |
| SharedStorageBudgetMetadata budget_metadata, |
| SharedStorageReportingMap reporting_map); |
| ~SharedStorageURNMappingResult(); |
| }; |
| |
| // When the result of an ad auction is a main ad URL with a set of ad |
| // component URLs (instead of just a single ad URL), a URN that maps to the |
| // main ad URL needs to be loaded in a (parent) fenced frame, and then that |
| // frame needs to have access to a new list of URNs, one for each ad component |
| // URL, which it can then load in its own child fenced frames. |
| // |
| // To do this, the parent fenced frame needs two things, on commit: |
| // 1) A list of URNs for the ad components, provided to the parent fenced |
| // frame via a Javascript API. |
| // 2) Its URN to URL mapping needs to be updated to map those URNs to the ad |
| // component URLs returned by the auction. |
| // |
| // This class has functions that do both of these. GetURNs() returns the list |
| // of URNs that need to be provided to the parent fenced frame so they are |
| // accessible by the frame's scripts, and AddToMapping(), when invoked on the |
| // parent fenced frame's FencedFrameURLMapping (not that of the frame that |
| // actually ran the auction) adds those URNs and their corresponding URLs to |
| // that mapping. |
| class CONTENT_EXPORT PendingAdComponentsMap { |
| public: |
| PendingAdComponentsMap(const PendingAdComponentsMap&); |
| PendingAdComponentsMap(PendingAdComponentsMap&&); |
| |
| ~PendingAdComponentsMap(); |
| |
| PendingAdComponentsMap& operator=(const PendingAdComponentsMap&); |
| PendingAdComponentsMap& operator=(PendingAdComponentsMap&&); |
| |
| // Returns the ordered list of URNs in this map. |
| std::vector<GURL> GetURNs() const; |
| |
| // Exports URN to URL mappings to the passed in mapping. Generally only |
| // called once per PendingAdComponentsMap, on the mapping associated with a |
| // frame being navigated to a URN. Calling this twice with the |
| // PendingAdComponentsMap on the same FencedFrameURLMapping will assert, |
| // since it will result in adding the same URNs twice to the same mapping. |
| void ExportToMapping(FencedFrameURLMapping& mapping) const; |
| |
| private: |
| friend class FencedFrameURLMapping; |
| |
| // Contains an ad component URN and the URL it maps to. |
| struct AdComponent { |
| GURL urn; |
| GURL url; |
| }; |
| |
| explicit PendingAdComponentsMap(const std::vector<GURL>& ad_component_urls); |
| |
| std::vector<AdComponent> component_ads_; |
| }; |
| |
| private: |
| // Contains the fenced frame configuration a particular URN is mapped to. |
| // This specifies how to generate a set of `FencedFrameProperties` to install |
| // at navigation commit time. |
| // Most properties are copied over directly from the configuration, but some |
| // require some additional processing (e.g. `ad_component_urls`). |
| // |
| // TODO(https://crbug.com/1260472): In order to only report FLEDGE results if |
| // an ad is shown, InterestGroup reporting will also need to be wired up here. |
| struct MapInfo { |
| MapInfo(); |
| explicit MapInfo(const GURL& url); |
| MapInfo(const GURL& url, |
| const SharedStorageBudgetMetadata& shared_storage_budget_metadata, |
| const ReportingMetadata& reporting_metadata = ReportingMetadata()); |
| MapInfo(const MapInfo&); |
| MapInfo(MapInfo&&); |
| ~MapInfo(); |
| |
| MapInfo& operator=(const MapInfo&); |
| MapInfo& operator=(MapInfo&&); |
| |
| GURL mapped_url; |
| |
| // Extra data set if `mapped_url` is the result of a FLEDGE auction. Used |
| // to fill in `AdAuctionDocumentData` for the fenced frame that navigates |
| // to `mapped_url`. |
| absl::optional<AdAuctionData> ad_auction_data; |
| |
| // Ad component URLs if `mapped_url` is the result of a FLEDGE auction. When |
| // a fenced frame navigates to `mapped_url`, these will be mapped to URNs |
| // themselves, and those URNs will be provided to the fenced frame. |
| absl::optional<std::vector<GURL>> ad_component_urls; |
| |
| // Contains the metadata needed for shared storage budget charging. Will be |
| // initialized to absl::nullopt if the associated URN is not generated from |
| // shared storage. Its `budget_to_charge` can be updated to 0 when the |
| // budget is charged. |
| absl::optional<SharedStorageBudgetMetadata> shared_storage_budget_metadata; |
| |
| // If reporting events from fenced frames are registered, then this |
| // information gets filled here. |
| ReportingMetadata reporting_metadata; |
| }; |
| |
| public: |
| // Contains a set of fenced frame properties. These are generated at |
| // urn:uuid navigation time according to a fenced frame configuration, |
| // specified by `MapInfo` above. |
| // Most of these are copied from `MapInfo` directly, but some are generated |
| // by another transformation, e.g.: |
| // * We generate urns for the urls in `ad_component_urls` and store them in |
| // `pending_ad_components_map`. |
| // * We generate a pointer to `shared_storage_budget_metadata` and store it in |
| // `shared_storage_budget_metadata`, because it should only take effect once |
| // across all fenced frames navigated to a particular configuration. |
| // These `FencedFrameProperties` are stored in the fenced frame root |
| // `FrameTreeNode`, and live between embedder-initiated fenced frame |
| // navigations. |
| struct FencedFrameProperties { |
| explicit FencedFrameProperties(const MapInfo& map_info); |
| FencedFrameProperties(const FencedFrameProperties&); |
| FencedFrameProperties(FencedFrameProperties&&); |
| ~FencedFrameProperties(); |
| |
| FencedFrameProperties& operator=(const FencedFrameProperties&); |
| FencedFrameProperties& operator=(FencedFrameProperties&&); |
| |
| GURL mapped_url; |
| |
| absl::optional<AdAuctionData> ad_auction_data; |
| |
| // urn/url mappings for ad components. These are inserted into the |
| // fenced frame page's urn/url mapping when the urn navigation commits. |
| absl::optional<PendingAdComponentsMap> pending_ad_components_map; |
| |
| // This can only be possibly set for the outermost fenced frame root, |
| // because selectURL() is disallowed inside fenced frame, and the URN |
| // generated outside the a fenced frame cannot be recognized from inside, |
| // so a nested fenced frame can never navigate to a shared storage |
| // generated URN. |
| // |
| // This pointer to the outermost page's FencedFrameURLMapping is copied |
| // into the fenced frame root's FrameTreeNode. This is safe because a page |
| // will outlive any NavigationRequest occurring in fenced frames in the |
| // page. |
| absl::optional<raw_ptr<const SharedStorageBudgetMetadata>> |
| shared_storage_budget_metadata; |
| |
| ReportingMetadata reporting_metadata; |
| }; |
| |
| class MappingResultObserver { |
| public: |
| virtual ~MappingResultObserver() = default; |
| |
| // Called as soon as the URN mapping decision is made. |
| // |
| // On success, `properties` will be populated with the properties bound to |
| // the urn:uuid. |
| virtual void OnFencedFrameURLMappingComplete( |
| const absl::optional<FencedFrameProperties>& properties) = 0; |
| }; |
| |
| FencedFrameURLMapping(); |
| ~FencedFrameURLMapping(); |
| FencedFrameURLMapping(FencedFrameURLMapping&) = delete; |
| FencedFrameURLMapping& operator=(FencedFrameURLMapping&) = delete; |
| |
| // Adds a mapping for |url| to a URN:UUID that will be generated by this |
| // function. Should only be invoked with a valid URL which is one of the |
| // "potentially trustworthy URLs". |
| // Mapping will not be added and return absl::nullopt if number of mappings |
| // has reached limit. Enforcing a limit on number of mappings prevents |
| // excessive memory consumption. |
| // `reporting_metadata` will contain a `ReportingMetadata` that populates |
| // any metadata invoked by the worklet using `RegisterAdBeacon`. See |
| // https://github.com/WICG/turtledove/blob/main/Fenced_Frames_Ads_Reporting.md#registeradbeacon |
| absl::optional<GURL> AddFencedFrameURL( |
| const GURL& url, |
| const ReportingMetadata& reporting_metadata = ReportingMetadata()); |
| |
| // Assign ad auction data as well as an ordered list of ad component URLs, |
| // provided by a bidder running an auction, to the existing entry associated |
| // with the placeholder |urn_uuid|. These will to be made available to any |
| // fenced frame navigated to the returned URN, via the InterestGroup API. |
| // |
| // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md |
| void AssignFencedFrameURLAndInterestGroupInfo( |
| const GURL& urn_uuid, |
| const GURL& url, |
| AdAuctionData auction_data, |
| std::vector<GURL> ad_component_urls, |
| const ReportingMetadata& reporting_metadata = ReportingMetadata()); |
| |
| // Generate a URN that is mapped to a default constructed `MapInfo` without a |
| // specified URL. This method will fail and return absl::nullopt if number of |
| // mappings has reached limit. As the result, ad auction will be terminated up |
| // front. If success, info provided by auction bidder will later be assigned |
| // using `AssignFencedFrameURLAndInterestGroupInfo`. |
| absl::optional<GURL> GeneratePlaceholderURN(); |
| |
| // Generate a URN that is not yet mapped to a URL. Used by the Shared Storage |
| // API to return the URN for `sharedStorage.runURLSelectionOperation` before |
| // the URL selection decision is made. |
| GURL GeneratePendingMappedURN(); |
| |
| // Register an observer for `urn_uuid`. The observer will be notified with the |
| // mapping result and will be auto unregistered. If `urn_uuid` already exists |
| // in `urn_uuid_to_url_map_`, or if it is not recognized at all, the observer |
| // will be notified synchronously; if the mapping is pending (i.e. `urn_uuid` |
| // exists in `pending_urn_uuid_to_url_map_`), the observer will be notified |
| // asynchronously as soon as when the mapping decision is made. |
| void ConvertFencedFrameURNToURL(const GURL& urn_uuid, |
| MappingResultObserver* observer); |
| |
| // Explicitly unregister the observer for `urn_uuid`. This is only needed if |
| // the observer is going to become invalid and the mapping is still pending. |
| void RemoveObserverForURN(const GURL& urn_uuid, |
| MappingResultObserver* observer); |
| |
| // Called when the shared storage mapping decision is made for `urn_uuid`. |
| // Should only be invoked on a `urn_uuid` pending to be mapped. This method |
| // will trigger the observers' OnFencedFrameURLMappingComplete() method |
| // associated with the `urn_uuid`, unregister those observers, and move the |
| // `urn_uuid` from `pending_urn_uuid_to_url_map_` to `urn_uuid_to_url_map_`. |
| void OnSharedStorageURNMappingResultDetermined( |
| const GURL& urn_uuid, |
| const SharedStorageURNMappingResult& mapping_result); |
| |
| // Return the `SharedStorageBudgetMetadata` associated with `urn_uuid`, or |
| // nullptr if there's no metadata associated (i.e. `urn_uuid` was not |
| // originated from shared storage). Precondition: `urn_uuid` exists in |
| // `urn_uuid_to_url_map_`. |
| // |
| // This method will be called during the lifetime of a `NavigationRequest` |
| // object, to associate the budget metadata to each relevant committed |
| // document. A non-null returned pointer will stay valid during the |
| // `FencedFrameURLMapping`'s (thus the page's) lifetime, and a page will |
| // outlive any `NavigationRequest` occurring in fenced frames in the page, |
| // thus it's safe for a `NavigationRequest` to store a pointer to this. |
| SharedStorageBudgetMetadata* GetSharedStorageBudgetMetadata( |
| const GURL& urn_uuid); |
| |
| // Modifies the true URL from a URN by replacing substrings specified in the |
| // replacements map. The true URLs for any component ads associated with this |
| // URN will also have substrings substituted. This function will be removed |
| // once all FLEDGE auctions switch to using fenced frames. |
| // TODO(crbug.com/1253118): Remove this function when we remove support for |
| // showing FLEDGE ads in iframes. |
| void SubstituteMappedURL( |
| const GURL& urn_uuid, |
| const std::vector<std::pair<std::string, std::string>>& substitutions); |
| |
| bool HasObserverForTesting(const GURL& urn_uuid, |
| MappingResultObserver* observer); |
| |
| // Returns as an out parameter the `ReportingMetadata`'s map for value |
| // `"shared-storage-select-url"` associated with `urn_uuid`, or leaves the out |
| // parameter unchanged if there's no shared storage reporting metadata |
| // associated (i.e. `urn_uuid` did not originate from shared storage or else |
| // there was no metadata passed from JavaScript). Precondition: `urn_uuid` |
| // exists in `urn_uuid_to_url_map_`. |
| void GetSharedStorageReportingMapForTesting( |
| const GURL& urn_uuid, |
| SharedStorageReportingMap* out_reporting_map); |
| |
| private: |
| // Declare tests as friend of `FencedFrameURLMapping` for access to private |
| // member `kMaxUrnMappingSize`. |
| FRIEND_TEST_ALL_PREFIXES(FencedFrameURLMappingTest, |
| ExceedNumOfUrnMappingsLimitFailsAddURL); |
| FRIEND_TEST_ALL_PREFIXES(AdAuctionServiceImplTest, |
| RunAdAuctionExceedNumOfUrnMappingsLimitFailsAuction); |
| |
| using UrnUuidToUrlMap = std::map<GURL, MapInfo>; |
| |
| // The maximum number of urn mappings. |
| static constexpr size_t kMaxUrnMappingSize = 65536; |
| |
| // Adds an entry to `urn_uuid_to_url_map_` for `url`, generating a unique URN |
| // as the key. Insertion fails if number of entries has reached the limit. |
| absl::optional<UrnUuidToUrlMap::iterator> AddMappingForUrl(const GURL& url); |
| |
| bool IsMapped(const GURL& urn_uuid) const; |
| bool IsPendingMapped(const GURL& urn_uuid) const; |
| // Return true if number of mappings has reached the limit specified as |
| // `kMaxUrnMappingSize`. |
| bool IsFull() const; |
| |
| // The URNs that are already mapped to URLs, along with their mapping info. |
| UrnUuidToUrlMap urn_uuid_to_url_map_; |
| |
| // The URNs that are not yet mapped to URLs, along with the associated |
| // observers to be notified when the mapping decision is made. |
| std::map<GURL, std::set<raw_ptr<MappingResultObserver>>> |
| pending_urn_uuid_to_url_map_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_ |