Container Timing API

Draft Community Group Report,

This version:
https://WICG.github.io/container-timing/
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/container-timing
Issue Tracking:
GitHub
Editors:
Jason Williams (Bloomberg)
(Igalia)

Abstract

This specification defines an API that enables monitoring when annotated sections of the DOM are displayed on screen and have finished their initial paint.

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

The Container Timing API enables monitoring when annotated sections of the DOM are displayed on screen and have finished their initial paint. A developer can mark subsections of the DOM with the containertiming attribute (similar to elementtiming for the Element Timing API) and receive performance entries when that section has been painted for the first time.

This API allows developers to measure the timing of various components in their pages. As developers increasingly organize their applications into components, there’s a growing demand to measure performance on subsections of an application or a web page.

Unlike Element Timing, it is not possible for the renderer to know when a section of the DOM has finished painting (there could be future changes, asynchronous requests for new images, slow loading buttons, etc.), so this API emits candidates in the form of PerformanceEntry objects when there has been an update.

2. Motivation

Developers want to measure when subsections of the DOM have been painted, such as tables, widgets, or other components, so they can track paint times and submit them to analytics. Current Web APIs don’t adequately support this:

Web authors know their domain better than anyone else and want to communicate the performance of their own content blocks in ways their users or organization would understand (e.g., "time to first tweet").

2.1. Life Cycle

In this example life cycle a component will paint multiple pieces of content at different times, each of these times will generate a new PerformanceContainerTiming entry with updated information.

However, once a region has been painted, subsequent paints of that same region will not generate new entries. Container Timing Life Cycle Diagram

3. Usage Example

The following example demonstrates how to register a container root and observe its paint timings.

Registration is on a per-element basis using the containertiming attribute:
<div containertiming="foobar">
  <main>...</main>
  <aside>...</aside>
</div>

<script>
  const observer = new PerformanceObserver((list) => {
    let perfEntries = list.getEntries();
    for (const entry of perfEntries) {
      console.log('Container painted:', entry.identifier,
                  'at', entry.startTime,
                  'size:', entry.size);
    }
  });
  observer.observe({ entryTypes: ["container"] });
</script>

The attribute should be set before the element is added to the document (in HTML, or if set in JavaScript, before adding it to the document). Setting the attribute retroactively will only capture subsequent events and future paints.

3.1. Ignoring Subtrees

Parts of the DOM tree can be ignored using the <containertiming-ignore> attribute:
<div containertiming="foobar">
  <main>...</main>
  <!-- Aside updates won't trigger container timing events -->
  <aside containertiming-ignore>...</aside>
</div>

4. Terminology

A container root is an Element that has the containertiming attribute.

An ignored subtree is a subtree rooted at an Element with the containertiming-ignore> attribute.

A painted region is a region (collection of rectangles) representing all the painted portions of a container root accumulated since it was first observed.

The container timing API provides timing information about when container roots are painted to the screen.

5. The PerformanceContainerTiming Interface

[Exposed=Window]
interface PerformanceContainerTiming : PerformanceEntry {
    readonly attribute DOMString identifier;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute unsigned long long size;
    readonly attribute DOMHighResTimeStamp firstRenderTime;
    readonly attribute Element? lastPaintedElement;
    readonly attribute Element? rootElement;
};

PerformanceContainerTiming includes PaintTimingMixin;
Each PerformanceContainerTiming object has these associated concepts:

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

The name attribute’s getter must return the empty string.

The duration attribute must return 0.

The startTime attribute’s getter must return the value of this’s renderTime.

The identifier attribute must return the value of this’s identifier.

The intersectionRect attribute must return the value of this’s intersectionRect.

The size attribute must return the value of this’s size.

The firstRenderTime attribute must return the value of this’s firstRenderTime.

The lastPaintedElement attribute must return the value of this’s lastPaintedElement.

The rootElement attribute must return the value of this’s rootElement.

Note: The user agent needs to maintain the container root records map so that removed content does not introduce memory leaks. In particular, it can tie the lifetime of the entries to weak pointers to the Elements so that they can be cleaned up sometime after the Elements are removed. Since the map is not exposed to web developers, this does not expose garbage collection timing.

6. Processing Model

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

6.1. Per-Document State

For each Document, the user agent must maintain a container root records map that maps container root Elements to Container Timing Record objects.

6.2. Extensions to the Element Interface

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

We extend the Element interface as follows:

partial interface Element {
    [CEReactions] attribute DOMString containertiming;
    [CEReactions] attribute DOMString? containertimingIgnore;
};
The containertiming attribute is a DOMString that identifies the element as a container root. The value becomes the identifier in the corresponding PerformanceContainerTiming entry.

The containertiming-ignore attribute, when present, marks the element and its descendants as an ignored subtree that should not contribute to container timing measurements for ancestor container roots.

6.3. Container Timing Record

This specification defines an internal data structure used by the processing model:

A Container Timing Record has these associated concepts:
To create a Container Timing Record given a paint timing info paintTimingInfo and a DOMString identifier, perform the following steps:
  1. Let record be a new Container Timing Record.

  2. Set record’s paintTimingInfo to paintTimingInfo.

  3. Set record’s identifier to identifier.

  4. Return record.

6.4. Registering Container Roots

When an Element with a [^containertiming^] content attribute is connected to the document:

  1. The user agent must register the element as a container root.

  2. The user agent must initialize an empty painted region for the container root.

  3. The user agent must track all paint operations within the container root’s subtree, excluding any ignored subtrees.

6.5. Handle Element Painted for Container Timing

