Introducing Private Click Measurement, PCM (original) (raw)

This blog post covers a new feature called Private Click Measurement, or PCM, for measuring ad clicks across websites and from iOS apps to websites. It is part of iOS and iPadOS 14.5 betas.

Motivation and Goals

Classic ad attribution on the web is done with cookies carrying user or device IDs. Such attribution constitutes cross-site tracking which WebKit is committed to preventing. Websites should not be able to attribute data of an ad click and a conversion to a single user as part of large scale tracking.

At the same time, we want to support measurement of online advertising. PCM achieves this tradeoff by sending attribution reports with limited data in a dedicated Private Browsing mode without any cookies, delaying reports randomly between 24 and 48 hours to disassociate events in time, and handling data on-device.

The Feature in a Nutshell

A Proposed Standard

We first proposed privacy-preserving measurement of ad clicks in May 2019. Since then the proposal has changed name to Private Click Measurement and been discussed extensively in the W3C Privacy Community group, both through meetings and on GitHub.

A proposal needs two independent implementations to be on track to become a web standard. This means another browser such as Firefox, Brave, Chrome, or Edge needs to independently implement PCM before it can move further along the standards track. We are working with them to get there.

Nevertheless, we are happy to be the first browser to enable a proposed web standard for measuring advertising!

On By Default

You may ask why we are enabling PCM by default before there is a second independent implementation and before we’ve added the fraud prevention mechanism discussed in W3C Privacy CG. The reasons are:

Web-to-Web Click Measurement

PCM web-to-web is the case covered by the proposed standard, i.e. a user clicks a link on a webpage, is navigated cross-site, and up to seven days later, there’s a signal on the destination website saying it would like attribution for any previous clicks that took the user here.

For the purposes of the examples below, we assume the click happens on a website called social.example and the click navigates the user to shop.example.

The Click Side

Links that want to store click measurement data should look like this:

<!-- Link on social.example --> 
<a href="https://shop.example/product.html" 
   attributionsourceid="[8-bit source ID]"
   attributeon="https://shop.example">

  Markup

</a>

The two mandatory attributes are:

If the click indeed navigated the user to the attributeon website, the attributionsourceid is stored as a click from social.example to shop.example for 7 days.

Note that this data is not accessible to websites. It’s silently stored in the browser.

The Triggering Event

To trigger click attribution, the “attribute on” website has to make an HTTP GET request to the website(s) where it is running click-through ads. This way of doing it is intended to support existing “tracking pixels” and make adoption easy. In our example this would be the shop.example site making an HTTP GET request to social.example. For a more modern way of triggering attribution, see the Future Enhancements section.

The HTTP GET request to social.example triggers attribution if it is redirected to https://social.example/.well-known/private-click-measurement/trigger-attribution/[``4-bit`` trigger data]/[optional 6-bit priority]. Note: The first beta lacks the /trigger-attribution path component since this was a very recent decision in the standards group.

The two URL path parameters are:

Once a triggering event matches a stored click, a single attribution report is scheduled by the browser to be sent out randomly between 24 and 48 hours later, or the earliest time thereafter when the browser is running. As long as an attribution report has not yet been sent, it can be rescheduled based on a triggering event with higher priority.

The Attribution Report

PCM attribution reports are sent as HTTP POST requests to /.well-known/private-click-measurement/report-attribution/ on the website where the click happened, in our example https://social.example/.well-known/private-click-measurement/report-attribution/. Note: The first beta lacks the /report-attribution path component since this was a very recent decision in the standards group.

The report is in JSON and looks like this:

{
  "source_engagement_type" : "click",
  "source_site" : "social.example",
  "source_id" : [8-bit source ID],
  "attributed_on_site" : "shop.example",
  "trigger_data" : [4-bit trigger data],
  "version": 1
}

Notes on the non-obvious key-values above:

App-to-Web Click Measurement

This is exciting – we’re adding the capability to measure ad clicks from iOS and iPadOS apps to Safari!

Many advertisers in apps want to take the user to their website where the user can buy a product or sign up for a service. This is exactly the kind of ad PCM app-to-web allows them to measure.

The Click Side

The only thing that differs from PCM web-to-web is on the click side which is in an iOS app. To adopt this technology you need to do this:

  1. Add a URL to where you want PCM’s ad attribution reports to be sent when ads are clicked in your app. You do this under the key NSAdvertisingAttributionReportEndpoint in your Info.plist. The naming of this endpoint is deliberately not tied to PCM. Potential future ad measurement reports associated with apps may use this URL with a differing well-known location if appropriate. Note that the subsequent HTTP redirect to trigger attribution needs to go to this website.
  2. Populate and add the new UIEventAttribution to the options of your call to openURL:. See below for what fields you need to enter in UIEventAttribution.
  3. Overlay the parts of the click-through ad that will trigger navigations to websites with the new UIEventAttributionView. This view only serves as a checkpoint for Apple’s code on-device to check that a user gesture happened before the navigation. The view does not consume the gesture and you are free to decide whether or not to navigate to a website even if the gesture happened on one of these views. A user gesture is required for your UIEventAttribution object to be forwarded to the browser as part of the call to openURL:. Note that PCM app-to-web is so far only supported in Safari and only on iOS and iPadOS. We intend to add WebKit API to enable other default browsers to be the destination of PCM app-to-web too.

