Skip to content

Commit

Permalink
Allow setting automatic beacon data from cross-origin subframes.
Browse files Browse the repository at this point in the history
Cross-origin fenced frames/URN iframes can send automatic reporting
beacons, but currently require data included in these beacons to be
pre-registered via an API call accessible only to a document that is
same-origin to the fenced frame config's mapped URL. This poses a
problem for cross-origin subframes within the same entity (e.g., an ad
frame and a payment subframe from the same company) that need to include
dynamic data, like click information, in the beacon. The current
workaround involves cumbersome postMessage communication and introduces
potential timing issues, highlighting the need for a more practical
solution for cross-origin subframes to set their own beacon data.

This CL relaxes that restriction and lets cross-origin documents set
automatic beacon data as well as use it. This is subject to the same
kinds of opt ins as other cross-origin FFAR features. Namely, the root
frame must opt in via the "Allow-Fenced-Frame-Automatic-Beacons" header,
and the cross-origin subframe setting the data must opt in via the
'crossOriginExposed' parameter in the call to setReportEvent...().

See: WICG/fenced-frame#185

Change-Id: Iea922e737fa870f2edf0c24aa81927535f779d8b
Bug: 382500834
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6074470
Reviewed-by: Andrew Verge <[email protected]>
Reviewed-by: Dominic Farolino <[email protected]>
Commit-Queue: Liam Brady <[email protected]>
Reviewed-by: Arthur Sonzogni <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1403202}
  • Loading branch information
Liam Brady authored and chromium-wpt-export-bot committed Jan 7, 2025
1 parent d2d1720 commit 0a499ef
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<title>Test window.fence.setReportEventDataForAutomaticBeacons</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/utils.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/automatic-beacon-helper.js"></script>

<body>
<script>
promise_test(async(t) => {
// This test creates the following frame tree:
// Root Frame (A)
// └─Fenced Frame (A)
// └─IFrame (B) [sets data]
// └─IFrame (C) [performs navigation]
// It then checks that C's navigation uses the data set in B, even if B is
// cross-origin to fencedframe A.
const fencedframe = await attachFencedFrameContext({
generator_api: 'fledge', register_beacon: true
});

const beacon = {
eventType: "reserved.top_navigation_start",
eventData: "This is the start data",
destination: ["buyer"],
crossOriginExposed: true
}

await fencedframe.execute(async (beacon) => {
const iframe = await attachIFrameContext({
origin: 'https://{{hosts[][www2]}}:{{ports[https][1]}}'
});
// IFrame (C) is created during this setupAutomaticBeacon() call. The data
// for IFrame (B) is also set during this call.
return setupAutomaticBeacon(iframe, [beacon],
"resources/close.html", NavigationTrigger.CrossOriginClick,
"_blank");
}, [beacon]);

await multiClick(10, 10, fencedframe.element)

await verifyBeaconData(beacon.eventType, beacon.eventData,
get_host_info().HTTPS_REMOTE_ORIGIN);
}, 'An automatic beacon can use data set by a cross-origin ancestor.');

</script>
</body>
26 changes: 16 additions & 10 deletions fenced-frame/automatic-beacon-data-cross-origin-subframe.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,28 @@
origin: get_host_info().HTTPS_REMOTE_ORIGIN,
headers: [['Allow-Fenced-Frame-Automatic-Beacons', 'true']]
});
return setupAutomaticBeacon(iframe, [beacon],
"resources/close.html", NavigationTrigger.Click,
await setupAutomaticBeacon(iframe, [beacon],
"resources/close.html", NavigationTrigger.ClickOnce,
"_blank");
return iframe.execute(() => {
// Test that automatic beacon data is set correctly in the subframe. Data
// that is not cross-origin exposed should not be able to be set in a
// cross-origin subframe, even if the same frame that sets the data
// triggers the report.
window.fence.setReportEventDataForAutomaticBeacons({
eventType: "reserved.top_navigation_start",
eventData: "This should not be the data",
destination: ["buyer"],
crossOriginExposed: false
});
});
}, [beacon]);

await multiClick(10, 10, fencedframe.element)

// An automatic beacon should be sent, but no data should be attached to it,
// as it shouldn't have been able to be set from a cross-origin subframe.
await verifyBeaconData(beacon.eventType, "<No data>",
await verifyBeaconData(beacon.eventType, beacon.eventData,
get_host_info().HTTPS_REMOTE_ORIGIN);

// Leaving this fenced frame around for subsequent tests can lead to
// flakiness.
document.body.removeChild(fencedframe.element);
}, 'A cross origin subframe cannot set automatic beacon data.');
}, 'A cross origin subframe can set automatic beacon data.');

