Skip to content

Commit

Permalink
Introduce fence.setReportEventDataForAutomaticBeacons()
Browse files Browse the repository at this point in the history
This CL introduces a new API for window.fence that allows setting the
payload of an automatic beacon in advance. Because automatic beacons
will send without the web platform being aware of it, the web platform
needs to tell the browser what data should be sent as part of the
beacon before the beacon fires.

`setReportEventDataForAutomaticBeacons` will send an IPC to the frame's
`RenderFrameHost`, which in turn will store the payload in its
associated FrameTreeNode's `fenced_frame_properties_` object. A follow
up CL will have popups and `_unfencedTop` navigations read the payload
and send it to any beacons registered with the `reserved.top_navigation`
event.

See design doc:
https://docs.google.com/document/d/1a4Z-EA9j9RnAkxtxIeapN3CrfFDwYTvUmRZdyS12YTw/edit?usp=sharing&resourcekey=0-KAsNrfiZ2Q1X66BoJnyDFQ

Change-Id: If44f9ad91e71f5e04a1580865390368518da01d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4167776
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Garrett Tanzer <gtanzer@chromium.org>
Commit-Queue: Liam Brady <lbrady@google.com>
Reviewed-by: Shivani Sharma <shivanisha@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1098653}
  • Loading branch information
Liam Brady authored and Chromium LUCI CQ committed Jan 30, 2023
1 parent 935bcb0 commit 6da2cc9
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 56 deletions.
6 changes: 4 additions & 2 deletions content/browser/fenced_frame/fenced_frame_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4593,9 +4593,11 @@ class FencedFrameReportEventBrowserTest
std::string GetConsoleWarningPattern(Step::Result result) {
switch (result) {
case Step::Result::kModeNotOpaque:
return "fence.reportEvent is only available in the 'opaque-ads' mode.";
return "Fenced event reporting is only available in the 'opaque-ads' "
"mode.";
case Step::Result::kCrossOrigin:
return "fence.reportEvent is only available in same-origin subframes.";
return "Fenced event reporting is only available in same-origin "
"subframes.";
case Step::Result::kNoMeta:
return "This frame did not register reporting metadata.";
case Step::Result::kNoDestination:
Expand Down
7 changes: 7 additions & 0 deletions content/browser/fenced_frame/fenced_frame_reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,11 @@ bool FencedFrameReporter::SendReportInternal(
return true;
}

void FencedFrameReporter::UpdateAutomaticBeaconData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination) {
automatic_beacon_data_ = event_data;
automatic_beacon_destination_ = destination;
}

} // namespace content
17 changes: 17 additions & 0 deletions content/browser/fenced_frame/fenced_frame_reporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ class CONTENT_EXPORT FencedFrameReporter
const url::Origin& request_initiator,
std::string& error_message);

// Stores the payload that will be sent as part of the
// `reserved.top_navigation` automatic beacon.
void UpdateAutomaticBeaconData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination);

private:
friend class base::RefCounted<FencedFrameReporter>;
friend class FencedFrameURLMappingTestPeer;
Expand Down Expand Up @@ -153,6 +159,17 @@ class CONTENT_EXPORT FencedFrameReporter
base::flat_map<blink::FencedFrame::ReportingDestination,
ReportingDestinationInfo>
reporting_metadata_;

