chromium/docs/privacy_budget/good_identifiable_surface.md

# What's a Good Candidate For an IdentifiableSurface? {#good-surface}

Once you have a source of potentially identifying information picked out, you
need to determine how to represent the surface using
[`blink::IdentifiableSurface`].

The first step would be to determine what the surface *is* in the first place.

_If the surface were to be presented as a question that the document asks
a user-agent, what details should the question include in order for the answer
to be identifiable across a wide range of user-agents?_

Sometimes the question is straightforward. E.g. [`window.screenTop`] pretty much
captures the entire question. But it can get tricky as we'll see in the
examples below.

All the pieces of information that the document needs to present to the
user-agent in order to ask the identifiable question should be represented in
the [`blink::IdentifiableSurface`].

There are two broad categories of identifiable surfaces:

*  **Direct Surfaces**: Surfaces accessible via looking up an attribute or
   invoking a parameter-less operation of a global singleton object.

   *Global singleton objects* refer to an object which is effectively the only
   instance of its interface in a single execution context. If one were to
   start from the global object and follow a chain of attributes or
   parameter-less methods, all objects encountered along the way are global
   singleton objects. One could pluck the attribute or operation out of the
   last interface and stick it in the global object and there would be no
   semantic difference.

   In our `window.screenTop` example the global object exposes the `Window`
   interface. `Window.window` or just `window` is a reference back to this
   global object. The `Window` interface exposes the `screenTop` attribute. So
   `window.screenTop` is an expression that looks up an attribute of the global
   object.

   By convention direct surfaces are represented using their corresponding
   [`blink::WebFeature`]. All APIs that are direct identifiable surfaces should
   have corresponding [Use Counters] and hence corresponding `WebFeature`
   values.

   For `window.screenTop`, the resulting `IdentifiableSurface` constructor
   would look like this:

   ```cpp
   IdentifiableSurface::FromTypeAndToken(
       Type::kWebFeature,                  // All direct surfaces use this type.
       WebFeature::WindowScreenTop)
   ```

   See [Instrumenting Direct Surfaces] for details on how to instrument
   these surfaces.

2. **Indirect Surfaces**. A.k.a. everything else.

   [`HTMLMediaElement.canPlayType()`] takes a string indicating a MIME type and
   returns a vague indication of whether that media type is supported (its
   return values are one of `"probably"`, `"maybe"`, or `""`). So the question
   necessarily must include the MIME type.

   Hence the `IdentifiableSurface` constructor could look like this:

   ```cpp
   IdentifiableSurface::FromTypeAndToken(
       Type::kHTMLMediaElement_CanPlayType,
       IdentifiabilityBenignStringToken(mime_type))
   ```

   The [`blink::IdentifiableSurface`] includes:
   * That it represents an `HTMLMediaElement.canPlayType()` invocation.
     `Type::kHTMLMediaElement_CanPlayType` is a bespoke enum value that was
     introduced for this specific surface. See [Adding a Surface Type]
     for details on how to add a surface type.
   * The parameter that was passed to the operation.

   `IdentifiabilityBenignStringToken` is a helper function that calculates
   a digest of a "benign" string. See [Instrumentation] for more details on how
   to represent operation arguments.

The distinction between a direct surface and an indirect surface can sometimes
be fuzzy. But it's always based on what's known _a priori_ and what's practical
to measure. A `canPlayType("audio/ogg; codecs=vorbis")` query could just as
easily be represented as a `WebFeature` like
`MediaElementCanPlayType_Audio_Ogg_Codecs_Vorbis`. But