</script>
</body>
62 changes: 62 additions & 0 deletions fenced-frame/automatic-beacon-data-multiple-ancestors.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<title>Test window.fence.setReportEventDataForAutomaticBeacons</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/utils.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/automatic-beacon-helper.js"></script>

<body>
<script>
promise_test(async(t) => {
// This test creates the following frame tree:
// Root Frame (A)
// └─Fenced Frame (A1) [sets valid data]
// └─IFrame (A2) [sets invalid data]
// └─IFrame (B) [performs navigation]
// It then checks that B's navigation uses the data set in A1 and not A2,
// since A1's data is valid for B to use.
const fencedframe = await attachFencedFrameContext({
generator_api: 'fledge', register_beacon: true
});

const valid_beacon = {
eventType: "reserved.top_navigation_start",
eventData: "This is the start data",
destination: ["buyer"],
crossOriginExposed: true
}

const invalid_beacon = {
eventType: "reserved.top_navigation_start",
eventData: "This data should not be used",
destination: ["buyer"],
crossOriginExposed: false
}

await fencedframe.execute(async (valid_beacon, invalid_beacon)=> {
window.fence.setReportEventDataForAutomaticBeacons(valid_beacon);

const iframe = await attachIFrameContext();
await iframe.execute((valid_beacon, invalid_beacon) => {
window.fence.setReportEventDataForAutomaticBeacons(invalid_beacon);
}, [valid_beacon, invalid_beacon]);

return setupAutomaticBeacon(iframe, [],
"resources/close.html", NavigationTrigger.CrossOriginClick,
"_blank");
}, [valid_beacon, invalid_beacon]);

await multiClick(10, 10, fencedframe.element)

await verifyBeaconData(valid_beacon.eventType, valid_beacon.eventData,
get_host_info().HTTPS_REMOTE_ORIGIN);
}, 'An automatic beacon from uses the first valid ancestor-set data.');

</script>
</body>
59 changes: 59 additions & 0 deletions fenced-frame/automatic-beacon-data-set-by-sibling.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<title>Test window.fence.setReportEventDataForAutomaticBeacons</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/utils.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/automatic-beacon-helper.js"></script>

<body>
<script>
promise_test(async(t) => {
// This test creates the following frame tree:
// Root Frame (A)
// └─Fenced Frame (A)
// ├─IFrame (B) [performs navigation]
// └─IFrame (B) [sets data]
// It then checks that B's navigation sends an automatic beacon without data,
// even if a different B sets automatic beacon data.
const fencedframe = await attachFencedFrameContext({
generator_api: 'fledge', register_beacon: true
});

const beacon = {
eventType: "reserved.top_navigation_start",
eventData: "This data should not be sent",
destination: ["buyer"],
crossOriginExposed: true
}

await fencedframe.execute(async (beacon) => {
const iframe_a = await attachIFrameContext({
origin: get_host_info().HTTPS_REMOTE_ORIGIN,
headers: [['Allow-Fenced-Frame-Automatic-Beacons', 'true']]
});
const iframe_b = await attachIFrameContext({
origin: get_host_info().HTTPS_REMOTE_ORIGIN,
headers: [['Allow-Fenced-Frame-Automatic-Beacons', 'true']]
});
iframe_b.execute((beacon) => {
window.fence.setReportEventDataForAutomaticBeacons(beacon);
}, [beacon]);
await setupAutomaticBeacon(iframe_a, [],
"resources/close.html", NavigationTrigger.ClickOnce,
"_blank");
}, [beacon]);

await multiClick(10, 10, fencedframe.element)

await verifyBeaconData(beacon.eventType, "<No data>",
get_host_info().HTTPS_REMOTE_ORIGIN);
}, 'An automatic beacon does not use data set by a same-origin sibling.');

</script>
</body>
3 changes: 1 addition & 2 deletions fenced-frame/resources/automatic-beacon-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ async function setupAutomaticBeacon(
window.fence.setReportEventDataForAutomaticBeacons(beacon_event);
});
// Add a cross-origin iframe that will perform the top-level
// navigation. Do not set the 'Allow-Fenced-Frame-Automatic-Beacons'
// header to true.
// navigation.
const iframe = await attachIFrameContext({
origin: get_host_info().HTTPS_REMOTE_ORIGIN,
headers: [[
Expand Down

0 comments on commit 0a499ef

Please sign in to comment.