Title: [285967] trunk
Revision
285967
Author
[email protected]
Date
2021-11-17 17:08:40 -0800 (Wed, 17 Nov 2021)

Log Message

PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
https://bugs.webkit.org/show_bug.cgi?id=233173
<rdar://79426605>

Reviewed by Alex Christensen.

Source/WebCore:

This patch enables click destination sites a non-_javascript_ way to fire triggering
events without a requirement to make cross-site requests to source sites. This is
referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
https://github.com/privacycg/private-click-measurement/issues/71

The reason why some merchants want such an "API" is reluctance to deploy new
_javascript_ on their sites. In some industries it's even a compliance issue. Legacy
"pixels" are however accepted and so a same-site "pixel" can work for them.

Test: http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html

* html/HTMLAnchorElement.cpp:
(WebCore::HTMLAnchorElement::handleClick):
    This change is because of clarification in naming:
    - attributionReportSourceURL to attributionReportClickSourceURL
* loader/PrivateClickMeasurement.cpp:
(WebCore::PrivateClickMeasurement::parseAttributionRequestQuery):
    New function that parses out query string parameters.
(WebCore::PrivateClickMeasurement::parseAttributionRequest):
    Now calls the new PrivateClickMeasurement::parseAttributionRequestQuery()
    which handles data coming in in query parameters, in this case
    the new parameter "attributionSource."
(WebCore::PrivateClickMeasurement::attributionReportClickSourceURL const):
    New name.
(WebCore::PrivateClickMeasurement::attributionReportClickDestinationURL const):
    New name.
(WebCore::PrivateClickMeasurement::attributionReportJSON const):
    Now uses the constant privateClickMeasurementVersion.
(WebCore::PrivateClickMeasurement::tokenSignatureJSON const):
    Now uses the constant privateClickMeasurementVersion.
(WebCore::PrivateClickMeasurement::attributionReportSourceURL const): Deleted.
    Renamed attributionReportClickSourceURL.
(WebCore::PrivateClickMeasurement::attributionReportAttributeOnURL const): Deleted.
    Renamed attributionReportClickDestinationURL.
* loader/PrivateClickMeasurement.h:
(WebCore::PrivateClickMeasurement::sourceSecretToken const):
    New name.
(WebCore::PrivateClickMeasurement::AttributionTriggerData::encode const):
(WebCore::PrivateClickMeasurement::AttributionTriggerData::decode):
    Encoding and decoding of the new field sourceRegistrableDomain.
(WebCore::PrivateClickMeasurement::sourceUnlinkableToken const): Deleted.
    Renamed sourceSecretToken.
    Note that it was always the secret token used, just bad renaming earlier.

Source/WebKit:

This patch enables click destination sites a non-_javascript_ way to fire triggering
events without a requirement to make cross-site requests to source sites. This is
referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
https://github.com/privacycg/private-click-measurement/issues/71

The reason why some merchants want such an "API" is reluctance to deploy new
_javascript_ on their sites. In some industries it's even a compliance issue. Legacy
"pixels" are however accepted and so a same-site "pixel" can work for them.

* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp:
(WebKit::PCM::Database::insertPrivateClickMeasurement):
    These changes are just a correction of a function name:
    - sourceUnlinkableToken() to sourceSecretToken()
    Note that it was always the secret token used, just bad renaming earlier.
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp:
(WebKit::PrivateClickMeasurementManager::handleAttribution):
    Now checks if the incoming WebCore::PrivateClickMeasurement::AttributionTriggerData
    carries a sourceRegistrableDomain. If so, it accepts that domain as the source site
    for attribution if the triggering event was same-site as the first-party.
(WebKit::PrivateClickMeasurementManager::attribute):
(WebKit::PrivateClickMeasurementManager::fireConversionRequest):
    These changes are just a correction of a function name:
    - sourceUnlinkableToken() to sourceSecretToken()
    Note that it was always the secret token used, just bad renaming earlier.
(WebKit::PrivateClickMeasurementManager::fireConversionRequestImpl):
    These changes are because of clarification in naming:
    - attributionReportSourceURL to attributionReportClickSourceURL
    - attributionReportAttributeOnURL to attributionReportClickDestinationURL
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp:
(WebKit::PCM::Store::attributePrivateClickMeasurement):
* NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h:

Source/WTF:

* wtf/URL.cpp:
(WTF::queryParameters):
    New convenience getter.
* wtf/URL.h:

Tools:

These changes are just a correction of a function name:
sourceUnlinkableToken() to sourceSecretToken()

* TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm:
(TestWebKitAPI::TEST):

LayoutTests:

* http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py: Added.
* http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt: Added.
* http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (285966 => 285967)


--- trunk/LayoutTests/ChangeLog	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/LayoutTests/ChangeLog	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1,3 +1,15 @@
+2021-11-17  John Wilander  <[email protected]>
+
+        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=233173
+        <rdar://79426605>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py: Added.
+        * http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt: Added.
+        * http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html: Added.
+
 2021-11-17  Ryan Haddad  <[email protected]>
 
         [iOS] imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_window_open_download_block_downloads.tentative.html is frequently failing

Added: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py (0 => 285967)


