@@ -358,7 +358,7 @@ The histogram created by the [=conversion report=] is constructed as follows:
358
358
a histogram consisting entirely of zeros (0) is constructed.
359
359
360
360
* If one or more matching impressions is found, the browser runs the attribution
361
- logic (default last-touch) to select the most recent impression. The provided conversion
361
+ logic (default last-n- touch) to select the most recent impression. The provided conversion
362
362
value is added to a histogram at the bucket that was specified at the time of the
363
363
attributed impression. All other buckets are set to zero.
364
364
@@ -597,6 +597,7 @@ dictionary AttributionImpressionOptions {
597
597
sequence<USVString> conversionSites = [];
598
598
sequence<USVString> conversionCallers = [];
599
599
unsigned long lifetimeDays = 30;
600
+ long priority = 0;
600
601
};
601
602
602
603
dictionary AttributionImpressionResult {
@@ -646,6 +647,10 @@ The arguments to <a method for=Attribution>saveImpression()</a> are as follows:
646
647
The [=user agent=] should impose an upper limit on the lifetime,
647
648
and silently reduce the value specified here if it exceeds that limit.
648
649
</dd>
650
+ <dt> <dfn>priority</dfn> </dt>
651
+ <dd>
652
+ An integer used to sort [=impressions=] during attribution.
653
+ </dd>
649
654
</dl>
650
655
651
656
## Requesting Attribution for a Conversion ## {#measure-conversion-api}
@@ -721,7 +726,10 @@ contribute to the histogram, i.e., will be uniformly zero.
721
726
722
727
<xmp highlight=js>
723
728
const attributionDetails = {
724
- logic: "last-touch",
729
+ logic: "last-n-touch",
730
+ logicOptions: {
731
+ credit: [.25, .25, .5]
732
+ }
725
733
value: 3,
726
734
maxValue: 7,
727
735
};
@@ -759,13 +767,18 @@ dictionary AttributionConversionOptions {
759
767
sequence<USVString> impressionSites = [];
760
768
sequence<USVString> impressionCallers = [];
761
769
762
- AttributionLogic logic = "last-touch";
770
+ AttributionLogic logic = "last-n-touch";
771
+ AttributionLogicOptions logicOptions;
763
772
unsigned long value = 1;
764
773
unsigned long maxValue = 1;
765
774
};
766
775
767
776
enum AttributionLogic {
768
- "last-touch",
777
+ "last-n-touch",
778
+ };
779
+
780
+ dictionary AttributionLogicOptions {
781
+ sequence<double> credit;
769
782
};
770
783
771
784
dictionary AttributionConversionResult {
@@ -960,6 +973,7 @@ The [=impression store=] is a [=set=] of [=impression|impressions=]:
960
973
<dfn>Timestamp</dfn> : The time at which <a>saveImpression()</a> was called.
961
974
<dfn>Lifetime</dfn> : The [=duration=] an [=/impression=] remains eligible for attribution, either from the call to <a>saveImpression()</a> , or a [=/user agent=] -defined limit.
962
975
<dfn>Histogram Index</dfn> : The histogram index passed to <a>saveImpression()</a> .
976
+ <dfn>Priority</dfn> : An integer used to sort [=/impressions=] during attribution.
963
977
</pre>
964
978
</div>
965
979
@@ -1382,6 +1396,8 @@ The <dfn method for=Attribution>saveImpression(|options|)</dfn> method steps are
1382
1396
multiplied by a [=duration=] of one day
1383
1397
: [=impression/Histogram Index=]
1384
1398
:: |options|.{{AttributionImpressionOptions/histogramIndex}}
1399
+ : [=impression/Priority=]
1400
+ :: |options|.{{AttributionImpressionOptions/priority}}
1385
1401
1. If the Attribution API is [[#opt-out|enabled]] ,
1386
1402
save |impression| to the [=impression store=] .
1387
1403
1. Let |result| be a new {{AttributionImpressionResult}} .
@@ -1464,6 +1480,7 @@ The <dfn method for=Attribution>measureConversion(|options|)</dfn> method steps
1464
1480
<dfn>Impression Sites</dfn> : A [=set=] of [=sites=] .
1465
1481
<dfn>Impression Callers</dfn> : A [=set=] of [=sites=] .
1466
1482
<dfn>Logic</dfn> : A {{AttributionLogic}} .
1483
+ <dfn>Logic Options</dfn> : An {{AttributionLogicOptions}} .
1467
1484
<dfn>Value</dfn> : A [=32-bit unsigned integer=] .
1468
1485
<dfn>Max Value</dfn> : A [=32-bit unsigned integer=] .
1469
1486
</pre>
@@ -1486,16 +1503,25 @@ To <dfn>validate {{AttributionConversionOptions}}</dfn> |options|:
1486
1503
or is greater than the <dfn ignore>maximum aggregation-service histogram size</dfn> ,
1487
1504
if any, for |options|.{{AttributionConversionOptions/aggregationService}} ,
1488
1505
throw a {{RangeError}} .
1506
+ 1. Let |credit| be «1».
1489
1507
1. Switch on the value of |options|.{{AttributionConversionOptions/logic}} :
1490
1508
<dl class="switch">
1491
- : <a enum-value for=AttributionLogic>"last-touch"</a>
1509
+ : <a enum-value for=AttributionLogic>"last-n- touch"</a>
1492
1510
:: Perform the following steps:
1493
1511
1. If |options|.{{AttributionConversionOptions/value}} is 0,
1494
1512
throw a {{RangeError}} .
1495
1513
1. If |options|.{{AttributionConversionOptions/value}}
1496
1514
is greater than |options|.{{AttributionConversionOptions/maxValue}} ,
1497
1515
throw a {{RangeError}} .
1516
+ 1. If |options|.{{AttributionConversionOptions}} .{{AttributionLogicOptions/credit}} [=map/exists=] :
1517
+ 1. Set |credit| to |options|.{{AttributionConversionOptions}} .{{AttributionLogicOptions/credit}} .
1518
+ 1. If |credit| is not a [=list=] or [=list/is empty=] , throw a {{RangeError}} .
1519
+ 1. If any of the [=list/items=] of |credit| are less than or equal to 0, throw a {{RangeError}} .
1520
+ 1. If the [=list/size=] of |credit| exceeds an implementation-defined maximum, throw a {{RangeError}} .
1498
1521
</dl>
1522
+ 1. Let |validatedLogicOptions| be a {{AttributionLogicOptions}} with the following fields:
1523
+ : {{AttributionLogicOptions/credit}}
1524
+ :: |credit|
1499
1525
1. Let |lookback| be |options|.{{AttributionConversionOptions/lookbackDays}} [=days=]
1500
1526
if it [=map/exists=] , the [=implementation-defined=] maximum otherwise.
1501
1527
1. If |lookback| is 0 [=days=] , throw a {{RangeError}} .
@@ -1535,6 +1561,8 @@ To <dfn>validate {{AttributionConversionOptions}}</dfn> |options|:
1535
1561
:: |impressionCallers|
1536
1562
: [=validated conversion options/Logic=]
1537
1563
:: |options|.{{AttributionConversionOptions/logic}}
1564
+ : [=validated conversion options/Logic Options=]
1565
+ :: |validatedLogicOptions|
1538
1566
: [=validated conversion options/Value=]
1539
1567
:: |options|.{{AttributionConversionOptions/value}}
1540
1568
: [=validated conversion options/Max Value=]
@@ -1591,10 +1619,11 @@ To <dfn>do attribution and fill a histogram</dfn>, given
1591
1619
1592
1620
1. Switch on |options|' [=validated conversion options/logic=] :
1593
1621
<dl class="switch">
1594
- : <a enum-value for=AttributionLogic>"last-touch"</a>
1595
- :: Return the result of [=fill a histogram with last-touch attribution=] with |matchedImpressions|,
1596
- |options|' [=validated conversion options/histogram size=] , and
1597
- |options|' [=validated conversion options/value=] .
1622
+ : <a enum-value for=AttributionLogic>"last-n-touch"</a>
1623
+ :: Return the result of [=fill a histogram with last-n-touch attribution=] with |matchedImpressions|,
1624
+ |options|' [=validated conversion options/histogram size=] ,
1625
+ |options|' [=validated conversion options/value=] , and
1626
+ |options|' [=validated conversion options/logic options=] .{{AttributionLogicOptions/credit}} .
1598
1627
1599
1628
</dl>
1600
1629
@@ -1668,23 +1697,50 @@ To perform <dfn>common matching logic</dfn>, given
1668
1697
1669
1698
</div>
1670
1699
1671
- #### Last-Touch Attribution #### {#last-touch-attribution}
1700
+ #### Last-N- Touch Attribution #### {#last-n -touch-attribution}
1672
1701
1673
1702
<div algorithm>
1674
- To <dfn>fill a histogram with last-touch attribution</dfn> , given a [=set=] of
1675
- [=impressions=] |matchedImpressions|, an integer |histogramSize|, and an integer |value|:
1703
+ To <dfn>fairly allocate credit</dfn> , given a [=list=] of doubles |credit| and an integer |value|:
1704
+ 1. Multiply each entry in |credit| by |value|.
1705
+ 1. Let |sumCredit| be the sum of |credit|'s [=list/items=] .
1706
+ 1. Let |rawNormalizedCredit| be the result of element-wise dividing each [=list/item=] in |credit| by |sumCredit|.
1707
+ 1. Let |normalizedCredit| be |rawNormalizedCredit|, with each [=list/item=] rounded towards positive Infinity and converted to an integer.
1708
+ 1. Let |shuffledFractionalIndices| be [=list/get the indices|the indices=] of |credit|, ordered randomly.
1709
+ 1. [=list/remove|Remove=] any index |i| from |shuffledFractionalIndices| where |rawNormalizedCredit|[|i|] has no fractional part.
1710
+ 1. [=list/iterate|For each=] |index| of |shuffledFractionalIndices|:
1711
+ 1. If the sum of |normalizedCredit|'s [=list/items=] is equal to |value|, [=iteration/break=] .
1712
+ 1. Decrement |normalizedCredit|[|index|] by 1.
1713
+ 1. Return |normalizedCredit|.
1714
+ <p class=note> The algorithm aims to 1) allocate exactly |value| total value across assignments,
1715
+ 2) never bias towards any particular credit assignment in rounding, and
1716
+ 3) never incur an error greater than 1 to any assignment.
1717
+ <p class=issue>
1718
+ While the algorithm is "unbiased" in the sense that the probability of being decremented is equal
1719
+ across entries, ideally the final array of value is unbiased in the sense that its expectation
1720
+ should be equal to |normalizedCredit|. This is not achieved by the current algorithm.
1676
1721
1677
- 1. [=Assert=] : |matchedImpressions| is [=set/is empty|not empty=] .
1722
+ </div>
1678
1723
1679
- 1. Let |impression| be the value in |matchedImpressions| with the most recent
1680
- [=impression/timestamp=] .
1724
+ <div algorithm>
1725
+ To <dfn>fill a histogram with last-n-touch attribution</dfn> , given a [=set=] of
1726
+ [=impressions=] |matchedImpressions|, an integer |histogramSize|, an integer |value|, and
1727
+ a [=list=] of doubles |credit|:
1681
1728
1729
+ 1. [=Assert=] : |matchedImpressions| is [=set/is empty|not empty=] .
1730
+ 1. Let |sortedImpressions| be |matchedImpressions|, [=list/sorted in ascending order=]
1731
+ by [=impression/priority=] , then by [=impression/timestamp=] .
1732
+ 1. Let |N| be the minimum of the [=list/size=] of |credit|, and the [=list/size=] of |sortedImpressions|.
1733
+ 1. Let |lastNImpressions| be the last |N| entries of |sortedImpressions|.
1734
+ 1. [=list/remove|Remove=] all but the last |N| entries of |credit|.
1735
+ 1. Let |normalizedCredit| be the result of [=fairly allocate credit|fairly allocating credit=] with |credit| and |value|.
1682
1736
1. Let |histogram| be the result of invoking [=create an all-zero histogram=]
1683
1737
with |histogramSize|.
1684
1738
1685
- 1. Let |index| be |impression|'s [=impression/histogram index=] .
1686
-
1687
- 1. If |index| is less than |histogram|'s [=list/size=] , set |histogram|[|index|] to |value|.
1739
+ 1. [=list/iterate|For each=] |i| of [=list/get the indices|the indices=] of |lastNImpressions|:
1740
+ 1. Let |impression| be |lastNImpressions|[|i|] .
1741
+ 1. Let |value| be |normalizedCredit|[|i|] .
1742
+ 1. Let |index| be |impression|'s [=impression/histogram index=] .
1743
+ 1. If |index| is less than |histogram|'s [=list/size=] , increment |histogram|[|index|] by |value|.
1688
1744
1689
1745
1. Return |histogram|.
1690
1746
@@ -1753,6 +1809,11 @@ the {{AttributionImpressionOptions}} dictionary passed to
1753
1809
Value of <a dict-member for=AttributionImpressionOptions>histogramIndex</a> ,
1754
1810
a non-negative [=structured header/integer=] . This key is required.
1755
1811
</dd>
1812
+ <dt> <dfn noexport><code>priority</code></dfn> </dt>
1813
+ <dd>
1814
+ Value of <a dict-member for=AttributionImpressionOptions>priority</a> ,
1815
+ an [=structured header/integer=] . This key is optional.
1816
+ </dd>
1756
1817
<dt> <dfn noexport><code>match-value</code></dfn> </dt>
1757
1818
<dd>
1758
1819
Value of <a dict-member for=AttributionImpressionOptions>matchValue</a> ,
@@ -1787,6 +1848,8 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
1787
1848
1. If |matchValue| is not an [=structured header/integer=] or is less than 0, return an error.
1788
1849
1. Let |lifetimeDays| be |dict|["<code>[=save-impression/lifetime-days=] </code> "] [=map/with default=] 30.
1789
1850
1. If |lifetimeDays| is not an [=structured header/integer=] or is less than or equal to 0, return an error.
1851
+ 1. Let |priority| be |dict|["<code>[=save-impression/priority=] </code> "] [=map/with default=] 0.
1852
+ 1. If |priority| is not an [=structured header/integer=] , return an error.
1790
1853
1. Return a new {{AttributionImpressionOptions}} with the following items:
1791
1854
: {{AttributionImpressionOptions/histogramIndex}}
1792
1855
:: |histogramIndex|
@@ -1798,6 +1861,8 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
1798
1861
:: |conversionCallers|
1799
1862
: {{AttributionImpressionOptions/lifetimeDays}}
1800
1863
:: |lifetimeDays|
1864
+ : {{AttributionImpressionOptions/priority}}
1865
+ :: |priority|
1801
1866
1802
1867
</div>
1803
1868
0 commit comments