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

content / browser / fenced_frame / fenced_frame_url_mapping.h [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_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_
#define CONTENT_BROWSER_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_

#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "content/browser/fenced_frame/fenced_frame_config.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "content/common/content_export.h"
#include "url/gurl.h"

namespace blink {

struct AdDescriptor;
struct AdSize;

}  // namespace blink

namespace content {

class FencedFrameURLMappingTestPeer;

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
// TODO(crbug.com/40252330): Add methods for:
// 1. generating the pending config.
// 2. finalizing the pending config.
class CONTENT_EXPORT FencedFrameURLMapping {
 public:
  // The runURLSelectionOperation's url mapping result. It contains the mapped
  // url, the `SharedStorageBudgetMetadata`, and a FencedFrameReporter.
  struct CONTENT_EXPORT SharedStorageURNMappingResult {
    GURL mapped_url;
    SharedStorageBudgetMetadata budget_metadata;
    scoped_refptr<FencedFrameReporter> fenced_frame_reporter;
    SharedStorageURNMappingResult();
    SharedStorageURNMappingResult(
        GURL mapped_url,
        SharedStorageBudgetMetadata budget_metadata,
        scoped_refptr<FencedFrameReporter> fenced_frame_reporter);
    ~SharedStorageURNMappingResult();
  };

  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 std::optional<FencedFrameProperties>& properties) = 0;
  };

  FencedFrameURLMapping();
  ~FencedFrameURLMapping();
  FencedFrameURLMapping(FencedFrameURLMapping&) = delete;
  FencedFrameURLMapping& operator=(FencedFrameURLMapping&) = delete;

  // Imports URN to URL mappings from 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 same
  // PendingAdComponentsMap on the same FencedFrameURLMapping will do nothing.
  void ImportPendingAdComponents(
      const std::vector<std::pair<GURL, FencedFrameConfig>>& components);

  // Move pending mapped `urn_uuid` from `pending_urn_uuid_to_url_map_` to
  // `urn_uuid_to_url_map_`. Then assign ad auction data as well as an ordered
  // list of ad component URLs, provided by a bidder running an auction, to the
  // entry associated with the `urn_uuid` and its associated
  // `FencedFrameConfig`. These will to be made available to any fenced frame
  // that gets navigated to the URN encapsulated inside the
  // `RedactedFencedFrameConfig` that is returned from this method. Either this
  // config or the internal URN inside of it is returned to script via the
  // InterestGroup API. They used to perform the fenced frame navigation.
  //
  // `on_navigate_callback` should be run on navigation to `urn_uuid`.
  //
  // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md
  blink::FencedFrame::RedactedFencedFrameConfig
  AssignFencedFrameURLAndInterestGroupInfo(
      const GURL& urn_uuid,
      std::optional<blink::AdSize> container_size,
      const blink::AdDescriptor& ad_descriptor,
      AdAuctionData auction_data,
      base::RepeatingClosure on_navigate_callback,
      std::vector<blink::AdDescriptor> ad_component_descriptors,
      scoped_refptr<FencedFrameReporter> fenced_frame_reporter = nullptr);

  // Generate a URN that is not yet mapped to a URL.
  // * For Shared Storage, it will be returned by
  // `sharedStorage.runURLSelectionOperation` before the URL selection decision
  // is made.
  // * For FLEDGE, it will be moved from `pending_urn_uuid_to_url_map_` to
  // `urn_uuid_to_url_map_` when ad auction completes. Info provided by auction
  // bidder will be assigned using `AssignFencedFrameURLAndInterestGroupInfo`.
  //
  // This method will fail and return std::nullopt if number of
  // mappings has reached limit. Ad auction and `selectURL()` will be terminated
  // up front and an error will be reported.
  std::optional<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_`.
  // If the resolved URL is fenced-frame-compatible, the return value is the
  // populated fenced frame config. It is used to notify the observers in shared
  // storage worklet host manager. Tests can then obtain the populated fenced
  // frame configs from the observers.
  // Otherwise this method returns an std::nullopt.
  std::optional<FencedFrameConfig> OnSharedStorageURNMappingResultDetermined(
      const GURL& urn_uuid,
      const SharedStorageURNMappingResult& mapping_result);

  // 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 std::nullopt if number of mappings
  // has reached limit. Enforcing a limit on number of mappings prevents
  // excessive memory consumption.
  // `fenced_frame_reporter` will contain a `FencedFrameReporter` to associate
  // with the created URN. It may be nullptr.
  std::optional<GURL> AddFencedFrameURLForTesting(
      const GURL& url,
      scoped_refptr<FencedFrameReporter> fenced_frame_reporter = nullptr);

  // Erases the urn_uuid_to_url_map_ and the pending_urn_uuid_to_url_map_.
  void ClearMapForTesting();

  // 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* GetSharedStorageBudgetMetadataForTesting(
      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/40199055): 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);

 private:
  friend class FencedFrameURLMappingTestPeer;

  using UrnUuidToUrlMap = std::map<GURL, FencedFrameConfig>;

  // 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.
  std::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 in `urn_uuid_to_url_map_` and
  // `pending_urn_uuid_to_url_map_` 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_