UIEventAttribution

This is the optional data structure you submit in your call to openURL: when you want to measure clicks:

open class UIEventAttribution : NSObject, NSCopying {
    open var sourceIdentifier: UInt8 { get }
    open var destinationURL: URL { get }
    open var reportEndpoint: String? { get }
    open var sourceDescription: String { get }
    open var purchaser: String { get }
    public init(sourceIdentifier: UInt8,
                destinationURL: URL,
                sourceDescription: String,
                purchaser: String)
}

UIEventAttribution Sample Code

func openAdLink() {
    let adURL = URL(string: "https://shop.example/tabletStandDeluxe.html")!
    let eventAttribution =
        UIEventAttribution(sourceIdentifier: 4,
                           destinationURL: adURL,
                           sourceDescription: "Banner ad for Tablet Stand Deluxe.",
                           purchaser: "Shop Example, Inc.")

    // If using scene lifecycle.
    let sceneOpenURLOptions = UIScene.OpenExternalURLOptions()
    sceneOpenURLOptions.eventAttribution = eventAttribution
    self.view.window?.windowScene?.open(adURL,
                                        options: sceneOpenURLOptions,
                                        completionHandler: nil)

    // If using application lifecycle.
    let appOpenURLOptions: [UIApplication.OpenExternalURLOptionsKey : Any] = [
        .eventAttribution: eventAttribution
    ]
    UIApplication.shared.open(adURL,
                              options: appOpenURLOptions,
                              completionHandler: nil)
}

UIEventAttributionView

UIEventAttributionView is the view that is placed over the tappable content, typically an ad. It’s used by the system to verify that a user gesture has occurred.

open class UIEventAttributionView : UIView {
}

The view is invisible and very lightweight. The simplest use case is to create one of these views and stretch it over your entire tappable content. You can also place multiple over a single piece of content if you for instance want to create specific tappable areas.

To ensure your UIEventAttributionView works correctly:

UIEventAttributionView Sample Code

func addEventAttributionView() {
    // Create an event attribution view.
    let eventAttributionView = UIEventAttributionView()

    // Place it over your ad however you'd like.
    eventAttributionView.translatesAutoresizingMaskIntoConstraints = false
    adView.addSubview(eventAttributionView)
    NSLayoutConstraint.activate([
        adView.topAnchor.constraint(equalTo: eventAttributionView.topAnchor),
        adView.leadingAnchor.constraint(equalTo: eventAttributionView.leadingAnchor),
        adView.trailingAnchor.constraint(equalTo: eventAttributionView.trailingAnchor),
        adView.bottomAnchor.constraint(equalTo: eventAttributionView.bottomAnchor)
    ])
}

Testing and Debugging

WebKit has an experimental feature called Private Click Measurement Debug Mode. You’ll find it under Develop–>Experimental Features on macOS and under Settings–>Safari–>Advanced–>Experimental Features on iOS and iPadOS. When you enable this mode and restart Safari, reports go out a mere 10 seconds after the triggering event instead of 24-48 hours later. This allows quick turnaround in testing and debugging.

The debug mode also enables debug output in Web Inspector’s console. This output will show up by default in a later beta.

Remember to disable debug mode once you’re done testing.

Future Enhancements

As is always the case with web standards, proposed or established, there are enhancement requests, corner cases, and a need to evolve the specification as the platform progresses. Below is a list of prominent and relevant issues that may show up as changes to our implementation of PCM in upcoming releases. Please take part on GitHub if you have input.

Misuse or Use Together With Tracking May Lead To Blocking

PCM is intended to support privacy-preserving measurement of clicks across websites or from apps to websites. It is not intended to be used to track users, events, or devices across those contexts.

If PCM is being misused for tracking purposes or being used in conjunction with unrelated means of tracking users, events, or devices, we may block the offending party from using PCM and potential future measurement features.

FAQ

Thank You

We’d like to thank the W3C Privacy Community Group for all the work filing issues, suggesting changes, and engaging with us on this work. Please continue to do so as we move forward. Also, a big thank you to the engineers who’ve helped implement this feature – Anant, Kate, Jon, Chris, Jonathan, Chris, and Glen.