Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions api.bs
Original file line number Diff line number Diff line change
Expand Up @@ -984,36 +984,45 @@ defined in [[CLEAR-SITE-DATA#header]].

When the [[CLEAR-SITE-DATA#clear-response|clear site data for response]] algorithm is invoked,
if the list of types [=set/contains=] \``"impressions"`\`,
the [=clear impressions for a conversion site=] is invoked,
the [=clear impressions for a site=] is invoked,
passing the <var ignore=''>origin</var>.

<div algorithm>
To <dfn>clear impressions for a conversion site</dfn>,
To <dfn>clear impressions for a site</dfn>,
given an [=origin=] |origin|,
run these steps:

1. If |origin| is not a [=tuple origin=] with a [=scheme=] of `https`,
return.
1. If |origin| is not a [=tuple origin=], return.

1. Let |site| be the value returned
by invoking [=registrable domain|obtain a registrable domain=],
passing the [=host=] part of the [=tuple origin|origin tuple=].
passing |origin|'s [=host=] part.

1. [=set/iterate|For each=] [=impression=] |impression| of
the [=impression store=]:

1. If |impression|'s [=impression/Intermediary Site=] is `undefined` and
its [=impression/Impression Site=] is equal to |site|,
[=set/remove=] |impression| from the [=impression store=] and [=iteration/continue=].

1. If |impression| has an [=impression/Intermediary Site=] equal to |site|,
[=set/remove=] |impression| from the [=impression store=] and [=iteration/continue=].

1. If |impression| has a [=impression/Conversion Sites=] [=set=]
that does not [=set/contain=] the value |site|, [=iteration/continue=].
1. If |impression|'s [=impression/Conversion Sites=] [=set/contains=] |site|:

1. [=set/Remove=] |site| from |impression|'s [=impression/Conversion Sites=].

1. If |impression|'s [=impression/Conversion Sites=] [=set/is empty=],
[=set/remove=] |impression| from the [=impression store=] and
[=iteration/continue=].

1. If |impression|'s [=impression/Conversion Callers=] [=set/contains=] |site|:

1. If the [=set/size=] of |impression|'s [=impression/Conversion Sites=]
is greater than one,
[=set/remove|remove=] |site| from |impression|'s [=impression/Conversion Sites=]
and [=iteration/continue=].
1. [=set/Remove=] |site| from |impression|'s [=impression/Conversion Callers=].

1. Otherwise, [=set/remove=] |impression| from the [=impression store=].
1. If |impression|'s [=impression/Conversion Callers=] [=set/is empty=],
[=set/remove=] |impression| from the [=impression store=] and
[=iteration/continue=].

<p class=note>This process does not remove impressions
that are saved with an empty [=set=] of [=impression/Conversion Sites=].
Expand Down Expand Up @@ -2873,7 +2882,7 @@ at the time they are saved.

When clearing site data at the request of a [=site=],
through the use of the [:Clear-Site-Data:] header,
a [=user agent=] only [=clear impressions for a conversion site|removes impressions=],
a [=user agent=] only [=clear impressions for a site|removes impressions=],
without altering either the [=privacy budget store=]
or the [=epoch start store=] for affected [=sites=].

Expand Down
245 changes: 245 additions & 0 deletions impl/e2e-tests/clear-site-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
{
"$comment": "use different advertiser sites to avoid depending on budgeting",
"events": [
{
"seconds": 1,
"site": "a.example",
"event": "saveImpression",
"options": { "histogramIndex": 0 }
},
{
"seconds": 2,
"site": "b.example",
"event": "saveImpression",
"options": {
"histogramIndex": 1,
"conversionSites": [
"advertiser-1.example",
"advertiser-2.example",
"advertiser-3.example"
]
}
},
{
"seconds": 3,
"site": "c.example",
"intermediarySite": "d.example",
"event": "saveImpression",
"options": { "histogramIndex": 2 }
},
{
"seconds": 4,
"site": "advertiser-1.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [2, 2, 2]
},
{
"seconds": 5,
"site": "c.example",
"event": "clearImpressionsForSite",
"$comment": "should remove no impressions, as although there is an impression site for c.example, it has a different intermediary site"
},
{
"seconds": 6,
"site": "advertiser-2.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [2, 2, 2]
},
{
"seconds": 7,
"site": "d.example",
"event": "clearImpressionsForSite",
"$comment": "should remove only the impression with histogramIndex 2"
},
{
"seconds": 8,
"site": "advertiser-3.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [3, 3, 0]
},
{
"seconds": 9,
"site": "advertiser-1.example",
"event": "clearImpressionsForSite",
"$comment": "should remove no impressions, but remove this site from the conversionSites of the impression with histogramIndex 1"
},
{
"seconds": 10,
"site": "advertiser-1.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [6, 0, 0]
},
{
"seconds": 11,
"site": "advertiser-2.example",
"event": "clearImpressionsForSite",
"$comment": "should remove no impressions, but remove this site from the conversionSites of the impression with histogramIndex 1"
},
{
"seconds": 12,
"site": "advertiser-2.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [6, 0, 0]
},
{
"seconds": 13,
"site": "advertiser-3.example",
"event": "clearImpressionsForSite",
"$comment": "should remove the impression with histogramIndex 1, as its conversion sites set should become empty"
},
{
"seconds": 14,
"site": "advertiser-3.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [6, 0, 0]
},
{
"seconds": 15,
"site": "a.example",
"event": "clearImpressionsForSite",
"$comment": "should remove the impression with histogramIndex 0"
},
{
"seconds": 16,
"site": "advertiser-4.example",
"event": "measureConversion",
"$comment": "try to select 3 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 3,
"value": 6,
"maxValue": 6,
"credit": [1, 1, 1]
},
"expected": [0, 0, 0]
},
{
"seconds": 17,
"site": "e.example",
"event": "saveImpression",
"options": {
"histogramIndex": 3,
"conversionSites": ["advertiser-5.example"],
"conversionCallers": [
"intermediary-1.example",
"intermediary-2.example"
]
}
},
{
"seconds": 18,
"site": "intermediary-1.example",
"event": "clearImpressionsForSite",
"$comment": "should remove no impressions, but remove this site from the conversionCallers of the impression with histogramIndex 3"
},
{
"seconds": 19,
"site": "advertiser-5.example",
"intermediarySite": "intermediary-1.example",
"event": "measureConversion",
"$comment": "try to select 4 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 4,
"value": 8,
"maxValue": 8,
"credit": [1, 1, 1, 1]
},
"expected": [0, 0, 0, 0]
},
{
"seconds": 20,
"site": "advertiser-5.example",
"intermediarySite": "intermediary-2.example",
"event": "measureConversion",
"$comment": "try to select 4 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 4,
"value": 8,
"maxValue": 8,
"credit": [1, 1, 1, 1]
},
"expected": [0, 0, 0, 8]
},
{
"seconds": 21,
"site": "intermediary-2.example",
"event": "clearImpressionsForSite",
"$comment": "should remove the impression with histogramIndex 3, as its conversion callers set should become empty"
},
{
"seconds": 22,
"site": "advertiser-5.example",
"intermediarySite": "intermediary-2.example",
"event": "measureConversion",
"$comment": "try to select 4 impressions to avoid depending on ordering",
"options": {
"aggregationService": "https://agg-service.example",
"epsilon": 0.5,
"histogramSize": 4,
"value": 8,
"maxValue": 8,
"credit": [1, 1, 1, 1]
},
"expected": [0, 0, 0, 0]
}
]
}
24 changes: 16 additions & 8 deletions impl/src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface Impression {
impressionSite: string;
intermediarySite: string | undefined;
conversionSites: Set<string>;
conversionCallers: ReadonlySet<string>;
conversionCallers: Set<string>;
timestamp: Temporal.Instant;
lifetime: Temporal.Duration;
histogramIndex: number;
Expand Down Expand Up @@ -601,19 +601,27 @@ export class Backend {
return startEpoch;
}

clearImpressionsForConversionSite(site: string): void {
clearImpressionsForSite(site: string): void {
function shouldRemoveImpression(i: Impression): boolean {
if (i.intermediarySite === site) {
if (i.intermediarySite === undefined && i.impressionSite === site) {
return true;
}
if (!i.conversionSites.has(site)) {
return false;
if (i.intermediarySite === site) {
return true;
}
if (i.conversionSites.size > 1) {
if (i.conversionSites.has(site)) {
i.conversionSites.delete(site);
return false;
if (i.conversionSites.size === 0) {
return true;
}
}
return true;
if (i.conversionCallers.has(site)) {
i.conversionCallers.delete(site);
if (i.conversionCallers.size === 0) {
return true;
}
}
return false;
}

this.#impressions = this.#impressions.filter(
Expand Down
Loading