--- trunk/LayoutTests/http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py	                        (rev 0)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py	2021-11-18 01:08:40 UTC (rev 285967)
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import time
+from urllib.parse import parse_qs
+
+query = parse_qs(os.environ.get('QUERY_STRING', ''), keep_blank_values=True)
+delay_ms = query.get('delay_ms', [None])[0]
+conversion_data = query.get('conversionData', [None])[0]
+priority = query.get('priority', [None])[0]
+
+if delay_ms is not None:
+    time.sleep(int(delay_ms) * 0.001)
+
+sys.stdout.write(
+    'status: 302\r\n'
+    'Cache-Control: no-cache, no-store, must-revalidate\r\n'
+    'Access-Control-Allow-Origin: *\r\n'
+    'Access-Control-Allow-Methods: GET\r\n'
+    'Content-Type: text/html\r\n'
+)
+
+if conversion_data is not None and priority is not None:
+    sys.stdout.write('Location: /.well-known/private-click-measurement/trigger-attribution/{}/{}?attributionSource=https://127.0.0.1\r\n'.format(conversion_data, priority))
+elif conversion_data is not None:
+    sys.stdout.write('Location: /.well-known/private-click-measurement/trigger-attribution/{}?attributionSource=https://127.0.0.1\r\n'.format(conversion_data))
+
+sys.stdout.write('\r\n')
Property changes on: trunk/LayoutTests/http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py
___________________________________________________________________

Added: svn:executable

+* \ No newline at end of property

Added: trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt (0 => 285967)


--- trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt	2021-11-18 01:08:40 UTC (rev 285967)
@@ -0,0 +1,15 @@
+CONSOLE MESSAGE: [Private Click Measurement] Conversion was not accepted because the conversion data could not be parsed or was higher than the allowed maximum of 15.
+CONSOLE MESSAGE: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin. Status code: 404
+CONSOLE MESSAGE: Fetch API cannot load https://localhost:8443/.well-known/private-click-measurement/trigger-attribution/Dummy?attributionSource=https://127.0.0.1 due to access control checks.
+Tests triggering of private click measurement attribution with same-site triggering event request.
+
+
+Attributed Private Click Measurements:
+WebCore::PrivateClickMeasurement 1
+Source site: 127.0.0.1
+Attribute on site: localhost
+Source ID: 3
+Attribution trigger data: 12
+Attribution priority: 0
+Attribution earliest time to send: Within 24-48 hours
+Application bundle identifier: testBundleID

Added: trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html (0 => 285967)


--- trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html	2021-11-18 01:08:40 UTC (rev 285967)
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <script src=""
+    <script src=""
+</head>
+<body _onload_="runTest()">
+<div id="description">Tests triggering of private click measurement attribution with same-site triggering event request.</div>
+<a id="targetLink" href="" attributionsourceid=3 attributiondestination="http://localhost:8000">Link</a><br>
+<div id="output"></div>
+<script>
+    prepareTest();
+
+    function activateElement(elementID) {
+        var element = document.getElementById(elementID);
+        var centerX = element.offsetLeft + element.offsetWidth / 2;
+        var centerY = element.offsetTop + element.offsetHeight / 2;
+        UIHelper.activateAt(centerX, centerY).then(
+            function () {
+            },
+            function () {
+                document.getElementById("output").innerText = "FAIL Promise rejected.";
+                tearDownAndFinish();
+            }
+        );
+    }
+
+    function triggerFetch(conversionData) {
+        return fetch("https://localhost:8443/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py?conversionData="+ conversionData + "&delay_ms=100", { keepalive: true });
+    }
+
+    function runTest() {
+        if (window.location.search === "?stepTwo") {
+            // Start private click attribution fetch but navigate away before the fetch redirection happens.
+            triggerFetch(12);
+            document.location.href = ""
+            return;
+        }
+        if (window.location.search === "?stepThree") {
+            document.body.removeChild(document.getElementById("targetLink"));
+            // Do an invalid private click attribution fetch to ensure the previous correct click attribution fetch will be finished.
+            triggerFetch("Dummy").catch(() => {
+                if (window.testRunner)
+                    testRunner.dumpPrivateClickMeasurement();
+                tearDownAndFinish();
+            });
+            return;
+        }
+        testRunner.setPrivateClickMeasurementAppBundleIDForTesting("testBundleID");
+        activateElement("targetLink");
+    }
+</script>
+</body>
+</html>

Modified: trunk/Source/WTF/ChangeLog (285966 => 285967)


--- trunk/Source/WTF/ChangeLog	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WTF/ChangeLog	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1,3 +1,16 @@
+2021-11-17  John Wilander  <[email protected]>
+
+        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=233173
+        <rdar://79426605>
+
+        Reviewed by Alex Christensen.
+
+        * wtf/URL.cpp:
+        (WTF::queryParameters):
+            New convenience getter.
+        * wtf/URL.h:
+
 2021-11-17  Chris Dumez  <[email protected]>
 
         Web Locks API does get enabled in Service Workers when running layout tests

Modified: trunk/Source/WTF/wtf/URL.cpp (285966 => 285967)


--- trunk/Source/WTF/wtf/URL.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WTF/wtf/URL.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1190,6 +1190,11 @@
 
 #endif
 
