Element Timing API

Draft Community Group Report,

This version:
https://wicg.github.io/element-timing
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/element-timing
Issue Tracking:
GitHub
Editors:
(Google)
(Google)
Former Editor:
(Google)

Abstract

This document defines an API that enables monitoring when large or developer-specified image elements and text nodes are displayed on screen.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

Knowing when critical elements are displayed on screen is key to understanding page load performance. While fast rendering of the essential components is not sufficient for a satisfactory loading experience, it is necessary. Therefore, monitoring these rendering timestamps is important to improve and prevent regressions in page loads.

This specification gives developers and analytics providers an API to measure rendering timestamps of critical elements. There is currently no good way to measure these timestamps for real users. Existing approaches would require either registering observers too early or significant DOM manipulation. These approaches are discussed on the § 4 Security & privacy considerations section.

Web developers are the experts in critical user interactions for their sites, so they should be allowed to tell the user agent which are the elements they care about. Thus, this API exposes rendering timing information about web-developer-annotated elements.

1.1. Elements exposed

The Element Timing API supports timing information about timing-eligible elements, as defined by [PAINT-TIMING].

Elements that have a "elementtiming" content attribute are reported in the report image element timing and the report text element timing algorithms.

1.2. Usage example

The following example shows an image that is registered for observation via its elementtiming attribute, and an observer gathering the timing information.

<img... elementtiming='foobar'/>
<p elementtiming='important-paragraph'>This is text I care about.</p>
...
<script>
const observer = new PerformanceObserver((list) => {
  let perfEntries = list.getEntries();
  // Process the entries by iterating over them.
});
observer.observe({type: 'element', buffered: true});
</script>

The following are sample elements whose rendering timestamps could be measured by using this API and which should be compared to page navigation:

The API could have use cases outside of page load by comparing the rendering timestamps with input timestamps. For example, developers could monitor the time it takes for a widget to show up after a click that triggers it.

2. Element Timing

Element Timing involves the following new interfaces:

2.1. PerformanceElementTiming interface

[Exposed=Window]
interface PerformanceElementTiming : PerformanceEntry {
    readonly attribute DOMHighResTimeStamp renderTime;
    readonly attribute DOMHighResTimeStamp loadTime;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute DOMString identifier;
    readonly attribute unsigned long naturalWidth;
    readonly attribute unsigned long naturalHeight;
    readonly attribute DOMString id;
    readonly attribute Element? element;
    readonly attribute DOMString url;
    [Default] object toJSON();
};

A PerformanceElementTiming object reports timing information about one associated element.

Each PerformanceElementTiming object has these associated concepts, all of which are initially set to null:

The associated concepts and some attributes for PerformanceElementTiming are specified in the processing model in § 3.3 Report Image Element Timing and § 3.4 Report Text Element Timing.

The entryType attribute’s getter must return the DOMString "element".

The name attribute’s getter must return the value it was initialized to.

The startTime attribute’s getter must return the value of this’s renderTime if it is not 0, and the value of this’s loadTime otherwise.

The duration attribute’s getter must return 0.

The renderTime attribute must return the value it was initialized to.

The loadTime attribute’s getter must return the the value it was initialized to.

The intersectionRect attribute must return the value it was initialized to.

The identifier attribute’s getter must return the value it was initialized to.

The naturalWidth attribute must return the value it was initialized to.

The naturalHeight attribute must return the value it was initialized to.

The id attribute’s getter must return the value it was initialized to.

The element attribute’s getter must perform the following steps:

  1. If this’s element is not exposed for paint timing given null, return null.

  2. Return this’s element.

Note: This means that an element that is no longer descendant of the Document will no longer be returned by element's attribute getter.

The url attribute’s getter must perform the following steps:

  1. If this’s request is null, return the empty string.

  2. Let urlString be this’s request’s current URL.

  3. Let url be the result of parsing urlString.

  4. If url’s scheme is "`data`", trim urlString to its first 100 characters.

  5. Return urlString.

Note: The URL is trimmed for data URLs to avoid excessive memory in the entry.

3. Processing model

Note: A user agent implementing the Element Timing API would need to include "element" in supportedEntryTypes for Window contexts. This allows developers to detect support for element timing.

3.1. Modifications to the DOM specification

This section will be removed once the [DOM] specification has been modified.

We extend the Element interface as follows:

partial interface Element {
    [CEReactions] attribute DOMString elementTiming;
};

The elementTiming attribute must reflect the element’s "elementtiming" content attribute.

3.2. Report Element Timing

When asked to report element timing given a Document doc, a timestamp now, an ordered set of pending image records paintedImages, and an ordered set of elements paintedTextNodes, perform the following steps:
  1. For each record in paintedImages:

    1. Run the report image element timing algorithm passing in record, now, and doc.

  2. For each Element element in paintedTextNodes:

    1. Run the report text element timing given element, now, and doc.

3.3. Report Image Element Timing

When asked to report image element timing given a pending image record record, a DOMHighResTimestamp renderTime and a Document document, perform the following steps:
  1. If record’s element's "elementtiming" content attribute is absent, then abort these steps.

  2. Let intersectionRect be the value returned by the intersection rect algorithm using record’s element as the target and viewport as the root.

  3. Create and initialize a PerformanceElementTiming object entry with document’s relevant realm.

    1. Initialize entry’s request to record’s request.

    2. Initialize entry’s element to record’s element.

    3. Initialize entry’s name to the DOMString "image-paint".

    4. Initialize entry’s renderTime to renderTime.

    5. Initialize entry’s loadTime to record’s loadTime.

    6. Initialize entry’s intersectionRect to intersectionRect.

    7. Initialize entry’s identifier to record’s element's "elementtiming" content attribute.

    8. Initialize entry’s naturalWidth and naturalHeight by running the same steps for an img's naturalWidth and naturalHeight attribute getters, but using record’s request as the image.

    9. Initialize entry’s id to record’s element's "id" content attribute.

  4. Queue the PerformanceEntry entry.

