Bug 228104 - PCM: Safari on iOS and macOS are not sending ad click attribution reports for Private Click Measurement
Summary: PCM: Safari on iOS and macOS are not sending ad click attribution reports for...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit Misc. (show other bugs)
Version: Safari 14
Hardware: Mac (Intel) macOS 11
: P2 Normal
Assignee: John Wilander
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-07-20 04:13 PDT by Karthik C
Modified: 2021-11-02 11:36 PDT (History)
8 users (show)

See Also:


Attachments
Technology browser console logs (46.56 KB, image/png)
2021-07-21 00:43 PDT, Karthik C
no flags Details
Conversion registration with user agent (147.87 KB, image/png)
2021-07-21 00:44 PDT, Karthik C
no flags Details
Patch (20.39 KB, patch)
2021-11-01 15:31 PDT, Kate Cheney
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (31.10 KB, patch)
2021-11-01 16:20 PDT, Kate Cheney
no flags Details | Formatted Diff | Diff
Patch for landing (32.54 KB, patch)
2021-11-02 10:53 PDT, Kate Cheney
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Karthik C 2021-07-20 04:13:50 PDT
I am trying to use the Apple Private Click Measurement API for measuring ads conversions. I am able to receive attribution reports in Safari Technology Preview browser(Release 127 (Safari 15.0, WebKit 16612.1.18.11.3)) but on trying the same steps in Safari(Safari Version 14.1.1 (16611.2.7.1.4)) on macOS BigSur 11.4 (Macbook Pro), I am not receiving any attribution reports.
Waited for more than 48 hours after conversion registration on the .well-known url but still don't see any attribution reports.

Observing the same on my iPhone iOS(14.6 18F72) Safari mobile browser.

In Safari Technology Preview browser as well, attribution reports are not sent reliably when the delay is 1-2 days. Some are sent while some are never sent.

Filed bugs about the same with Apple here: 
https://feedbackassistant.apple.com/feedback/9367526 (iOS Safari)
https://feedbackassistant.apple.com/feedback/9367494 (macOS Safari)
Comment 1 John Wilander 2021-07-20 09:26:44 PDT
Hi! Thanks for filing. Could you share a repro case online or a repro case with detailed steps such as your markup and what Web Inspector says? Thanks!
Comment 2 Karthik C 2021-07-21 00:43:12 PDT
Created attachment 433921 [details]
Technology browser console logs

Shows all steps of user agent storing click, registering conversion, scheduling sending and sending an attribution report.
Comment 3 Karthik C 2021-07-21 00:44:23 PDT
Created attachment 433922 [details]
Conversion registration with user agent

GET request followed by redirect to .well-known URL.
Comment 4 Karthik C 2021-07-21 00:45:08 PDT
Steps for reproducing:
1) The click is served by click source website with these attributes.
<a href="addestination.html"
 attributionsourceid="55" 
 attributiondestination="https://googleusercontent.com">
Karthik's advertiser
</a>

2) Conversion registration on advertiser page with user agent happens with a GET followed by 302 redirect to .well-known url
.well-known/private-click-measurement/trigger-attribution/03/03

3) In Safari Technology Preview browser can see 2 attribution requests being sent, one to advertiser(static html page so it fails in my trial) but is received successfully at AdTech endpoint(appspot domain).

Attached screenshots showing steps 2 & 3.
Comment 5 Karthik C 2021-07-21 00:50:22 PDT
Can you please clarify if this feature has been enabled in Safari macOS and Safari iOS browsers? If so, from which version ?
Comment 6 John Wilander 2021-07-22 14:41:49 PDT
Thanks for the follow-up. I need more details though.

(In reply to Karthik C from comment #4)
> Steps for reproducing:
> 1) The click is served by click source website with these attributes.

Let's call this website clickSource.example.

> <a href="addestination.html"