+Vector<KeyValuePair<String, String>> queryParameters(const URL& url)
+{
+    return URLParser::parseURLEncodedForm(url.query());
+}
+
 Vector<KeyValuePair<String, String>> differingQueryParameters(const URL& firstURL, const URL& secondURL)
 {
     auto firstQueryParameters = URLParser::parseURLEncodedForm(firstURL.query());

Modified: trunk/Source/WTF/wtf/URL.h (285966 => 285967)


--- trunk/Source/WTF/wtf/URL.h	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WTF/wtf/URL.h	2021-11-18 01:08:40 UTC (rev 285967)
@@ -250,6 +250,7 @@
 WTF_EXPORT_PRIVATE bool equalIgnoringFragmentIdentifier(const URL&, const URL&);
 WTF_EXPORT_PRIVATE bool protocolHostAndPortAreEqual(const URL&, const URL&);
 WTF_EXPORT_PRIVATE Vector<KeyValuePair<String, String>> differingQueryParameters(const URL&, const URL&);
+WTF_EXPORT_PRIVATE Vector<KeyValuePair<String, String>> queryParameters(const URL&);
 WTF_EXPORT_PRIVATE bool isEqualIgnoringQueryAndFragments(const URL&, const URL&);
 WTF_EXPORT_PRIVATE void removeQueryParameters(URL&, const HashSet<String>&);
 

Modified: trunk/Source/WebCore/ChangeLog (285966 => 285967)


--- trunk/Source/WebCore/ChangeLog	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebCore/ChangeLog	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1,3 +1,55 @@
+2021-11-17  John Wilander  <[email protected]>
+
+        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=233173
+        <rdar://79426605>
+
+        Reviewed by Alex Christensen.
+
+        This patch enables click destination sites a non-_javascript_ way to fire triggering
+        events without a requirement to make cross-site requests to source sites. This is
+        referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
+        https://github.com/privacycg/private-click-measurement/issues/71
+
+        The reason why some merchants want such an "API" is reluctance to deploy new
+        _javascript_ on their sites. In some industries it's even a compliance issue. Legacy
+        "pixels" are however accepted and so a same-site "pixel" can work for them.
+
+        Test: http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html
+
+        * html/HTMLAnchorElement.cpp:
+        (WebCore::HTMLAnchorElement::handleClick):
+            This change is because of clarification in naming:
+            - attributionReportSourceURL to attributionReportClickSourceURL
+        * loader/PrivateClickMeasurement.cpp:
+        (WebCore::PrivateClickMeasurement::parseAttributionRequestQuery):
+            New function that parses out query string parameters.
+        (WebCore::PrivateClickMeasurement::parseAttributionRequest):
+            Now calls the new PrivateClickMeasurement::parseAttributionRequestQuery()
+            which handles data coming in in query parameters, in this case
+            the new parameter "attributionSource."
+        (WebCore::PrivateClickMeasurement::attributionReportClickSourceURL const):
+            New name.
+        (WebCore::PrivateClickMeasurement::attributionReportClickDestinationURL const):
+            New name.
+        (WebCore::PrivateClickMeasurement::attributionReportJSON const):
+            Now uses the constant privateClickMeasurementVersion.
+        (WebCore::PrivateClickMeasurement::tokenSignatureJSON const):
+            Now uses the constant privateClickMeasurementVersion.
+        (WebCore::PrivateClickMeasurement::attributionReportSourceURL const): Deleted.
+            Renamed attributionReportClickSourceURL.
+        (WebCore::PrivateClickMeasurement::attributionReportAttributeOnURL const): Deleted.
+            Renamed attributionReportClickDestinationURL.
+        * loader/PrivateClickMeasurement.h:
+        (WebCore::PrivateClickMeasurement::sourceSecretToken const):
+            New name.
+        (WebCore::PrivateClickMeasurement::AttributionTriggerData::encode const):
+        (WebCore::PrivateClickMeasurement::AttributionTriggerData::decode):
+            Encoding and decoding of the new field sourceRegistrableDomain.
+        (WebCore::PrivateClickMeasurement::sourceUnlinkableToken const): Deleted.
+            Renamed sourceSecretToken.
+            Note that it was always the secret token used, just bad renaming earlier.
+
 2021-11-17  Tim Horton  <[email protected]>
 
         Momentum animator: Short scrolls are too far, medium scrolls aren't far enough

Modified: trunk/Source/WebCore/html/HTMLAnchorElement.cpp (285966 => 285967)


--- trunk/Source/WebCore/html/HTMLAnchorElement.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebCore/html/HTMLAnchorElement.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -534,7 +534,7 @@
     auto privateClickMeasurement = parsePrivateClickMeasurement();
     // A matching triggering event needs to happen before an attribution report can be sent.
     // Thus, URLs should be empty for now.
-    ASSERT(!privateClickMeasurement || (privateClickMeasurement->attributionReportSourceURL().isNull() && privateClickMeasurement->attributionReportAttributeOnURL().isNull()));
+    ASSERT(!privateClickMeasurement || (privateClickMeasurement->attributionReportClickSourceURL().isNull() && privateClickMeasurement->attributionReportClickDestinationURL().isNull()));
     
     frame->loader().changeLocation(completedURL, effectiveTarget, &event, referrerPolicy, document().shouldOpenExternalURLsPolicyToPropagate(), newFrameOpenerPolicy, downloadAttribute, systemPreviewInfo, WTFMove(privateClickMeasurement));
 

Modified: trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp (285966 => 285967)


--- trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -44,6 +44,7 @@
 static const char privateClickMeasurementReportAttributionPath[] = "/.well-known/private-click-measurement/report-attribution/";
 const size_t privateClickMeasurementAttributionTriggerDataPathSegmentSize = 2;
 const size_t privateClickMeasurementPriorityPathSegmentSize = 2;
+const uint8_t privateClickMeasurementVersion = 2;
 
 const Seconds PrivateClickMeasurement::maxAge()
 {
@@ -103,6 +104,35 @@
     return copy;
 }
 
+Expected<PrivateClickMeasurement::AttributionTriggerData, String> PrivateClickMeasurement::parseAttributionRequestQuery(const URL& redirectURL)
+{
+    if (!redirectURL.hasQuery())
+        return AttributionTriggerData { };
+
+    auto parameters = queryParameters(redirectURL);
+    if (!parameters.size())
+        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL had a query string but it didn't contain supported parameters."_s);
+
+    if (parameters.size() > 1)
+        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's query string contained unsupported parameters."_s);
+
+    auto parameter = parameters.first();
+    if (parameter.key == "attributionSource") {
+        if (parameter.value.isEmpty())
+            return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's attributionSource query parameter had no value."_s);
+
+        auto attributionSourceURL = URL(URL(), parameter.value);
+        if (!attributionSourceURL.isValid() || (attributionSourceURL.hasPath() && attributionSourceURL.path().length() > 1) || attributionSourceURL.hasCredentials() || attributionSourceURL.hasQuery() || attributionSourceURL.hasFragmentIdentifier())
+            return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's attributionSource query parameter was not a valid URL or was a URL with a path, credentials, query string, or fragment."_s);
+
+        AttributionTriggerData attributionTriggerData;
+        attributionTriggerData.sourceRegistrableDomain = RegistrableDomain { attributionSourceURL };
+        return attributionTriggerData;
+    }
+
+    return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL did not contain an attributionSource query parameter."_s);
+}
+
 Expected<PrivateClickMeasurement::AttributionTriggerData, String> PrivateClickMeasurement::parseAttributionRequest(const URL& redirectURL)
 {
     auto path = StringView(redirectURL.string()).substring(redirectURL.pathStart(), redirectURL.pathEnd() - redirectURL.pathStart());
@@ -109,9 +139,13 @@
     if (path.isEmpty() || !path.startsWith(privateClickMeasurementTriggerAttributionPath))
         return makeUnexpected(nullString());
 
-    if (!redirectURL.protocolIs("https") || redirectURL.hasCredentials() || redirectURL.hasQuery() || redirectURL.hasFragmentIdentifier())
-        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's protocol is not HTTPS or the URL contains one or more of username, password, query string, and fragment."_s);
+    if (!redirectURL.protocolIs("https") || redirectURL.hasCredentials() || redirectURL.hasFragmentIdentifier())
+        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's protocol is not HTTPS or the URL contains one or more of username, password, and fragment."_s);
 
+    auto result = parseAttributionRequestQuery(redirectURL);
+    if (!result && !result.error().isEmpty())
+        return result;
+    auto attributionTriggerData = result.value();
 
     auto prefixLength = sizeof(privateClickMeasurementTriggerAttributionPath) - 1;
     if (path.length() == prefixLength + privateClickMeasurementAttributionTriggerDataPathSegmentSize) {
@@ -119,7 +153,9 @@
         if (!attributionTriggerDataUInt64 || *attributionTriggerDataUInt64 > AttributionTriggerData::MaxEntropy)
             return makeUnexpected(makeString("[Private Click Measurement] Conversion was not accepted because the conversion data could not be parsed or was higher than the allowed maximum of "_s, AttributionTriggerData::MaxEntropy, "."_s));
 
-        return AttributionTriggerData { static_cast<uint8_t>(*attributionTriggerDataUInt64), Priority { 0 } };
+        attributionTriggerData.data = ""
+        attributionTriggerData.priority = 0;
+        return attributionTriggerData;
     }
     
     if (path.length() == prefixLength + privateClickMeasurementAttributionTriggerDataPathSegmentSize + 1 + privateClickMeasurementPriorityPathSegmentSize) {
@@ -131,7 +167,9 @@
         if (!attributionPriorityUInt64 || *attributionPriorityUInt64 > Priority::MaxEntropy)
             return makeUnexpected(makeString("[Private Click Measurement] Conversion was not accepted because the priority could not be parsed or was higher than the allowed maximum of "_s, Priority::MaxEntropy, "."_s));
 
-        return AttributionTriggerData { static_cast<uint8_t>(*attributionTriggerDataUInt64), Priority { static_cast<uint8_t>(*attributionPriorityUInt64) } };
+        attributionTriggerData.data = ""
+        attributionTriggerData.priority = static_cast<uint8_t>(*attributionPriorityUInt64);
+        return attributionTriggerData;
     }
 
     return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL path contained unrecognized parts."_s);
@@ -189,7 +227,7 @@
     return makeValidURL(domain, privateClickMeasurementReportAttributionPath);
 }
 
-URL PrivateClickMeasurement::attributionReportSourceURL() const
+URL PrivateClickMeasurement::attributionReportClickSourceURL() const
 {
     if (!isValid())
         return URL();
@@ -197,7 +235,7 @@
     return attributionReportURL(m_sourceSite.registrableDomain);
 }
 
-URL PrivateClickMeasurement::attributionReportAttributeOnURL() const
+URL PrivateClickMeasurement::attributionReportClickDestinationURL() const
 {
     if (!isValid())
         return URL();
@@ -216,7 +254,7 @@
     reportDetails->setInteger("source_id"_s, m_sourceID.id);
     reportDetails->setString("attributed_on_site"_s, m_destinationSite.registrableDomain.string());
     reportDetails->setInteger("trigger_data"_s, m_attributionTriggerData->data);
-    reportDetails->setInteger("version"_s, 2);
+    reportDetails->setInteger("version"_s, privateClickMeasurementVersion);
 
     // This token has been kept secret this far and cannot be linked to the unlinkable token.
     if (m_sourceSecretToken) {
@@ -274,7 +312,7 @@
     reportDetails->setString("source_nonce"_s, m_ephemeralSourceNonce->nonce);
     // This token can not be linked to the secret token.
     reportDetails->setString("source_unlinkable_token"_s, m_sourceUnlinkableToken.valueBase64URL);
-    reportDetails->setInteger("version"_s, 2);
+    reportDetails->setInteger("version"_s, privateClickMeasurementVersion);
     return reportDetails;
 }
 

Modified: trunk/Source/WebCore/loader/PrivateClickMeasurement.h (285966 => 285967)


--- trunk/Source/WebCore/loader/PrivateClickMeasurement.h	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebCore/loader/PrivateClickMeasurement.h	2021-11-18 01:08:40 UTC (rev 285967)
@@ -172,7 +172,8 @@
         static constexpr uint8_t MaxEntropy = 15;
 
         enum class WasSent : bool { No, Yes };
-        
+
+        AttributionTriggerData() = default;
         AttributionTriggerData(uint8_t data, Priority priority, WasSent wasSent = WasSent::No)
             : data { data }
             , priority { priority.value }
@@ -188,6 +189,7 @@
         uint8_t data { 0 };
         PriorityValue priority;
         WasSent wasSent = WasSent::No;
+        std::optional<RegistrableDomain> sourceRegistrableDomain;
 
         template<class Encoder> void encode(Encoder&) const;
         template<class Decoder> static std::optional<AttributionTriggerData> decode(Decoder&);
@@ -317,8 +319,8 @@
     WEBCORE_EXPORT static Expected<AttributionTriggerData, String> parseAttributionRequest(const URL& redirectURL);
     WEBCORE_EXPORT AttributionSecondsUntilSendData attributeAndGetEarliestTimeToSend(AttributionTriggerData&&, IsRunningLayoutTest);
     WEBCORE_EXPORT bool hasHigherPriorityThan(const PrivateClickMeasurement&) const;
-    WEBCORE_EXPORT URL attributionReportSourceURL() const;
-    WEBCORE_EXPORT URL attributionReportAttributeOnURL() const;
+    WEBCORE_EXPORT URL attributionReportClickSourceURL() const;
+    WEBCORE_EXPORT URL attributionReportClickDestinationURL() const;
     WEBCORE_EXPORT Ref<JSON::Object> attributionReportJSON() const;
     const SourceSite& sourceSite() const { return m_sourceSite; };
     const AttributionDestinationSite& destinationSite() const { return m_destinationSite; };
@@ -371,7 +373,7 @@
 #endif
 
     void setSourceUnlinkableTokenValue(const String& value) { m_sourceUnlinkableToken.valueBase64URL = value; }
-    const std::optional<SourceSecretToken>& sourceUnlinkableToken() const { return m_sourceSecretToken; }
+    const std::optional<SourceSecretToken>& sourceSecretToken() const { return m_sourceSecretToken; }
     WEBCORE_EXPORT void setSourceSecretToken(SourceSecretToken&&);
 
     template<class Encoder> void encode(Encoder&) const;
@@ -380,6 +382,7 @@
     WEBCORE_EXPORT PrivateClickMeasurement isolatedCopy() const;
 
 private:
+    static Expected<AttributionTriggerData, String> parseAttributionRequestQuery(const URL&);
     bool isValid() const;
 
     SourceID m_sourceID;
@@ -505,7 +508,7 @@
 template<class Encoder>
 void PrivateClickMeasurement::AttributionTriggerData::encode(Encoder& encoder) const
 {
-    encoder << data << priority << wasSent;
+    encoder << data << priority << wasSent << sourceRegistrableDomain;
 }
 
 template<class Decoder>
@@ -526,7 +529,14 @@
     if (!wasSent)
         return std::nullopt;
     
-    return AttributionTriggerData { WTFMove(*data), Priority { *priority }, *wasSent };
+    std::optional<std::optional<RegistrableDomain>> sourceRegistrableDomain;
+    decoder >> sourceRegistrableDomain;
+    if (!sourceRegistrableDomain)
+        return std::nullopt;
+    
+    AttributionTriggerData attributionTriggerData { WTFMove(*data), Priority { *priority }, *wasSent };
+    attributionTriggerData.sourceRegistrableDomain = WTFMove(*sourceRegistrableDomain);
+    return attributionTriggerData;
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebKit/ChangeLog (285966 => 285967)


--- trunk/Source/WebKit/ChangeLog	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/ChangeLog	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1,3 +1,44 @@
+2021-11-17  John Wilander  <[email protected]>
+
+        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=233173
+        <rdar://79426605>
+
+        Reviewed by Alex Christensen.
+
+        This patch enables click destination sites a non-_javascript_ way to fire triggering
+        events without a requirement to make cross-site requests to source sites. This is
+        referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
+        https://github.com/privacycg/private-click-measurement/issues/71
+
+        The reason why some merchants want such an "API" is reluctance to deploy new
+        _javascript_ on their sites. In some industries it's even a compliance issue. Legacy
+        "pixels" are however accepted and so a same-site "pixel" can work for them.
+
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp:
+        (WebKit::PCM::Database::insertPrivateClickMeasurement):
+            These changes are just a correction of a function name:
+            - sourceUnlinkableToken() to sourceSecretToken()
+            Note that it was always the secret token used, just bad renaming earlier.
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp:
+        (WebKit::PrivateClickMeasurementManager::handleAttribution):
+            Now checks if the incoming WebCore::PrivateClickMeasurement::AttributionTriggerData
+            carries a sourceRegistrableDomain. If so, it accepts that domain as the source site
+            for attribution if the triggering event was same-site as the first-party.
+        (WebKit::PrivateClickMeasurementManager::attribute):
+        (WebKit::PrivateClickMeasurementManager::fireConversionRequest):
+            These changes are just a correction of a function name:
+            - sourceUnlinkableToken() to sourceSecretToken()
+            Note that it was always the secret token used, just bad renaming earlier.
+        (WebKit::PrivateClickMeasurementManager::fireConversionRequestImpl):
+            These changes are because of clarification in naming:
+            - attributionReportSourceURL to attributionReportClickSourceURL
+            - attributionReportAttributeOnURL to attributionReportClickDestinationURL
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp:
+        (WebKit::PCM::Store::attributePrivateClickMeasurement):
+        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h:
+
 2021-11-17  Wenson Hsieh  <[email protected]>
 
         Add a Translation WebKit2 logging category

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp (285966 => 285967)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -175,7 +175,7 @@
     if (!sourceID || !attributionDestinationID)
         return;
 
-    auto& sourceUnlinkableToken = attribution.sourceUnlinkableToken();
+    auto& sourceSecretToken = attribution.sourceSecretToken();
     if (attributionType == PrivateClickMeasurementAttributionType::Attributed) {
         auto attributionTriggerData = attribution.attributionTriggerData() ? attribution.attributionTriggerData().value().data : -1;
         auto priority = attribution.attributionTriggerData() ? attribution.attributionTriggerData().value().priority : -1;
@@ -194,9 +194,9 @@
             || statement->bindInt(5, priority) != SQLITE_OK
             || statement->bindDouble(6, attribution.timeOfAdClick().secondsSinceEpoch().value()) != SQLITE_OK
             || statement->bindDouble(7, sourceEarliestTimeToSend) != SQLITE_OK
-            || statement->bindText(8, sourceUnlinkableToken ? sourceUnlinkableToken->tokenBase64URL : emptyString()) != SQLITE_OK
-            || statement->bindText(9, sourceUnlinkableToken ? sourceUnlinkableToken->signatureBase64URL : emptyString()) != SQLITE_OK
-            || statement->bindText(10, sourceUnlinkableToken ? sourceUnlinkableToken->keyIDBase64URL : emptyString()) != SQLITE_OK
+            || statement->bindText(8, sourceSecretToken ? sourceSecretToken->tokenBase64URL : emptyString()) != SQLITE_OK
+            || statement->bindText(9, sourceSecretToken ? sourceSecretToken->signatureBase64URL : emptyString()) != SQLITE_OK
+            || statement->bindText(10, sourceSecretToken ? sourceSecretToken->keyIDBase64URL : emptyString()) != SQLITE_OK
             || statement->bindDouble(11, destinationEarliestTimeToSend) != SQLITE_OK
             || statement->bindText(12, attribution.sourceApplicationBundleID()) != SQLITE_OK
             || statement->step() != SQLITE_DONE) {
@@ -214,9 +214,9 @@
         || statement->bindInt(2, *attributionDestinationID) != SQLITE_OK
         || statement->bindInt(3, attribution.sourceID().id) != SQLITE_OK
         || statement->bindDouble(4, attribution.timeOfAdClick().secondsSinceEpoch().value()) != SQLITE_OK
-        || statement->bindText(5, sourceUnlinkableToken ? sourceUnlinkableToken->tokenBase64URL : emptyString()) != SQLITE_OK
-        || statement->bindText(6, sourceUnlinkableToken ? sourceUnlinkableToken->signatureBase64URL : emptyString()) != SQLITE_OK
-        || statement->bindText(7, sourceUnlinkableToken ? sourceUnlinkableToken->keyIDBase64URL : emptyString()) != SQLITE_OK
+        || statement->bindText(5, sourceSecretToken ? sourceSecretToken->tokenBase64URL : emptyString()) != SQLITE_OK
+        || statement->bindText(6, sourceSecretToken ? sourceSecretToken->signatureBase64URL : emptyString()) != SQLITE_OK
+        || statement->bindText(7, sourceSecretToken ? sourceSecretToken->keyIDBase64URL : emptyString()) != SQLITE_OK
         || statement->bindText(8, attribution.sourceApplicationBundleID()) != SQLITE_OK
         || statement->step() != SQLITE_DONE) {
         RELEASE_LOG_ERROR(PrivateClickMeasurement, "%p - Database::insertPrivateClickMeasurement insertUnattributedPrivateClickMeasurementQuery, error message: %" PRIVATE_LOG_STRING, this, m_database.lastErrorMsg());

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp (285966 => 285967)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -248,14 +248,22 @@
         return;
     }
 
+    RegistrableDomain sourceDomain;
     if (redirectDomain.matches(firstPartyURL)) {
-        m_client->broadcastConsoleMessage(MessageLevel::Warning, "[Private Click Measurement] Triggering event was not accepted because it was requested in an HTTP redirect that is same-site as the first-party."_s);
+        if (!attributionTriggerData.sourceRegistrableDomain) {
+            m_client->broadcastConsoleMessage(MessageLevel::Warning, "[Private Click Measurement] Triggering event was not accepted because it was requested in an HTTP redirect that is same-site as the   first-party and no attributionSource query parameter was provided."_s);
+            return;
+        }
+        sourceDomain = *attributionTriggerData.sourceRegistrableDomain;
+    } else if (attributionTriggerData.sourceRegistrableDomain) {
+        m_client->broadcastConsoleMessage(MessageLevel::Warning, "[Private Click Measurement] Triggering event was not accepted because it was requested in an HTTP redirect that is cross-site from the first-party but an attributionSource query parameter was still provided."_s);
         return;
-    }
+    } else
+        sourceDomain = WTFMove(redirectDomain);
 
     m_client->broadcastConsoleMessage(MessageLevel::Log, "[Private Click Measurement] Triggering event accepted."_s);
 
-    attribute(SourceSite { WTFMove(redirectDomain) }, AttributionDestinationSite { firstPartyURL }, WTFMove(attributionTriggerData), m_privateClickMeasurementAppBundleIDForTesting ? *m_privateClickMeasurementAppBundleIDForTesting : applicationBundleIdentifier);
+    attribute(SourceSite { WTFMove(sourceDomain) }, AttributionDestinationSite { firstPartyURL }, WTFMove(attributionTriggerData), m_privateClickMeasurementAppBundleIDForTesting ? *m_privateClickMeasurementAppBundleIDForTesting : applicationBundleIdentifier);
 }
 
 void PrivateClickMeasurementManager::startTimerImmediatelyForTesting()