3.4. Report Text Element Timing

When asked to report text element timing given an Element element, a DOMHighResTimestamp renderTime and a Document document, perform the following steps:
  1. If element’s "elementtiming" content attribute is absent, then abort these steps.

  2. Let intersectionRect be an empty rectangle.

  3. For each Text node text in element’s set of owned text nodes:

    1. Augment intersectionRect to be smallest rectangle containing the border box of text and intersectionRect.

  4. Intersect intersectionRect with the visual viewport.

  5. Create and initialize a PerformanceElementTiming object entry with document’s relevant realm.

    1. Initialize entry’s element to element.

    2. Initialize entry’s name to the DOMString "text-paint".

    3. Initialize entry’s renderTime to renderTime.

    4. Initialize entry’s loadTime to 0.

    5. Initialize entry’s intersectionRect to intersectionRect.

    6. Initialize entry’s identifier to element’s "elementtiming" content attribute.

    7. Initialize entry’s naturalWidth and naturalHeight to 0.

    8. Initialize entry’s id to element’s "id" content attribute.

  6. Queue the PerformanceEntry entry.

4. Security & privacy considerations

This API exposes some information about cross-origin images. In particular, images that do not pass the timing allow check still have their resource load time exposed, which could be a source of privacy concerns.

However, this is considered to not add new attacks to the web platform because the ResourceTiming API exposes a similar timestamp already. In addition, the onload handler exposes load timing when it is available, and the resource load time is a close proxy to this. The current high resolution time computed at the beginning of the onload handler would provide the image load time. We choose to expose the loadTime because it is very easy to obtain even without an onload handler. In addition, we believe any fix to remove the leak provided by image onload handlers or ResourceTiming could also fix the leak provided by this API.

The renderTime (display timestamp) can also be polyfilled via the PaintTiming API. To do this, add an iframe that contains trivial content on the onload handler of the target image or text content. Then, query the first paint of that iframe to obtain the rendering timestamp of the content. This is quite inefficient and the polyfill itself might affect the timing obtained. Due to the difficulty in obtaining this information today, we choose not to expose the display timestamp for images that fail the timing allow check. For clarity, here is a code snippet using the PaintTiming API:

// In the attacker frame.
<iframe src=attack.html></iframe>
<script>
    window.onmessage = e => {
        let timestamp = e.data;
        // Acquired the display timestamp for 'victim.jpg'!
    }
</script>

// In the attack.html iframe.
<img src='victim.jpg'/>
<script>
    // Wait until onload or some time when the PaintTiming entries will be visible.
    onload() => {
        let entry = performance.getEntriesByType('paint')[0];
        top.postMessage(entry.startTime, '*');
    }
</script>

The other nontrivial parameter being exposed here is the intersectionRect. This can already be polyfilled, for example using IntersectionObserver. The polyfill process would be similar: add an IntersectionObserver on the onload handler of the target image or text content. This solution is inefficient because it requires registering the observer once the content has loaded, but it should still provide the same level of accuracy. For images, we compute the intersectionRect once the image has loaded if it does not pass the timing allow check. Computing it at this point in time allows exposing the entry at that time. If we were to compute the rect only until the image is fully displayed, we’d only be able to expose the entry after that time.

If we do not want to expose the rendering timetamp of an image, it’s preferable to dispatch the entry to the PerformanceObserver right away. Suppose we waited and exposed all the entries during the report element timing algorithm. An attacker could infer nontrivial information about the rendering timestamp of an image. It would do so by only observing the timing for that image. Even though the timestamp is not exposed as a member of the PerformanceElementTiming entry received, the fact that we wait until the next update the rendering step means that the attacker can distinguish between a very slow rendering time and a very fast rendering time by measuring the time at which it received the entry. This would unintentionally leak some of the display timing of the image.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. URL: https://drafts.fxtf.org/geometry/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[INTERSECTION-OBSERVER]
Stefan Zager; Emilio Cobos Álvarez; Traian Captan. Intersection Observer. URL: https://w3c.github.io/IntersectionObserver/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing. URL: https://w3c.github.io/paint-timing/
[PERFORMANCE-TIMELINE]
Nicolas Pena Moreno. Performance Timeline. URL: https://w3c.github.io/performance-timeline/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[Exposed=Window]
interface PerformanceElementTiming : PerformanceEntry {
    readonly attribute DOMHighResTimeStamp renderTime;
    readonly attribute DOMHighResTimeStamp loadTime;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute DOMString identifier;
    readonly attribute unsigned long naturalWidth;
    readonly attribute unsigned long naturalHeight;
    readonly attribute DOMString id;
    readonly attribute Element? element;
    readonly attribute DOMString url;
    [Default] object toJSON();
};

partial interface Element {
    [CEReactions] attribute DOMString elementTiming;
};

MDN

Element/elementTiming

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/element

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/id

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/identifier

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/intersectionRect

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/loadTime

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/naturalHeight

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/naturalWidth

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/renderTime

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/toJSON

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming/url

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

PerformanceElementTiming

In only one current engine.

FirefoxNoneSafariNoneChrome77+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?