So this will take the user to addestination.html on clickSource.example? Does that page load then redirect to googleusercontent.com? If not, how does the user land on googleusercontent.com as first-party website as the result of a click on the link?

>  attributionsourceid="55" 
>  attributiondestination="https://googleusercontent.com">

This is the required first-party landing website, i.e. googleusercontent.com.

> Karthik's advertiser
> </a>
> 
> 2) Conversion registration on advertiser page with user agent happens with a
> GET followed by 302 redirect to .well-known url
> .well-known/private-click-measurement/trigger-attribution/03/03

Does this GET request happen on googleusercontent.com and go to clickSource.example which makes a same-site redirect to clickSource.example?

> 
> 3) In Safari Technology Preview browser can see 2 attribution requests being
> sent, one to advertiser(static html page so it fails in my trial) but is
> received successfully at AdTech endpoint(appspot domain).

Which version of Safari Technology Preview is this and on which version of macOS?

It sounds to me like you are describing a working case above. Correct?

Are you saying the same markup and server-side behavior is not resulting in attribution reports on shipping Safari on macOS Big Sur? If so, what happens if you use "attributeon" instead of "attributiondestination" as described in https://webkit.org/blog/11529/introducing-private-click-measurement-pcm/ ?
Comment 7 Radar WebKit Bug Importer 2021-07-22 17:00:41 PDT
<rdar://problem/80991209>
Comment 8 John Wilander 2021-07-26 11:54:09 PDT
Any updates based on what I wrote above, Karthik?
Comment 9 Karthik C 2021-07-28 03:01:25 PDT
The AdTech website is an appspot domain:
https://<my-adtech-domain>.appspot.com/
The eTLD+1 for this is <my-adtech-domain>.appspot.com

This Appspot domain serves a page with just one link with attributionsourceid and attributiondestination set:
http://storage.cloud.google.com/karthik-bucket1/addestination.html
This is a static html page on Google Cloud Storage. This URL redirects to <random-id>-apidata.googleusercontent.com which has a eTLD+1 of googleusercontent.com.
Hence I have set the attributiondestination="https://googleusercontent.com"

For conversion registration, the GET request happens on https://<my-adtech-domain>.appspot.com/ which has an endpoint for handling it. This is then redirected to https://<my-adtech-domain>.appspot.com/.well-known/private-click-measurement/trigger-attribution/05/05 as shows in my attachment,
with 05 being the trigger-data and also priority.


The Safari Technology Preview browser version is
Release 128 (Safari 15.0, WebKit 16612.1.22.11.3)
macOS version is macOS Big Sur 11.5
With 1-2 delayed attribution reports, some are sent while some are dropped. This seems to happen even when the browser is kept open. I observed attribution report sent after browser is reopened, but without some instrumentation it is difficult to detect attribution reports loss at AdTech side (due to PCM not being enabled, older browser version, possible loss due to pending bugs etc).
Comment 10 Karthik C 2021-07-28 08:47:26 PDT
I will try using attributeon as the attribute and see if that results in sending of attribution reports in regular Safari. I will have to wait for 1-2 days to see attribution reports and verify if this works. Is there any faster way to verify this?

I had tried using the older .well-known url of .well-known/ad-click-attribution and it didn't help.
Comment 11 Karthik C 2021-07-28 13:15:00 PDT
Used attributeon attribute and am able to see attribution reports from Safari now! Thanks.

Can we please figure out a way why some attribution reports are getting missed?