// Stores data registered by one of the documents in a FencedFrame using
// the `Fence.setReportEventDataForAutomaticBeacons` API.
//
// Currently, only the `reserved.top_navigation` event exists.
//
// The data will be sent directly to the network, without going back to any
// renderer process, so they are not made part of the redacted properties.
absl::optional<std::string> automatic_beacon_data_;
std::vector<blink::FencedFrame::ReportingDestination>
automatic_beacon_destination_;
};

} // namespace content
Expand Down
56 changes: 40 additions & 16 deletions content/browser/renderer_host/frame_tree_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -856,26 +856,50 @@ bool FrameTreeNode::IsInFencedFrameTree() const {

const absl::optional<FencedFrameProperties>&
FrameTreeNode::GetFencedFrameProperties() {
if (!IsInFencedFrameTree()) {
// If we might be in a urn iframe, try to find the "urn iframe root"
// and if it exists, return the attached `FencedFrameProperties`.
if (blink::features::IsAllowURNsInIframeEnabled()) {
FrameTreeNode* node = this;
while (node->parent()) {
CHECK(node->parent()->frame_tree_node());
if (node->fenced_frame_properties_.has_value()) {
return node->fenced_frame_properties_;
}
node = node->parent()->frame_tree_node();
return GetFencedFramePropertiesForEditing();
}

absl::optional<FencedFrameProperties>&
FrameTreeNode::GetFencedFramePropertiesForEditing() {
if (IsInFencedFrameTree()) {
// Because we already confirmed we're in a fenced frame tree, we know
// there must be a fenced frame root with properties stored.
CHECK(frame_tree().root()->fenced_frame_properties_.has_value());
return frame_tree().root()->fenced_frame_properties_;
}

// If we might be in a urn iframe, try to find the "urn iframe root",
// and, if it exists, return the attached `FencedFrameProperties`.
if (blink::features::IsAllowURNsInIframeEnabled()) {
FrameTreeNode* node = this;
while (node->parent()) {
CHECK(node->parent()->frame_tree_node());
if (node->fenced_frame_properties_.has_value()) {
return node->fenced_frame_properties_;
}
node = node->parent()->frame_tree_node();
}
return fenced_frame_properties_;
}

// Because we already confirmed we're in a fenced frame tree, we know
// there must be a fenced frame root with properties stored.
CHECK(frame_tree().root()->fenced_frame_properties_.has_value());
return frame_tree().root()->fenced_frame_properties_;
return fenced_frame_properties_;
}

void FrameTreeNode::SetFencedFrameAutomaticBeaconReportEventData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination) {
absl::optional<FencedFrameProperties>& properties =
GetFencedFramePropertiesForEditing();
// `properties` will exist for both fenced frames as well as iframes loaded
// with a urn:uuid. This allows URN iframes to call this function without
// getting bad-messaged.
if (!properties || !properties->fenced_frame_reporter_) {
mojo::ReportBadMessage(
"Automatic beacon data can only be set in fenced frames or iframes "
"loaded with a URN.");
return;
}
properties->fenced_frame_reporter_->UpdateAutomaticBeaconData(event_data,
destination);
}

size_t FrameTreeNode::GetFencedFrameDepth() {
Expand Down
9 changes: 9 additions & 0 deletions content/browser/renderer_host/frame_tree_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,13 @@ class CONTENT_EXPORT FrameTreeNode : public RenderFrameHostOwner {
// either this node or an ancestor of it.
const absl::optional<FencedFrameProperties>& GetFencedFrameProperties();

// Called from the currently active document via the
// `Fence.setReportEventDataForAutomaticBeacons` JS API.
void SetFencedFrameAutomaticBeaconReportEventData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination)
override;

// Return the number of fenced frame boundaries above this frame. The
// outermost main frame's frame tree has fenced frame depth 0, a topmost
// fenced frame tree embedded in the outermost main frame has fenced frame
Expand Down Expand Up @@ -673,6 +680,8 @@ class CONTENT_EXPORT FrameTreeNode : public RenderFrameHostOwner {
// user activation state in the widget. Otherwise, this returns false.
bool VerifyUserActivation();

absl::optional<FencedFrameProperties>& GetFencedFramePropertiesForEditing();

// The next available browser-global FrameTreeNode ID.
static int next_frame_tree_node_id_;

Expand Down
31 changes: 31 additions & 0 deletions content/browser/renderer_host/render_frame_host_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7677,6 +7677,12 @@ void RenderFrameHostImpl::SendFencedFrameReportingBeacon(
"consistent between the two.");
return;
}
if (event_data.length() > blink::kFencedFrameMaxBeaconLength) {
mojo::ReportBadMessage(
"The data provided to SendFencedFrameReportingBeacon() exceeds the "
"maximum length, which is 64KB.");
return;
}

if (destination ==
blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl &&
Expand All @@ -7698,6 +7704,31 @@ void RenderFrameHostImpl::SendFencedFrameReportingBeacon(
}
}

void RenderFrameHostImpl::SetFencedFrameAutomaticBeaconReportEventData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination) {
if (event_data.length() > blink::kFencedFrameMaxBeaconLength) {
mojo::ReportBadMessage(
"The data provided to SetFencedFrameAutomaticBeaconReportEventData() "
"exceeds the maximum length, which is 64KB.");
return;
}

// The call is ignored if the RenderFrameHost is not the currently active one
// in the FrameTreeNode. For instance, this is ignored when it is pending
// deletion or if it entered the BackForwardCache.
//
// Note: The renderer process already tests the document is not detached from
// the frame tree before sending the IPC, but this might race with frame
// deletion IPC sent from other processes.
if (!IsActive()) {
return;
}
CHECK(owner_); // See `owner_` invariants about `IsActive()`.

owner_->SetFencedFrameAutomaticBeaconReportEventData(event_data, destination);
}

void RenderFrameHostImpl::CreatePortal(
mojo::PendingAssociatedReceiver<blink::mojom::Portal> pending_receiver,
mojo::PendingAssociatedRemote<blink::mojom::PortalClient> client,
Expand Down
4 changes: 4 additions & 0 deletions content/browser/renderer_host/render_frame_host_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2310,6 +2310,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
const std::string& event_data,
const std::string& event_type,
blink::FencedFrame::ReportingDestination destination) override;
void SetFencedFrameAutomaticBeaconReportEventData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>& destination)
override;
void CreatePortal(
mojo::PendingAssociatedReceiver<blink::mojom::Portal> pending_receiver,
mojo::PendingAssociatedRemote<blink::mojom::PortalClient> client,
Expand Down
7 changes: 7 additions & 0 deletions content/browser/renderer_host/render_frame_host_owner.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ class RenderFrameHostOwner {

// Return the iframe.credentialless attribute value.
virtual bool Credentialless() const = 0;

// Stores the payload that will be sent as part of an automatic beacon. Right
// now only the "reserved.top_navigation" beacon is supported.
virtual void SetFencedFrameAutomaticBeaconReportEventData(
const std::string& event_data,
const std::vector<blink::FencedFrame::ReportingDestination>&
destination) = 0;
};

} // namespace content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class GURL;

namespace blink {

// To prevent overloading the RAM, limit the maximum automatic beacon length
// to 64KB.
const size_t kFencedFrameMaxBeaconLength = 64000;

// Histogram names for fenced frame.
inline constexpr char kFencedFrameCreationOrNavigationOutcomeHistogram[] =
"Blink.FencedFrame.CreationOrNavigationOutcome";
Expand Down
9 changes: 9 additions & 0 deletions third_party/blink/public/mojom/frame/frame.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,15 @@ interface LocalFrameHost {
string event_type,
ReportingDestination destination);

// Fenced frames can send beacons automatically when an event happens.
//
// For now, only "reserved.top_navigation" is supported. The `event_data` is
// installed in the RenderFrameHost and will be sent toward `destination` on
// the next cross-document top-frame navigation.
SetFencedFrameAutomaticBeaconReportEventData(
string event_data,
array<ReportingDestination> destination);

// Sent by the renderer process to request the creation of a new portal.
// |portal| is the pipe to be used for the Portal object, |client| is the pipe
// used to communicate back with the caller. Returns:
Expand Down
Loading

0 comments on commit 6da2cc9

Please sign in to comment.