When an element is painted and the user agent needs to handle container timing updates, given a Document document, a paint timing info paintTimingInfo, an Element element, and a DOMRectReadOnly intersectionRect, perform the following steps:
  1. If element does not Contribute to container timing, return.

  2. Let containerRoot be the result of get the container root element given element.

  3. If containerRoot is null, return.

  4. Let record be the entry in document’s container root records map for containerRoot. If no such entry exists, set record to the result of create a Container Timing Record given paintTimingInfo and the value of containerRoot’s [^containertiming^] content attribute; then add (containerRootrecord) to document’s container root records map.

  5. Let enclosingRect be the smallest enclosing rectangle of intersectionRect.

  6. Maybe update the last new painted area for record given document, containerRoot, element, enclosingRect, and paintTimingInfo.

  7. Mark the Document as having pending container timing changes.

Note: This algorithm is invoked for each image or text node that paints. The intersection rectangle should be computed using the intersection rect algorithm with the element as target and viewport as root, intersected with the visual viewport. For text nodes, the intersection rectangle is the smallest rectangle containing the border boxes of all text nodes in the set of owned text nodes, intersected with the visual viewport.

6.6. Emit Container Timing Entries

When asked to emit container timing entries for a Document document, perform the following steps. This should be called once per frame after all paint operations have been processed:
  1. If document does not have pending container timing changes, return.

  2. For each containerRootrecord of document’s container root records map:

    1. If record’s hasPendingChanges is false, continue.

    2. Create a Container Timing entry given record and containerRoot.

    3. Set record’s hasPendingChanges to false.

  3. Mark the Document as no longer having pending container timing changes.

Note: Unlike some other paint timing APIs that may emit multiple entries per painted element, Container Timing accumulates painted regions for each container root and emits at most one PerformanceContainerTiming entry per container root per frame. This batching approach is more efficient and provides a holistic view of the container’s paint state.

6.7. Getting the parent container root Element

To get the parent container root element given an Element element, perform the following steps:
  1. Let parent be element’s parentElement.

  2. If parent is null, return null.

  3. Return the result of get the container root element given parent.

6.8. Contributes to Container Timing

An Element contributes to the container timing of a container root if all of the following are true:

To determine whether an Element element contributes to the container timing of a container root containerRoot:
  1. If element is null, return false.

  2. If element is in a shadow tree, return false.

  3. If element is not a descendant of containerRoot, return false.

  4. If element is within an ignored subtree, return false.

  5. Return true.

6.9. Getting the container root Element

To get the container root element given an Element element, perform the following steps:
  1. If element is null, return null.

  2. If element’s [^containertiming^] content attribute is present, return element.

  3. If element’s parentElement is not null, return the result of get the container root element given element’s parentElement.

  4. Return null.

6.10. Maybe Update Last New Painted Area

To maybe update the last new painted area for a Container Timing Record record given a Document document, a container root Element containerRoot, an Element element, a DOMRectReadOnly enclosingRect, and a paint timing info paintTimingInfo, perform the following steps:
  1. Let paintedRegion be record’s paintedRegion.

  2. If paintedRegion fully contains enclosingRect, return.

  3. Set record’s paintedRegion to the union of paintedRegion and enclosingRect.

  4. Set record’s lastNewPaintedAreaPaintTimingInfo to paintTimingInfo.

  5. Set record’s lastNewPaintedAreaElement to element.

  6. Set record’s hasPendingChanges to true.

  7. If containerRoot’s [^containertiming-ignore^] content attribute is present, return.

  8. Let parentContainerRoot be the result of get the parent container root element given containerRoot.

  9. If parentContainerRoot is null, return.

  10. Let parentRecord be the entry in document’s container root records map for parentContainerRoot. If no such entry exists, set parentRecord to the result of create a Container Timing Record given paintTimingInfo and the value of parentContainerRoot’s [^containertiming^] content attribute; then add (parentContainerRootparentRecord) to document’s container root records map.

  11. Maybe update the last new painted area for parentRecord given document, parentContainerRoot, element, enclosingRect, and paintTimingInfo.

Note: This algorithm reports any change to the painted region, regardless of size. Even a 1 pixel change in the painted area will result in a new PerformanceContainerTiming entry being queued. Developers who wish to filter out small changes may do so by comparing the size values between entries.

6.11. Create a Container Timing Entry

In order to create a Container Timing entry given a Container Timing Record record and a container root Element containerRoot, the user agent must perform the following steps:
  1. Let entry be a new PerformanceContainerTiming entry with its:

  2. Queue the PerformanceEntry entry.

7. Security and Privacy Considerations

7.1. Cross-Origin Restrictions

The API respects cross-origin boundaries:

7.2. Information Exposure

Most information provided by this API can already be estimated through existing APIs:

The API does not expose:

7.3. Timing Attacks

The API uses DOMHighResTimeStamp which may be subject to resolution limitations for security purposes, consistent with other Performance APIs.

7.4. Privacy Considerations

The API does not:

8. Acknowledgments

Many thanks for valuable feedback and advice from:

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/
[GEOMETRY-1]
Sebastian Zartner; Yehonatan Daniv. Geometry Interfaces Module Level 1. URL: https://drafts.csswg.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/
[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
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[INTERSECTION-OBSERVER]
Stefan Zager; Emilio Cobos Álvarez; Traian Captan. Intersection Observer. URL: https://w3c.github.io/IntersectionObserver/

IDL Index

[Exposed=Window]
interface PerformanceContainerTiming : PerformanceEntry {
    readonly attribute DOMString identifier;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute unsigned long long size;
    readonly attribute DOMHighResTimeStamp firstRenderTime;
    readonly attribute Element? lastPaintedElement;
    readonly attribute Element? rootElement;
};

PerformanceContainerTiming includes PaintTimingMixin;

partial interface Element {
    [CEReactions] attribute DOMString containertiming;
    [CEReactions] attribute DOMString? containertimingIgnore;
};