Is there a way to know the % loss of attribution reports due to various reasons: older browser version, user PCM disabled, attribution reports loss due to any bugs, browser being inactive for long durations.
Comment 12 Karthik C 2021-08-04 11:16:39 PDT
Any updates about
1) the intermittent loss of attribution reports we are observing in Safari Technology Preview browser,
2) options to measuring % lost attribution reports for different reason:  older browser version, user PCM disabled, browser being inactive for long durations, attribution reports loss due to bugs.
Comment 13 John Wilander 2021-08-04 21:23:38 PDT
Do both endpoints have valid TLS certificates and respond to the well-known location?
Comment 14 Karthik C 2021-08-09 09:17:48 PDT
Yes both end points have valid TLS certificates.
Comment 15 John Wilander 2021-08-09 16:48:31 PDT
(In reply to Karthik C from comment #14)
> Yes both end points have valid TLS certificates.

Thanks! The thing I'm trying to see here is if we may have an issue where if the first of the two attribution report requests fails for some reason (bad TLS cert, error response, no response), we would not try to send the second one. Based on your info, that doesn't sound plausible.
Comment 16 John Wilander 2021-08-10 23:28:09 PDT
Looking back at your earlier descriptions, you said:

> 3) In Safari Technology Preview browser can see 2 attribution requests being sent, one to advertiser(static html page so it fails in my trial) but is received successfully at AdTech endpoint(appspot domain).

This sounds like the report to the click destination (the advertiser) will result in a failed network request. Is that true?

If so, are the missing reports ones that should have been sent *after* the failing one?