@@ -276,12 +284,12 @@
     m_firePendingAttributionRequestsTimer.startOneShot(seconds);
 }
 
-void PrivateClickMeasurementManager::attribute(const SourceSite& sourceSite, const AttributionDestinationSite& destinationSite, AttributionTriggerData&& attributionTriggerData, const ApplicationBundleIdentifier& applicationBundleIdentifier)
+void PrivateClickMeasurementManager::attribute(SourceSite&& sourceSite, AttributionDestinationSite&& destinationSite, AttributionTriggerData&& attributionTriggerData, const ApplicationBundleIdentifier& applicationBundleIdentifier)
 {
     if (!featureEnabled())
         return;
 
-    store().attributePrivateClickMeasurement(sourceSite, destinationSite, applicationBundleIdentifier, WTFMove(attributionTriggerData), m_isRunningTest ? WebCore::PrivateClickMeasurement::IsRunningLayoutTest::Yes : WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No, [this, weakThis = WeakPtr { *this }] (auto attributionSecondsUntilSendData, auto debugInfo) {
+    store().attributePrivateClickMeasurement(WTFMove(sourceSite), WTFMove(destinationSite), applicationBundleIdentifier, WTFMove(attributionTriggerData), m_isRunningTest ? WebCore::PrivateClickMeasurement::IsRunningLayoutTest::Yes : WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No, [this, weakThis = WeakPtr { *this }] (auto attributionSecondsUntilSendData, auto debugInfo) {
         if (!weakThis)
             return;
         
@@ -319,7 +327,7 @@
     if (!featureEnabled())
         return;
 
-    if (!attribution.sourceUnlinkableToken()) {
+    if (!attribution.sourceSecretToken()) {
         fireConversionRequestImpl(attribution, attributionReportEndpoint);
         return;
     }
@@ -339,7 +347,7 @@
         auto publicKeyDataHash = crypto->computeHash();
 
         auto keyID = base64URLEncodeToString(publicKeyDataHash.data(), publicKeyDataHash.size());
-        if (keyID != attribution.sourceUnlinkableToken()->keyIDBase64URL)
+        if (keyID != attribution.sourceSecretToken()->keyIDBase64URL)
             return;
 
         fireConversionRequestImpl(attribution, attributionReportEndpoint);
@@ -351,10 +359,10 @@
     URL attributionURL;
     switch (attributionReportEndpoint) {
     case PrivateClickMeasurement::AttributionReportEndpoint::Source:
-        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportSourceURL : attribution.attributionReportSourceURL();
+        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportClickSourceURL : attribution.attributionReportClickSourceURL();
         break;
     case PrivateClickMeasurement::AttributionReportEndpoint::Destination:
-        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportAttributeOnURL : attribution.attributionReportAttributeOnURL();
+        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportClickDestinationURL : attribution.attributionReportClickDestinationURL();
     }
 
     if (attributionURL.isEmpty() || !attributionURL.isValid())

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h (285966 => 285967)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h	2021-11-18 01:08:40 UTC (rev 285967)
@@ -76,7 +76,7 @@
     void getSignedUnlinkableToken(PrivateClickMeasurement&&);
     void insertPrivateClickMeasurement(PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType, CompletionHandler<void()>&&);
     void clearSentAttribution(PrivateClickMeasurement&&, PrivateClickMeasurement::AttributionReportEndpoint);
-    void attribute(const SourceSite&, const AttributionDestinationSite&, AttributionTriggerData&&, const ApplicationBundleIdentifier&);
+    void attribute(SourceSite&&, AttributionDestinationSite&&, AttributionTriggerData&&, const ApplicationBundleIdentifier&);
     void fireConversionRequest(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
     void fireConversionRequestImpl(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
     void clearExpired();
@@ -94,8 +94,8 @@
     UniqueRef<PCM::Client> m_client;
 
     struct AttributionReportTestConfig {
-        URL attributionReportSourceURL;
-        URL attributionReportAttributeOnURL;
+        URL attributionReportClickSourceURL;
+        URL attributionReportClickDestinationURL;
     };
 
     std::optional<AttributionReportTestConfig> m_attributionReportTestConfig;

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp (285966 => 285967)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -94,7 +94,7 @@
     });
 }
 
-void Store::attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite& sourceSite, const WebCore::PrivateClickMeasurement::AttributionDestinationSite& destinationSite, const ApplicationBundleIdentifier& applicationBundleIdentifier, WebCore::PrivateClickMeasurement::AttributionTriggerData&& attributionTriggerData, WebCore::PrivateClickMeasurement::IsRunningLayoutTest isRunningTest, CompletionHandler<void(std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>&&, DebugInfo&&)>&& completionHandler)
+void Store::attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&& sourceSite, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&& destinationSite, const ApplicationBundleIdentifier& applicationBundleIdentifier, WebCore::PrivateClickMeasurement::AttributionTriggerData&& attributionTriggerData, WebCore::PrivateClickMeasurement::IsRunningLayoutTest isRunningTest, CompletionHandler<void(std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>&&, DebugInfo&&)>&& completionHandler)
 {
     postTask([this, protectedThis = Ref { *this }, sourceSite = sourceSite.isolatedCopy(), destinationSite = destinationSite.isolatedCopy(), applicationBundleIdentifier = applicationBundleIdentifier.isolatedCopy(), attributionTriggerData = WTFMove(attributionTriggerData), isRunningTest, completionHandler = WTFMove(completionHandler)] () mutable {
         if (!m_database) {

Modified: trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h (285966 => 285967)


--- trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h	2021-11-18 01:08:40 UTC (rev 285967)
@@ -53,7 +53,7 @@
     static void processDidResume();
 
     void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, WebKit::PrivateClickMeasurementAttributionType, CompletionHandler<void()>&&);
-    void attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, const ApplicationBundleIdentifier&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, WebCore::PrivateClickMeasurement::IsRunningLayoutTest, CompletionHandler<void(std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>&&, DebugInfo&&)>&&);
+    void attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&&, const ApplicationBundleIdentifier&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, WebCore::PrivateClickMeasurement::IsRunningLayoutTest, CompletionHandler<void(std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>&&, DebugInfo&&)>&&);
 
     void privateClickMeasurementToStringForTesting(CompletionHandler<void(String)>&&) const;
     void markAllUnattributedPrivateClickMeasurementAsExpiredForTesting();

Modified: trunk/Tools/ChangeLog (285966 => 285967)


--- trunk/Tools/ChangeLog	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Tools/ChangeLog	2021-11-18 01:08:40 UTC (rev 285967)
@@ -1,3 +1,19 @@
+2021-11-17  John Wilander  <[email protected]>
+
+        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
+        https://bugs.webkit.org/show_bug.cgi?id=233173
+        <rdar://79426605>
+
+        Reviewed by Alex Christensen.
+
+        These changes are just a correction of a function name:
+        sourceUnlinkableToken() to sourceSecretToken()
+
+        * TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm:
+        (TestWebKitAPI::TEST):
+
 2021-11-17  Ryan Haddad  <[email protected]>
 
         ASSERT NOT REACHED under WebKit::Daemon::ConnectionToMachService seen with TestWebKitAPI.WebPushD.BasicCommunication and PermissionManagement

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp (285966 => 285967)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp	2021-11-18 01:08:40 UTC (rev 285967)
@@ -47,9 +47,9 @@
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(min6BitValue), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
     attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(min6BitValue, PrivateClickMeasurement::Priority(min6BitValue)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
 
-    auto attributionSourceURL = attribution.attributionReportSourceURL();
+    auto attributionSourceURL = attribution.attributionReportClickSourceURL();
     ASSERT_EQ(attributionSourceURL.string(), "https://webkit.org/.well-known/private-click-measurement/report-attribution/");
-    auto attributionAttributeOnURL = attribution.attributionReportAttributeOnURL();
+    auto attributionAttributeOnURL = attribution.attributionReportClickDestinationURL();
     ASSERT_EQ(attributionAttributeOnURL.string(), "https://example.com/.well-known/private-click-measurement/report-attribution/");
 }
 
@@ -137,8 +137,8 @@
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { emptyURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
     attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
 
-    ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
-    ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
 }
 
 TEST(PrivateClickMeasurement, InvalidDestinationHost)
@@ -146,8 +146,8 @@
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { emptyURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
     attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
 
-    ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
-    ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
 }
 
 TEST(PrivateClickMeasurement, AttributionTriggerData)
@@ -155,8 +155,8 @@
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
     attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData((PrivateClickMeasurement::AttributionTriggerData::MaxEntropy + 1), PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
 
-    ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
-    ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
 }
 
 TEST(PrivateClickMeasurement, InvalidPriority)
@@ -164,8 +164,8 @@
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
     attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy + 1)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
 
-    ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
-    ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
 }
 
 TEST(PrivateClickMeasurement, InvalidMissingConversion)
@@ -172,8 +172,8 @@
 {
     PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
 
-    ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
-    ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
+    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
     ASSERT_FALSE(attribution.timesToSend().sourceEarliestTimeToSend && attribution.timesToSend().destinationEarliestTimeToSend);
 }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm (285966 => 285967)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm	2021-11-18 00:38:36 UTC (rev 285966)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm	2021-11-18 01:08:40 UTC (rev 285967)
@@ -94,7 +94,7 @@
     // Continue the test.
     errorMessage = pcm.calculateAndUpdateSourceSecretToken(base64URLEncodeToString([blindedSignature bytes], [blindedSignature length]));
     EXPECT_FALSE(errorMessage);
-    auto& persistentToken = pcm.sourceUnlinkableToken();
+    auto& persistentToken = pcm.sourceSecretToken();
     EXPECT_TRUE(persistentToken);
     EXPECT_FALSE(persistentToken->tokenBase64URL.isEmpty());
     EXPECT_FALSE(persistentToken->keyIDBase64URL.isEmpty());
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to