⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 285967 in webkit


Ignore:
Timestamp:
Nov 17, 2021, 5:08:40 PM (5 years ago)
Author:
wilander@apple.com
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.
Location:
trunk
Files:
3 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r285945 r285967  
     12021-11-17  John Wilander  <wilander@apple.com>
     2
     3        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
     4        https://bugs.webkit.org/show_bug.cgi?id=233173
     5        <rdar://79426605>
     6
     7        Reviewed by Alex Christensen.
     8
     9        * http/tests/privateClickMeasurement/resources/redirectToConversionWithAttributionSource.py: Added.
     10        * http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive-expected.txt: Added.
     11        * http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html: Added.
     12
    1132021-11-17  Ryan Haddad  <ryanhaddad@apple.com>
    214
  • trunk/Source/WTF/ChangeLog

    r285954 r285967  
     12021-11-17  John Wilander  <wilander@apple.com>
     2
     3        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
     4        https://bugs.webkit.org/show_bug.cgi?id=233173
     5        <rdar://79426605>
     6
     7        Reviewed by Alex Christensen.
     8
     9        * wtf/URL.cpp:
     10        (WTF::queryParameters):
     11            New convenience getter.
     12        * wtf/URL.h:
     13
    1142021-11-17  Chris Dumez  <cdumez@apple.com>
    215
  • trunk/Source/WTF/wtf/URL.cpp

    r283522 r285967  
    11911191#endif
    11921192
     1193Vector<KeyValuePair<String, String>> queryParameters(const URL& url)
     1194{
     1195    return URLParser::parseURLEncodedForm(url.query());
     1196}
     1197
    11931198Vector<KeyValuePair<String, String>> differingQueryParameters(const URL& firstURL, const URL& secondURL)
    11941199{
  • trunk/Source/WTF/wtf/URL.h

    r285588 r285967  
    251251WTF_EXPORT_PRIVATE bool protocolHostAndPortAreEqual(const URL&, const URL&);
    252252WTF_EXPORT_PRIVATE Vector<KeyValuePair<String, String>> differingQueryParameters(const URL&, const URL&);
     253WTF_EXPORT_PRIVATE Vector<KeyValuePair<String, String>> queryParameters(const URL&);
    253254WTF_EXPORT_PRIVATE bool isEqualIgnoringQueryAndFragments(const URL&, const URL&);
    254255WTF_EXPORT_PRIVATE void removeQueryParameters(URL&, const HashSet<String>&);
  • trunk/Source/WebCore/ChangeLog

    r285964 r285967  
     12021-11-17  John Wilander  <wilander@apple.com>
     2
     3        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
     4        https://bugs.webkit.org/show_bug.cgi?id=233173
     5        <rdar://79426605>
     6
     7        Reviewed by Alex Christensen.
     8
     9        This patch enables click destination sites a non-JavaScript way to fire triggering
     10        events without a requirement to make cross-site requests to source sites. This is
     11        referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
     12        https://github.com/privacycg/private-click-measurement/issues/71
     13
     14        The reason why some merchants want such an "API" is reluctance to deploy new
     15        JavaScript on their sites. In some industries it's even a compliance issue. Legacy
     16        "pixels" are however accepted and so a same-site "pixel" can work for them.
     17
     18        Test: http/tests/privateClickMeasurement/triggering-event-with-attribution-source-through-fetch-keepalive.html
     19
     20        * html/HTMLAnchorElement.cpp:
     21        (WebCore::HTMLAnchorElement::handleClick):
     22            This change is because of clarification in naming:
     23            - attributionReportSourceURL to attributionReportClickSourceURL
     24        * loader/PrivateClickMeasurement.cpp:
     25        (WebCore::PrivateClickMeasurement::parseAttributionRequestQuery):
     26            New function that parses out query string parameters.
     27        (WebCore::PrivateClickMeasurement::parseAttributionRequest):
     28            Now calls the new PrivateClickMeasurement::parseAttributionRequestQuery()
     29            which handles data coming in in query parameters, in this case
     30            the new parameter "attributionSource."
     31        (WebCore::PrivateClickMeasurement::attributionReportClickSourceURL const):
     32            New name.
     33        (WebCore::PrivateClickMeasurement::attributionReportClickDestinationURL const):
     34            New name.
     35        (WebCore::PrivateClickMeasurement::attributionReportJSON const):
     36            Now uses the constant privateClickMeasurementVersion.
     37        (WebCore::PrivateClickMeasurement::tokenSignatureJSON const):
     38            Now uses the constant privateClickMeasurementVersion.
     39        (WebCore::PrivateClickMeasurement::attributionReportSourceURL const): Deleted.
     40            Renamed attributionReportClickSourceURL.
     41        (WebCore::PrivateClickMeasurement::attributionReportAttributeOnURL const): Deleted.
     42            Renamed attributionReportClickDestinationURL.
     43        * loader/PrivateClickMeasurement.h:
     44        (WebCore::PrivateClickMeasurement::sourceSecretToken const):
     45            New name.
     46        (WebCore::PrivateClickMeasurement::AttributionTriggerData::encode const):
     47        (WebCore::PrivateClickMeasurement::AttributionTriggerData::decode):
     48            Encoding and decoding of the new field sourceRegistrableDomain.
     49        (WebCore::PrivateClickMeasurement::sourceUnlinkableToken const): Deleted.
     50            Renamed sourceSecretToken.
     51            Note that it was always the secret token used, just bad renaming earlier.
     52
    1532021-11-17  Tim Horton  <timothy_horton@apple.com>
    254
  • trunk/Source/WebCore/html/HTMLAnchorElement.cpp

    r285224 r285967  
    535535    // A matching triggering event needs to happen before an attribution report can be sent.
    536536    // Thus, URLs should be empty for now.
    537     ASSERT(!privateClickMeasurement || (privateClickMeasurement->attributionReportSourceURL().isNull() && privateClickMeasurement->attributionReportAttributeOnURL().isNull()));
     537    ASSERT(!privateClickMeasurement || (privateClickMeasurement->attributionReportClickSourceURL().isNull() && privateClickMeasurement->attributionReportClickDestinationURL().isNull()));
    538538   
    539539    frame->loader().changeLocation(completedURL, effectiveTarget, &event, referrerPolicy, document().shouldOpenExternalURLsPolicyToPropagate(), newFrameOpenerPolicy, downloadAttribute, systemPreviewInfo, WTFMove(privateClickMeasurement));
  • trunk/Source/WebCore/loader/PrivateClickMeasurement.cpp

    r285170 r285967  
    4545const size_t privateClickMeasurementAttributionTriggerDataPathSegmentSize = 2;
    4646const size_t privateClickMeasurementPriorityPathSegmentSize = 2;
     47const uint8_t privateClickMeasurementVersion = 2;
    4748
    4849const Seconds PrivateClickMeasurement::maxAge()
     
    104105}
    105106
     107Expected<PrivateClickMeasurement::AttributionTriggerData, String> PrivateClickMeasurement::parseAttributionRequestQuery(const URL& redirectURL)
     108{
     109    if (!redirectURL.hasQuery())
     110        return AttributionTriggerData { };
     111
     112    auto parameters = queryParameters(redirectURL);
     113    if (!parameters.size())
     114        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL had a query string but it didn't contain supported parameters."_s);
     115
     116    if (parameters.size() > 1)
     117        return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's query string contained unsupported parameters."_s);
     118
     119    auto parameter = parameters.first();
     120    if (parameter.key == "attributionSource") {
     121        if (parameter.value.isEmpty())
     122            return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL's attributionSource query parameter had no value."_s);
     123
     124        auto attributionSourceURL = URL(URL(), parameter.value);
     125        if (!attributionSourceURL.isValid() || (attributionSourceURL.hasPath() && attributionSourceURL.path().length() > 1) || attributionSourceURL.hasCredentials() || attributionSourceURL.hasQuery() || attributionSourceURL.hasFragmentIdentifier())
     126            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);
     127
     128        AttributionTriggerData attributionTriggerData;
     129        attributionTriggerData.sourceRegistrableDomain = RegistrableDomain { attributionSourceURL };
     130        return attributionTriggerData;
     131    }
     132
     133    return makeUnexpected("[Private Click Measurement] Conversion was not accepted because the URL did not contain an attributionSource query parameter."_s);
     134}
     135
    106136Expected<PrivateClickMeasurement::AttributionTriggerData, String> PrivateClickMeasurement::parseAttributionRequest(const URL& redirectURL)
    107137{
     
    110140        return makeUnexpected(nullString());
    111141
    112     if (!redirectURL.protocolIs("https") || redirectURL.hasCredentials() || redirectURL.hasQuery() || redirectURL.hasFragmentIdentifier())
    113         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);
    114 
     142    if (!redirectURL.protocolIs("https") || redirectURL.hasCredentials() || redirectURL.hasFragmentIdentifier())
     143        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);
     144
     145    auto result = parseAttributionRequestQuery(redirectURL);
     146    if (!result && !result.error().isEmpty())
     147        return result;
     148    auto attributionTriggerData = result.value();
    115149
    116150    auto prefixLength = sizeof(privateClickMeasurementTriggerAttributionPath) - 1;
     
    120154            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));
    121155
    122         return AttributionTriggerData { static_cast<uint8_t>(*attributionTriggerDataUInt64), Priority { 0 } };
     156        attributionTriggerData.data = static_cast<uint8_t>(*attributionTriggerDataUInt64);
     157        attributionTriggerData.priority = 0;
     158        return attributionTriggerData;
    123159    }
    124160   
     
    132168            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));
    133169
    134         return AttributionTriggerData { static_cast<uint8_t>(*attributionTriggerDataUInt64), Priority { static_cast<uint8_t>(*attributionPriorityUInt64) } };
     170        attributionTriggerData.data = static_cast<uint8_t>(*attributionTriggerDataUInt64);
     171        attributionTriggerData.priority = static_cast<uint8_t>(*attributionPriorityUInt64);
     172        return attributionTriggerData;
    135173    }
    136174
     
    190228}
    191229
    192 URL PrivateClickMeasurement::attributionReportSourceURL() const
     230URL PrivateClickMeasurement::attributionReportClickSourceURL() const
    193231{
    194232    if (!isValid())
     
    198236}
    199237
    200 URL PrivateClickMeasurement::attributionReportAttributeOnURL() const
     238URL PrivateClickMeasurement::attributionReportClickDestinationURL() const
    201239{
    202240    if (!isValid())
     
    217255    reportDetails->setString("attributed_on_site"_s, m_destinationSite.registrableDomain.string());
    218256    reportDetails->setInteger("trigger_data"_s, m_attributionTriggerData->data);
    219     reportDetails->setInteger("version"_s, 2);
     257    reportDetails->setInteger("version"_s, privateClickMeasurementVersion);
    220258
    221259    // This token has been kept secret this far and cannot be linked to the unlinkable token.
     
    275313    // This token can not be linked to the secret token.
    276314    reportDetails->setString("source_unlinkable_token"_s, m_sourceUnlinkableToken.valueBase64URL);
    277     reportDetails->setInteger("version"_s, 2);
     315    reportDetails->setInteger("version"_s, privateClickMeasurementVersion);
    278316    return reportDetails;
    279317}
  • trunk/Source/WebCore/loader/PrivateClickMeasurement.h

    r285170 r285967  
    173173
    174174        enum class WasSent : bool { No, Yes };
    175        
     175
     176        AttributionTriggerData() = default;
    176177        AttributionTriggerData(uint8_t data, Priority priority, WasSent wasSent = WasSent::No)
    177178            : data { data }
     
    189190        PriorityValue priority;
    190191        WasSent wasSent = WasSent::No;
     192        std::optional<RegistrableDomain> sourceRegistrableDomain;
    191193
    192194        template<class Encoder> void encode(Encoder&) const;
     
    318320    WEBCORE_EXPORT AttributionSecondsUntilSendData attributeAndGetEarliestTimeToSend(AttributionTriggerData&&, IsRunningLayoutTest);
    319321    WEBCORE_EXPORT bool hasHigherPriorityThan(const PrivateClickMeasurement&) const;
    320     WEBCORE_EXPORT URL attributionReportSourceURL() const;
    321     WEBCORE_EXPORT URL attributionReportAttributeOnURL() const;
     322    WEBCORE_EXPORT URL attributionReportClickSourceURL() const;
     323    WEBCORE_EXPORT URL attributionReportClickDestinationURL() const;
    322324    WEBCORE_EXPORT Ref<JSON::Object> attributionReportJSON() const;
    323325    const SourceSite& sourceSite() const { return m_sourceSite; };
     
    372374
    373375    void setSourceUnlinkableTokenValue(const String& value) { m_sourceUnlinkableToken.valueBase64URL = value; }
    374     const std::optional<SourceSecretToken>& sourceUnlinkableToken() const { return m_sourceSecretToken; }
     376    const std::optional<SourceSecretToken>& sourceSecretToken() const { return m_sourceSecretToken; }
    375377    WEBCORE_EXPORT void setSourceSecretToken(SourceSecretToken&&);
    376378
     
    381383
    382384private:
     385    static Expected<AttributionTriggerData, String> parseAttributionRequestQuery(const URL&);
    383386    bool isValid() const;
    384387
     
    506509void PrivateClickMeasurement::AttributionTriggerData::encode(Encoder& encoder) const
    507510{
    508     encoder << data << priority << wasSent;
     511    encoder << data << priority << wasSent << sourceRegistrableDomain;
    509512}
    510513
     
    527530        return std::nullopt;
    528531   
    529     return AttributionTriggerData { WTFMove(*data), Priority { *priority }, *wasSent };
     532    std::optional<std::optional<RegistrableDomain>> sourceRegistrableDomain;
     533    decoder >> sourceRegistrableDomain;
     534    if (!sourceRegistrableDomain)
     535        return std::nullopt;
     536   
     537    AttributionTriggerData attributionTriggerData { WTFMove(*data), Priority { *priority }, *wasSent };
     538    attributionTriggerData.sourceRegistrableDomain = WTFMove(*sourceRegistrableDomain);
     539    return attributionTriggerData;
    530540}
    531541
  • trunk/Source/WebKit/ChangeLog

    r285966 r285967  
     12021-11-17  John Wilander  <wilander@apple.com>
     2
     3        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
     4        https://bugs.webkit.org/show_bug.cgi?id=233173
     5        <rdar://79426605>
     6
     7        Reviewed by Alex Christensen.
     8
     9        This patch enables click destination sites a non-JavaScript way to fire triggering
     10        events without a requirement to make cross-site requests to source sites. This is
     11        referred to as a "same-site pixel API" and has been discussed in W3C Privacy CG:
     12        https://github.com/privacycg/private-click-measurement/issues/71
     13
     14        The reason why some merchants want such an "API" is reluctance to deploy new
     15        JavaScript on their sites. In some industries it's even a compliance issue. Legacy
     16        "pixels" are however accepted and so a same-site "pixel" can work for them.
     17
     18        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp:
     19        (WebKit::PCM::Database::insertPrivateClickMeasurement):
     20            These changes are just a correction of a function name:
     21            - sourceUnlinkableToken() to sourceSecretToken()
     22            Note that it was always the secret token used, just bad renaming earlier.
     23        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp:
     24        (WebKit::PrivateClickMeasurementManager::handleAttribution):
     25            Now checks if the incoming WebCore::PrivateClickMeasurement::AttributionTriggerData
     26            carries a sourceRegistrableDomain. If so, it accepts that domain as the source site
     27            for attribution if the triggering event was same-site as the first-party.
     28        (WebKit::PrivateClickMeasurementManager::attribute):
     29        (WebKit::PrivateClickMeasurementManager::fireConversionRequest):
     30            These changes are just a correction of a function name:
     31            - sourceUnlinkableToken() to sourceSecretToken()
     32            Note that it was always the secret token used, just bad renaming earlier.
     33        (WebKit::PrivateClickMeasurementManager::fireConversionRequestImpl):
     34            These changes are because of clarification in naming:
     35            - attributionReportSourceURL to attributionReportClickSourceURL
     36            - attributionReportAttributeOnURL to attributionReportClickDestinationURL
     37        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:
     38        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp:
     39        (WebKit::PCM::Store::attributePrivateClickMeasurement):
     40        * NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h:
     41
    1422021-11-17  Wenson Hsieh  <wenson_hsieh@apple.com>
    243
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.cpp

    r285170 r285967  
    176176        return;
    177177
    178     auto& sourceUnlinkableToken = attribution.sourceUnlinkableToken();
     178    auto& sourceSecretToken = attribution.sourceSecretToken();
    179179    if (attributionType == PrivateClickMeasurementAttributionType::Attributed) {
    180180        auto attributionTriggerData = attribution.attributionTriggerData() ? attribution.attributionTriggerData().value().data : -1;
     
    195195            || statement->bindDouble(6, attribution.timeOfAdClick().secondsSinceEpoch().value()) != SQLITE_OK
    196196            || statement->bindDouble(7, sourceEarliestTimeToSend) != SQLITE_OK
    197             || statement->bindText(8, sourceUnlinkableToken ? sourceUnlinkableToken->tokenBase64URL : emptyString()) != SQLITE_OK
    198             || statement->bindText(9, sourceUnlinkableToken ? sourceUnlinkableToken->signatureBase64URL : emptyString()) != SQLITE_OK
    199             || statement->bindText(10, sourceUnlinkableToken ? sourceUnlinkableToken->keyIDBase64URL : emptyString()) != SQLITE_OK
     197            || statement->bindText(8, sourceSecretToken ? sourceSecretToken->tokenBase64URL : emptyString()) != SQLITE_OK
     198            || statement->bindText(9, sourceSecretToken ? sourceSecretToken->signatureBase64URL : emptyString()) != SQLITE_OK
     199            || statement->bindText(10, sourceSecretToken ? sourceSecretToken->keyIDBase64URL : emptyString()) != SQLITE_OK
    200200            || statement->bindDouble(11, destinationEarliestTimeToSend) != SQLITE_OK
    201201            || statement->bindText(12, attribution.sourceApplicationBundleID()) != SQLITE_OK
     
    215215        || statement->bindInt(3, attribution.sourceID().id) != SQLITE_OK
    216216        || statement->bindDouble(4, attribution.timeOfAdClick().secondsSinceEpoch().value()) != SQLITE_OK
    217         || statement->bindText(5, sourceUnlinkableToken ? sourceUnlinkableToken->tokenBase64URL : emptyString()) != SQLITE_OK
    218         || statement->bindText(6, sourceUnlinkableToken ? sourceUnlinkableToken->signatureBase64URL : emptyString()) != SQLITE_OK
    219         || statement->bindText(7, sourceUnlinkableToken ? sourceUnlinkableToken->keyIDBase64URL : emptyString()) != SQLITE_OK
     217        || statement->bindText(5, sourceSecretToken ? sourceSecretToken->tokenBase64URL : emptyString()) != SQLITE_OK
     218        || statement->bindText(6, sourceSecretToken ? sourceSecretToken->signatureBase64URL : emptyString()) != SQLITE_OK
     219        || statement->bindText(7, sourceSecretToken ? sourceSecretToken->keyIDBase64URL : emptyString()) != SQLITE_OK
    220220        || statement->bindText(8, attribution.sourceApplicationBundleID()) != SQLITE_OK
    221221        || statement->step() != SQLITE_DONE) {
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.cpp

    r285170 r285967  
    249249    }
    250250
     251    RegistrableDomain sourceDomain;
    251252    if (redirectDomain.matches(firstPartyURL)) {
    252         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);
    253         return;
    254     }
     253        if (!attributionTriggerData.sourceRegistrableDomain) {
     254            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);
     255            return;
     256        }
     257        sourceDomain = *attributionTriggerData.sourceRegistrableDomain;
     258    } else if (attributionTriggerData.sourceRegistrableDomain) {
     259        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);
     260        return;
     261    } else
     262        sourceDomain = WTFMove(redirectDomain);
    255263
    256264    m_client->broadcastConsoleMessage(MessageLevel::Log, "[Private Click Measurement] Triggering event accepted."_s);
    257265
    258     attribute(SourceSite { WTFMove(redirectDomain) }, AttributionDestinationSite { firstPartyURL }, WTFMove(attributionTriggerData), m_privateClickMeasurementAppBundleIDForTesting ? *m_privateClickMeasurementAppBundleIDForTesting : applicationBundleIdentifier);
     266    attribute(SourceSite { WTFMove(sourceDomain) }, AttributionDestinationSite { firstPartyURL }, WTFMove(attributionTriggerData), m_privateClickMeasurementAppBundleIDForTesting ? *m_privateClickMeasurementAppBundleIDForTesting : applicationBundleIdentifier);
    259267}
    260268
     
    277285}
    278286
    279 void PrivateClickMeasurementManager::attribute(const SourceSite& sourceSite, const AttributionDestinationSite& destinationSite, AttributionTriggerData&& attributionTriggerData, const ApplicationBundleIdentifier& applicationBundleIdentifier)
    280 {
    281     if (!featureEnabled())
    282         return;
    283 
    284     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) {
     287void PrivateClickMeasurementManager::attribute(SourceSite&& sourceSite, AttributionDestinationSite&& destinationSite, AttributionTriggerData&& attributionTriggerData, const ApplicationBundleIdentifier& applicationBundleIdentifier)
     288{
     289    if (!featureEnabled())
     290        return;
     291
     292    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) {
    285293        if (!weakThis)
    286294            return;
     
    320328        return;
    321329
    322     if (!attribution.sourceUnlinkableToken()) {
     330    if (!attribution.sourceSecretToken()) {
    323331        fireConversionRequestImpl(attribution, attributionReportEndpoint);
    324332        return;
     
    340348
    341349        auto keyID = base64URLEncodeToString(publicKeyDataHash.data(), publicKeyDataHash.size());
    342         if (keyID != attribution.sourceUnlinkableToken()->keyIDBase64URL)
     350        if (keyID != attribution.sourceSecretToken()->keyIDBase64URL)
    343351            return;
    344352
     
    352360    switch (attributionReportEndpoint) {
    353361    case PrivateClickMeasurement::AttributionReportEndpoint::Source:
    354         attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportSourceURL : attribution.attributionReportSourceURL();
     362        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportClickSourceURL : attribution.attributionReportClickSourceURL();
    355363        break;
    356364    case PrivateClickMeasurement::AttributionReportEndpoint::Destination:
    357         attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportAttributeOnURL : attribution.attributionReportAttributeOnURL();
     365        attributionURL = m_attributionReportTestConfig ? m_attributionReportTestConfig->attributionReportClickDestinationURL : attribution.attributionReportClickDestinationURL();
    358366    }
    359367
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h

    r285170 r285967  
    7777    void insertPrivateClickMeasurement(PrivateClickMeasurement&&, PrivateClickMeasurementAttributionType, CompletionHandler<void()>&&);
    7878    void clearSentAttribution(PrivateClickMeasurement&&, PrivateClickMeasurement::AttributionReportEndpoint);
    79     void attribute(const SourceSite&, const AttributionDestinationSite&, AttributionTriggerData&&, const ApplicationBundleIdentifier&);
     79    void attribute(SourceSite&&, AttributionDestinationSite&&, AttributionTriggerData&&, const ApplicationBundleIdentifier&);
    8080    void fireConversionRequest(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
    8181    void fireConversionRequestImpl(const PrivateClickMeasurement&, PrivateClickMeasurement::AttributionReportEndpoint);
     
    9595
    9696    struct AttributionReportTestConfig {
    97         URL attributionReportSourceURL;
    98         URL attributionReportAttributeOnURL;
     97        URL attributionReportClickSourceURL;
     98        URL attributionReportClickDestinationURL;
    9999    };
    100100
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.cpp

    r285170 r285967  
    9595}
    9696
    97 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)
     97void 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)
    9898{
    9999    postTask([this, protectedThis = Ref { *this }, sourceSite = sourceSite.isolatedCopy(), destinationSite = destinationSite.isolatedCopy(), applicationBundleIdentifier = applicationBundleIdentifier.isolatedCopy(), attributionTriggerData = WTFMove(attributionTriggerData), isRunningTest, completionHandler = WTFMove(completionHandler)] () mutable {
  • trunk/Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementStore.h

    r285170 r285967  
    5454
    5555    void insertPrivateClickMeasurement(WebCore::PrivateClickMeasurement&&, WebKit::PrivateClickMeasurementAttributionType, CompletionHandler<void()>&&);
    56     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&&)>&&);
     56    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&&)>&&);
    5757
    5858    void privateClickMeasurementToStringForTesting(CompletionHandler<void(String)>&&) const;
  • trunk/Tools/ChangeLog

    r285963 r285967  
     12021-11-17  John Wilander  <wilander@apple.com>
     2
     3        PCM: Add capability for click destination to fire triggering event without cross-site requests to the click source
     4        https://bugs.webkit.org/show_bug.cgi?id=233173
     5        <rdar://79426605>
     6
     7        Reviewed by Alex Christensen.
     8
     9        These changes are just a correction of a function name:
     10        sourceUnlinkableToken() to sourceSecretToken()
     11
     12        * TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp:
     13        (TestWebKitAPI::TEST):
     14        * TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm:
     15        (TestWebKitAPI::TEST):
     16
    1172021-11-17  Ryan Haddad  <ryanhaddad@apple.com>
    218
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/PrivateClickMeasurement.cpp

    r285170 r285967  
    4848    attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(min6BitValue, PrivateClickMeasurement::Priority(min6BitValue)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
    4949
    50     auto attributionSourceURL = attribution.attributionReportSourceURL();
     50    auto attributionSourceURL = attribution.attributionReportClickSourceURL();
    5151    ASSERT_EQ(attributionSourceURL.string(), "https://webkit.org/.well-known/private-click-measurement/report-attribution/");
    52     auto attributionAttributeOnURL = attribution.attributionReportAttributeOnURL();
     52    auto attributionAttributeOnURL = attribution.attributionReportClickDestinationURL();
    5353    ASSERT_EQ(attributionAttributeOnURL.string(), "https://example.com/.well-known/private-click-measurement/report-attribution/");
    5454}
     
    138138    attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
    139139
    140     ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
    141     ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
     140    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
     141    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
    142142}
    143143
     
    147147    attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
    148148
    149     ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
    150     ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
     149    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
     150    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
    151151}
    152152
     
    156156    attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData((PrivateClickMeasurement::AttributionTriggerData::MaxEntropy + 1), PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
    157157
    158     ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
    159     ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
     158    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
     159    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
    160160}
    161161
     
    165165    attribution.attributeAndGetEarliestTimeToSend(PrivateClickMeasurement::AttributionTriggerData(PrivateClickMeasurement::AttributionTriggerData::MaxEntropy, PrivateClickMeasurement::Priority(PrivateClickMeasurement::Priority::MaxEntropy + 1)), WebCore::PrivateClickMeasurement::IsRunningLayoutTest::No);
    166166
    167     ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
    168     ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
     167    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
     168    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
    169169}
    170170
     
    173173    PrivateClickMeasurement attribution { PrivateClickMeasurement::SourceID(PrivateClickMeasurement::SourceID::MaxEntropy), PrivateClickMeasurement::SourceSite { webKitURL }, PrivateClickMeasurement::AttributionDestinationSite { exampleURL }, "test.bundle.identifier", WallTime::now(), WebCore::PrivateClickMeasurement::AttributionEphemeral::No };
    174174
    175     ASSERT_TRUE(attribution.attributionReportSourceURL().isEmpty());
    176     ASSERT_TRUE(attribution.attributionReportAttributeOnURL().isEmpty());
     175    ASSERT_TRUE(attribution.attributionReportClickSourceURL().isEmpty());
     176    ASSERT_TRUE(attribution.attributionReportClickDestinationURL().isEmpty());
    177177    ASSERT_FALSE(attribution.timesToSend().sourceEarliestTimeToSend && attribution.timesToSend().destinationEarliestTimeToSend);
    178178}
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/cocoa/PrivateClickMeasurementCocoa.mm

    r284890 r285967  
    9595    errorMessage = pcm.calculateAndUpdateSourceSecretToken(base64URLEncodeToString([blindedSignature bytes], [blindedSignature length]));
    9696    EXPECT_FALSE(errorMessage);
    97     auto& persistentToken = pcm.sourceUnlinkableToken();
     97    auto& persistentToken = pcm.sourceSecretToken();
    9898    EXPECT_TRUE(persistentToken);
    9999    EXPECT_FALSE(persistentToken->tokenBase64URL.isEmpty());
Note: See TracChangeset for help on using the changeset viewer.