They are sent on one randomized timer each which means they can come in either order and I'm wondering if there's a specific order to them when you see a missing request?
Comment 17 John Wilander 2021-08-17 08:49:55 PDT
Karthik, any comments on my thoughts and questions above? Thanks!
Comment 18 Karthik C 2021-08-19 08:45:31 PDT
I am not sure which attribution request was scheduled to be sent first(advertiser or AdTech) as I hadn't kept note of the same. Are you suspecting the failure of one attribution report(sent to AdTech / Advertiser) to affect the other?
Comment 19 John Wilander 2021-08-19 08:50:48 PDT
(In reply to Karthik C from comment #18)
> I am not sure which attribution request was scheduled to be sent
> first(advertiser or AdTech) as I hadn't kept note of the same. Are you
> suspecting the failure of one attribution report(sent to AdTech /
> Advertiser) to affect the other?

Right, that could be a lead here. If the first one fails, maybe the second one isn't sent (which would be a bug). That's what I'm trying to find out.
Comment 20 Karthik C 2021-08-25 23:57:17 PDT
Thanks for looking into this. From the console logs, I cannot distinguish if the first one was to the AdTech / advertiser.
Comment 21 Karthik C 2021-09-16 07:06:11 PDT
Hi I also had one more query regarding the random delay. Once a conversion occurs and the random delay of 24-48 hours is set, if a conversion of a higher priority occurs say 24 hours later, will the attribution report be rescheduled to be sent at a different time 24-48 hours from that one or the first scheduled time will be kept as is ?
Comment 22 Kate Cheney 2021-11-01 15:31:05 PDT
Created attachment 443021 [details]
Patch
Comment 23 John Wilander 2021-11-01 15:34:19 PDT
Thanks, Kate! Could you add a test that would have timed out without the fix?
Comment 24 John Wilander 2021-11-01 15:35:42 PDT
(In reply to John Wilander from comment #23)
> Thanks, Kate! Could you add a test that would have timed out without the fix?

Or are you saying existing tests would have timed out without the explicit trigger function? If so, you should be able to say that you updated existing tests, right?
Comment 25 Kate Cheney 2021-11-01 15:39:12 PDT
(In reply to John Wilander from comment #24)
> (In reply to John Wilander from comment #23)
> > Thanks, Kate! Could you add a test that would have timed out without the fix?
> 
> Or are you saying existing tests would have timed out without the explicit
> trigger function? If so, you should be able to say that you updated existing
> tests, right?

All of the tests will fail without the fix now that I have removed the immediate timer fire. I didn't update any existing tests, I removed some of the test infrastructure that hid this bug by launching the timer immediately if running a test.
Comment 26 Kate Cheney 2021-11-01 15:39:37 PDT
(In reply to Kate Cheney from comment #25)
> (In reply to John Wilander from comment #24)
> > (In reply to John Wilander from comment #23)
> > > Thanks, Kate! Could you add a test that would have timed out without the fix?
> > 
> > Or are you saying existing tests would have timed out without the explicit
> > trigger function? If so, you should be able to say that you updated existing
> > tests, right?
> 
> All of the tests will fail without the fix now that I have removed the
> immediate timer fire. I didn't update any existing tests, I removed some of
> the test infrastructure that hid this bug by launching the timer immediately
> if running a test.

well, probably not all tests, but all conversion tests that wait for a report.
Comment 27 Kate Cheney 2021-11-01 16:20:57 PDT
Created attachment 443031 [details]
Patch
Comment 28 John Wilander 2021-11-01 17:15:34 PDT
Comment on attachment 443031 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=443031&action=review

r=me with comments.

> Source/WebCore/ChangeLog:10
> +        passing of existing conversion tests will confirm this behavior.

Please add a note that several existing tests would timeout with the removal of m_firePendingAttributionRequestsTimer.startOneShot(m_isRunningTest ? 0_s : seconds) if your fix wasn't in place. That makes it clear that you have tests.

> Source/WebCore/loader/PrivateClickMeasurement.cpp:150
> +static Seconds randomlyBetweenTwentyFourAndFortyEightHours(bool isRunningTest)

Please use a boolean enum IsRunningTest to make it clear at the call sites.

> Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.h:48
> +    std::pair<std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>, DebugInfo> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, const ApplicationBundleIdentifier&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, bool isRunningTest);

Ditto on boolean enum IsRunningTest.

> Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:85
> +    Seconds interval() const;

Please give this a more expressive name like randomlyBetweenFifteenAndThirtyMinutes().
Comment 29 John Wilander 2021-11-01 17:58:34 PDT
TestWebKitAPI.PrivateClickMeasurement.EarliestTimeToSendAttributionMinimumDelay seems to be failing.
Comment 30 Kate Cheney 2021-11-02 10:53:25 PDT
Created attachment 443108 [details]
Patch for landing
Comment 31 Kate Cheney 2021-11-02 10:54:29 PDT
(In reply to John Wilander from comment #28)
> Comment on attachment 443031 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=443031&action=review
> 
> r=me with comments.
> 

Thanks! Addressed all comments, and fixed the failing test.

> > Source/WebCore/ChangeLog:10
> > +        passing of existing conversion tests will confirm this behavior.
> 
> Please add a note that several existing tests would timeout with the removal
> of m_firePendingAttributionRequestsTimer.startOneShot(m_isRunningTest ? 0_s
> : seconds) if your fix wasn't in place. That makes it clear that you have
> tests.
> 
> > Source/WebCore/loader/PrivateClickMeasurement.cpp:150
> > +static Seconds randomlyBetweenTwentyFourAndFortyEightHours(bool isRunningTest)
> 
> Please use a boolean enum IsRunningTest to make it clear at the call sites.
> 
> > Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementDatabase.h:48
> > +    std::pair<std::optional<WebCore::PrivateClickMeasurement::AttributionSecondsUntilSendData>, DebugInfo> attributePrivateClickMeasurement(const WebCore::PrivateClickMeasurement::SourceSite&, const WebCore::PrivateClickMeasurement::AttributionDestinationSite&, const ApplicationBundleIdentifier&, WebCore::PrivateClickMeasurement::AttributionTriggerData&&, bool isRunningTest);
> 
> Ditto on boolean enum IsRunningTest.
> 
> > Source/WebKit/NetworkProcess/PrivateClickMeasurement/PrivateClickMeasurementManager.h:85
> > +    Seconds interval() const;
> 
> Please give this a more expressive name like
> randomlyBetweenFifteenAndThirtyMinutes().
Comment 32 EWS 2021-11-02 11:36:40 PDT
Committed r285170 (243806@main): <https://commits.webkit.org/243806@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 443108 [details].