* [This doesn't scale].
* The set of MIME types can be pretty large and changing.
* It's not possible to hardcode all possible values at coding time.
* Most of the values will be irrelevant to identifiability, but we don't know which ones.

All things considered, deriving a digest for the argument is much more
practical than alternatives.

### Example: NetworkInformation.saveData {#eg-net-effective-type}

The following expression yields whether the user-agent is operating under
a reduce data usage constraint (See [`NetworkInformation.saveData`]):

```js
navigator.connection.saveData
```

This is a [direct surface]. As such constructing a `IdentifableSurface` only
requires knowing the interface and name of the final attribute or operation in
the expression.

Hence the `IdentifiableSurface` is of type `kWebFeature` with a web feature
named `NetInfoEffectiveType` (which was a pre-existing [Use Counter][Use
counters]). I.e.:

```cpp
IdentifiableSurface::FromTypeAndToken(
    Type::kWebFeature,
    WebFeature::NetInfoEffectiveType)
```

### Example: Media Capabilities {#eg-media-capabilities}

The [Media Capabilities API] helps determine whether some media type is
supported. E.g.:

```js
await navigator.mediaCapabilities.decodingInfo({
  type: "file",
  audio: { contentType: "audio/mp3" }
});
```

In this case the script is specifying a [`MediaDecodingConfiguration`]
dictionary. The [`MediaCapabilitiesInfo`] object returned by [`decodingInfo()`]
depends on the input. Hence we have to capture the input in the
`IdentifiableSurface` as follows:

```cpp
IdentifiableSurface::FromTypeAndToken(
    Type::kHTMLMediaElement_CanPlayType,
    IdentifiabilityBenignStringToken(mime_type))
```

See [Instrumentation] for more details on how to represent operation arguments
and caveats around encoding strings.

### Example: Media Streams API {#eg-media-streams}

Another more complicated example is this use of the [Media Streams API].

```js
var mediaStream = await navigator.mediaDevices.getUserMedia({video: {
  height: 240,
  width: 320
}});

var firstAudioTrack = mediaStream.getAudioTracks()[0];

var capabilities = firstAudioTrack.getCapabilities();
```

The target identifiable surface is the value of `capabilities`.

An important consideration here is that [`MediaDevices.getUserMedia`] operation
involves user interaction.

In theory, if the `getUserMedia` operation is successful, the
`IdentifiableSurface` for `capabilities` should represent the artifacts
(starting with the last global singleton object):

1. The operation [`MediaDevices.getUserMedia`].

1. A [`MediaStreamConstraints`] dictionary with value
   `{video: {height: 240, width: 320}}`.

1. The user action.

1. The operation [`MediaStream.getAudioTracks`] -- which is invoked on the
   result of the prior step assuming the operation succeeded.

1. `[0]`^th^ index -- applied to the list of [`MediaStreamTrack`]s resulting from
   the previous step

   > The Media Streams API does not specify the order of tracks. In general
   > where a spec doesn't state the ordering of a sequence, the ordering itself
   > can be a tracking concern. However in this case the implementation yields
   > at most one audio track after a successful `getUserMedia` invocation.
   > Hence there's no ordering concern here at least in Chromium.

1. The operation [`MediaStreamTrack.getCapabilities`] -- which is invoked on
   the result of the prior step.

However,

* The user action is not observable by the document. The only outcome exposed
  to the document is whether `getUserMedia()` returned a `MediaStream` or if
  the request was rejected due to some reason.

  It's not necessary to go beyond what the document can observe.

* If the call is successful the initial state of the resulting `MediaStream`
  determines the stable properties that a document can observe.

  The remaining accessors (e.g. `getAudioTracks()`, `getVideoTracks()` etc...)
  deterministically depend on the returned `MediaStream` with the exception of
  the indexing in step 5 which can be non-deterministic if there is more than
  one audio track.

  The diversity of document exposed state past step 3 is a subset of the
  diversity of the initial `MediaStream` object.

* If the call is rejected due to the request being over-constrained, then the
  exception could indicate limitations of the underlying devices.

Considering the above, we can tease apart multiple identifiable surfaces:

1. **VALID** The mapping from <`"MediaDevices.getUserMedia"` operation,
   `MediaStreamConstraints` instance> to <`Exception` instance> when
   the call rejects prior to any user interaction.

1. **OUT OF SCOPE** The mapping from <`"MediaDevices.getUserMedia"`
   operation, `MediaStreamConstraints` instance> to <time elapsed> when
   the call resolves.

   Timing vectors like this are outside the scope of the initial study.

1. **INFEASIBLE** The mapping from <`"MediaDevices.getUserMedia"` operation,
   <`MediaStreamConstraints` instance, (user action)> to
   <`MediaStream` instance>.

   As mentioned earlier the user action is not exposed to the document. Hence
   we end up with an incomplete metric where the key doesn't have sufficient
   diversity to account for the outcomes.

1. **INFEASIBLE** The mapping from <`"MediaDevices.getUserMedia"` operation,
   `MediaStreamConstraints` instance, (user action)> to <`Exception`
   instance>.

   Same problem as above.

1. **VALID** The mapping from <`MediaStreamTrack` instance> to
   <`MediaTrackCapabilities` instance>.

   We can reason that this mapping is going to be surjective. The diversity of
   <`MediaTrackCapabilities` instance> is not going to add information.

   For simplicity surjective mappings can be collapsed into a single point
   without losing information. Thus the mapping here is just <`MediaStream`
   instance> to <`1`> where the value is arbitrary and doesn't matter.

1. **VALID** The mapping from <`MediaStreamTrack.label` string> to
   <`MediaTrackCapabilities` instance>.

   The label is a string like "Internal microphone" which can be presented to
   the user and assumed to be discerning enough that the user will find the
   string sufficient to identify the correct device.

The metrics we can derive from this surface are marked as **VALID**.

Constructing a digest out of any of the dictionary instances also requires some
care. Only include properties of each object that are expected to persist
across browsing contexts. For example, any identifier that is origin-local,
document-local, or depends on input from the document is not a good candidate.

<!-- sort, case insensitive -->
[`blink::IdentifiableSurface`]: ../../third_party/blink/public/common/privacy_budget/identifiable_surface.h
[`blink::WebFeature`]: ../../third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
[`decodingInfo()`]: https://www.w3.org/TR/media-capabilities/#dom-mediacapabilities-decodinginfo
[`HTMLMediaElement.canPlayType()`]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType
[`MediaCapabilitiesInfo`]: https://www.w3.org/TR/media-capabilities/#dictdef-mediacapabilitiesinfo
[`MediaDecodingConfiguration`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaDecodingConfiguration
[`MediaDevices.getUserMedia`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
[`MediaStream.getAudioTracks`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/getAudioTracks
[`MediaStream`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaStream
[`MediaStreamConstraints`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
[`MediaStreamTrack.getCapabilities`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/getCapabilities
[`MediaStreamTrack`]: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack
[`NetworkInformation.saveData`]: https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData
[`window.screenTop`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/screenTop
[Adding a Surface Type]: privacy_budget_instrumentation.md#adding-a-surface-type
[direct surface]: privacy_budget_glossary.md#directsurface
[Instrumentation]: privacy_budget_instrumentation.md
[Instrumenting Direct Surfaces]: privacy_budget_instrumentation.md#annotating-direct-surfaces
[Media Capabilities API]: https://developer.mozilla.org/en-US/docs/Web/API/Media_Capabilities_API
[Media Streams API]: https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API
[this doesn't scale]: https://thecooperreview.com/10-tricks-appear-smart-meetings/
[Use Counters]: ../use_counter_wiki.md