Skip to content

Commit

Permalink
Fenced frames: Add reporting to custom destination urls [1/N]
Browse files Browse the repository at this point in the history
This CL adds the API surface for custom destination urls in reportEvent.

Later CLs will send this information to the browser and send reporting
beacons based on it.

WICG/turtledove#477

Change-Id: If901ceb39f759a2911e1781d91caf133874ac389
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4711374
Reviewed-by: Dominic Farolino <[email protected]>
Reviewed-by: Shivani Sharma <[email protected]>
Commit-Queue: Garrett Tanzer <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1175044}
  • Loading branch information
Garrett Tanzer authored and Chromium LUCI CQ committed Jul 25, 2023
1 parent 002b3af commit a8b49cf
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 5 deletions.
85 changes: 85 additions & 0 deletions third_party/blink/renderer/core/html/fenced_frame/fence.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ void Fence::reportEvent(ScriptState* script_state,
"fully active");
return;
}

if (event->hasDestinationURL()) {
reportEventToDestinationURL(script_state, event, exception_state);
} else {
reportEventToDestinationEnum(script_state, event, exception_state);
}
}

void Fence::reportEventToDestinationEnum(ScriptState* script_state,
const FenceEvent* event,
ExceptionState& exception_state) {
if (!event->hasDestination()) {
exception_state.ThrowTypeError("Missing required 'destination' property.");
return;
}
if (!event->hasEventType()) {
exception_state.ThrowTypeError("Missing required 'eventType' property.");
return;
}

if (event->hasEventData() &&
event->eventData().length() > blink::kFencedFrameMaxBeaconLength) {
exception_state.ThrowSecurityError(
Expand Down Expand Up @@ -119,6 +139,63 @@ void Fence::reportEvent(ScriptState* script_state,
attribution_reporting_runtime_features);
}

void Fence::reportEventToDestinationURL(ScriptState* script_state,
const FenceEvent* event,
ExceptionState& exception_state) {
if (event->hasEventType()) {
exception_state.ThrowTypeError(
"When reporting to a custom destination URL, 'eventType' is not "
"allowed.");
return;
}
if (event->hasEventData()) {
exception_state.ThrowTypeError(
"When reporting to a custom destination URL, 'eventData' is not "
"allowed.");
return;
}
if (event->hasDestination()) {
exception_state.ThrowTypeError(
"When reporting to a custom destination URL, 'destination' is not "
"allowed.");
return;
}
if (event->destinationURL().length() > blink::kFencedFrameMaxBeaconLength) {
exception_state.ThrowSecurityError(
"The destination URL provided to reportEvent() exceeds the maximum "
"length, which is 64KB.");
return;
}

GURL destinationURL(KURL(event->destinationURL()));
if (!destinationURL.is_valid()) {
exception_state.ThrowTypeError(
"The destination URL provided to reportEvent() is not a valid URL.");
return;
}
if (!destinationURL.SchemeIs(url::kHttpsScheme)) {
exception_state.ThrowTypeError(
"The destination URL provided to reportEvent() does not have the "
"required scheme (https).");
return;
}

LocalFrame* frame = DomWindow()->GetFrame();
DCHECK(frame->GetDocument());
bool has_fenced_frame_reporting =
frame->GetDocument()->Loader()->FencedFrameProperties().has_value() &&
frame->GetDocument()
->Loader()
->FencedFrameProperties()
->has_fenced_frame_reporting();
if (!has_fenced_frame_reporting) {
AddConsoleMessage("This frame did not register reporting metadata.");
return;
}

// TODO(gtanzer): Call into the browser.
}

void Fence::setReportEventDataForAutomaticBeacons(
ScriptState* script_state,
const FenceEvent* event,
Expand All @@ -129,6 +206,14 @@ void Fence::setReportEventDataForAutomaticBeacons(
"fully active");
return;
}
if (!event->hasDestination()) {
exception_state.ThrowTypeError("Missing required 'destination' property.");
return;
}
if (!event->hasEventType()) {
exception_state.ThrowTypeError("Missing required 'eventType' property.");
return;
}
if (event->eventType() != blink::kFencedFrameTopNavigationBeaconType) {
AddConsoleMessage(event->eventType() +
" is not a valid automatic beacon event type.");
Expand Down
19 changes: 16 additions & 3 deletions third_party/blink/renderer/core/html/fenced_frame/fence.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ class CORE_EXPORT Fence final : public ScriptWrappable,
public:
explicit Fence(LocalDOMWindow& window);

// If `event` is a FenceEvent, calls reportEvent() to send a beacon with the
// data in event to the registered reporting URL.
// If `event` is a FenceEvent, calls reportEvent() to send a beacon to a
// registered destination (referenced by destination enum and event name),
// or a custom destination URL as appropriate.
// If `event` is a string of the name of the event (i.e.
// FenceEvent.eventType), calls reportPrivateAggregationEvent() to trigger
// sending the contributions associated with the given event.
Expand All @@ -56,11 +57,23 @@ class CORE_EXPORT Fence final : public ScriptWrappable,
void Trace(Visitor*) const override;

private:
// Sends a beacon with the data in `event` to the registered reporting URL.
// Dispatches to `reportEventToDestinationEnum` or
// `reportEventToDestinationURL` depending on the format of `event`.
void reportEvent(ScriptState* script_state,
const FenceEvent* event,
ExceptionState& exception_state);

// Sends a report with `eventData` to the reporting destinations specified by
// `destination`.
void reportEventToDestinationEnum(ScriptState* script_state,
const FenceEvent* event,
ExceptionState& exception_state);

// Sends a report to `destinationURL`, with substitution of buyer macros.
void reportEventToDestinationURL(ScriptState* script_state,
const FenceEvent* event,
ExceptionState& exception_state);

// Triggers the sending of any contributions associated with the given event.
// This function simply passes off the work to the fenced frame reporter in
// the browser to handle the actual sending of contributions.
Expand Down
12 changes: 10 additions & 2 deletions third_party/blink/renderer/core/html/fenced_frame/fence_event.idl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ enum FenceReportingDestination {
};

dictionary FenceEvent {
required DOMString eventType;
// This dictionary has two mutually exclusive modes:

// When reporting to a preregistered destination (specified by enum), the
// following properties are used:
DOMString eventType;
DOMString eventData;
required sequence<FenceReportingDestination> destination;
sequence<FenceReportingDestination> destination;
boolean once = false;

// When reporting to a custom destination URL (with substitution of macros
// defined by the buyer), the following property is used:
USVString destinationURL;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<title>Test window.fence.reportEvent destination URL.</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>

<body>
<script>
promise_test(async(t) => {
const fencedframe = await attachFencedFrameContext({generator_api: 'fledge'});
await fencedframe.execute(() => {
// The destinationURL must be a valid URL.
let event = {destinationURL: "foobarbaz"};
assert_throws_js(TypeError, () => {window.fence.reportEvent(event);});

// The destinationURL must be an https URL.
event.destinationURL = "http://3pat.com";
assert_throws_js(TypeError, () => {window.fence.reportEvent(event);});

event.destinationURL = "https://3pat.com";
window.fence.reportEvent(event);

// `eventType` isn't allowed.
event.eventType = 'click';
assert_throws_js(TypeError, () => {window.fence.reportEvent(event);});
event.eventType = undefined;

// `eventData` isn't allowed.
event.eventData = 'payload';
assert_throws_js(TypeError, () => {window.fence.reportEvent(event);});
event.eventData = undefined;

// `destination` isn't allowed.
event.destination = ['buyer'];
assert_throws_js(TypeError, () => {window.fence.reportEvent(event);});
event.destination = undefined;
});
}, 'window.fence.reportEvent destinationURL');
</script>
</body>

0 comments on commit a8b49cf

Please sign in to comment.