Skip to content

WICG/container-timing

Repository files navigation

Container Timing: Explainer

Authors

  • Jason Williams (Bloomberg)
  • José Dapena Paz (Igalia)

Participate

Table Of Contents

  1. Authors
  2. Introduction
  3. Motivation
  4. Objectives
  5. Using The API
  6. Algorithm
    1. Life Cycle
  7. Nested Containers Roots
    1. Ignore
    2. Transparent
    3. Shadowed
  8. Non Goals
    1. LCP Integration
    2. Built-in containers
    3. Shadow DOM
  9. Security and Privacy
  10. How do we try this?
    1. Polyfill
    2. Browser Extension
  11. Considered alternatives
  12. Questions
  13. W3C Specification Meetings
  14. Standards Positions
  15. Browser extension
  16. Glossary
  17. Links

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 will have the ability to 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 will allow developers to measure the timing of various components in their pages.

Unlike with 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.). This API will produce PerformanceEntry objects when there has been an update to the containertiming region. Updates are defined as paints in new areas as-yet unpainted, these entries will have details such as "How much has painted since last time", "which element was the last to paint" and "what is the total area of paints so far".

Motivation

As developers increasingly organise their applications into components, there's growing demand to measure performance of subsections of an application or web page. For instance, a developer may want to know when a subsection of the DOM has been painted, like a table or a widget, so they can mark the paint time and submit it to their analytics.

Current Web APIs don't help with this. Element Timing is limited due to what it can mark, so it can't be used for whole sections. The polyfill referenced below attempts to provide a userspace solution by adding Element Timing to all elements within a container and using the data from those performance entries to know when painting has finished. This approach has several drawbacks though:

  • Marking elements with the elementtiming attribute needs to happen as early as possible before painting happens; this will require server-side changes or blocking rendering until all elements are marked (degrading performance)
  • A MutationObserver needs to be utilised to catch new elements being injected into the DOM (with elementtiming being set)
  • The polyfill will need to run and perform set up in the page header, increasing the time to first paint
  • Tracking of rectangles will need to be performed in userspace rather then the browser's built in 2D engine, making it much less efficient

Developers know their domain better than anyone else and they would like to be able to communicate the performance of their own blocks of content in a way that their users or organisation would understand, for example "time to first tweet".

Being able to mark a segment of content and asking the rendering engine to identify when that has been painted is a growing request by developers.

Goals

  1. Inform developers when sections of the DOM are first displayed on the screen. To keep the first version of this spec simpler, we are not including shadow DOM in this version, as this still needs to be understood for elementtiming.
  2. Inform developers when those same sections of the DOM have finished their initial paint activity (indicating this section is ready for viewing or interacting with).

Non-Goals

LCP Integration

This is not intended to provide changes to the Largest Contentful Paint algorithm. Although LCP could benefit in the future from user-marks of content which are containers and receiving paint times from those to choose better candidates, it's not currently in scope whether this will have any affect on existing browser metrics.

Built-in containers

The changes here are also not going to add support to built-in composite elements such as MathML or SVG. It's possible for a follow-up proposal in the future to mark those elements as containers so they can be counted for higher-level metrics, such as LCP, and added to the results when observing container timing.

Shadow DOM

Currently, Element Timing doesn't have support for shadow DOM. There will need to be many architecture-decisions made on how the shadow DOM interacts with Element Timing (should it be opened up or closed, should individual elements be surfaced or just the shadow host element). Once we have a good story for Element Timing, we can have a later proposal for Container Timing too (which hopefully follows similar rules to the Element Timing API).

Using the API

As with Element Timing, registration will be on a per-element basis. An element with a containertiming attribute will have itself and its whole sub-tree registered for container timing. There is currently no plan for implicit registration; see Built-in containers.

Example:

<div  containertiming="foobar"></div>
...
<script>
  const observer = new PerformanceObserver((list) => {
    let perfEntries = list.getEntries();
    // Process the entries by iterating over them.
  });
  observer.observe({ entryTypes: ["container"] });
</script>

It is strongly encouraged to set the attribute 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 give you subsequent events and any future paints that haven't happened yet.

This is the preferred method of annotating container roots, as it gives developers the power to decide which elements they consider important.

PerformanceContainerTiming

We now describe precisely what information is exposed via the WebPerf API. The PerformanceContainerTiming IDL attributes are defined as followed:

  • entryType: "container"
  • startTime: A DOMHighResTimeStamp of the latest container paint time
  • identifier: The value of the element's containertiming attribute (empty string if it does not have it)
  • intersectionRect: The bounding box of all paints accumulated so far within this container
  • size: The size of the combined region painted (so far) within this container
  • firstRenderTime: A DOMHighResTimeStamp of the first paint time for this container
  • duration: A DOMHighResTimeStamp set to 0
  • lastPaintedElement: An Element set to the last painted element (this may need to be a set of elements painted)

Ignoring parts of the DOM

If you want wish to ignore parts of the DOM tree you are monitoring, you can add the containertiming-ignore attribute to the element you want to ignore (plus its children).

<div  containertiming="foobar">
  <main>...</main>
  <!-- We don't want to track paint udpates in the aside -->
  <aside containertiming-ignore>...</aside>
</div>

Life Cycle

Diagram showing the life cycle of events when the container is being painted.
graph LR
  classDef note fill:none,stroke:none,color:#ddd,font-size:14px
  classDef gray fill:#2a2a2a,stroke:#555,color:#bbb
  classDef green fill:#2e6f2e,stroke:#1f4d1f,color:#fff

  %% Annotations
  N1["Only elements directly inside 'wrap' are counted for paint timing"]:::note --> A
  N2["These elements are ignored and not counted towards any perf entries"]:::note --> I

  %% Containers
  subgraph W[&lt;..containertiming=wrap /&gt;]
    direction LR

    A[elm]:::green
    B[elm]:::green

    subgraph I[<..containertimingignore>]
      direction TB

        I1[elm]:::gray
        I2[elm]:::gray
        I3[elm]:::gray
        I4[elm]:::gray


    end
  end

style I stroke-dasharray: 5
Loading

Security and Privacy

The cross-frame boundary is not breached: elements belonging to iframes are not exposed to the parent frames. No timing information is passed to the parent frame unless the developer themselves explicitly passes information via postMessage.

Most of the information provided by this API can already be estimated, even if in tedious ways. Element Timing returns the first rendering time for images and text. The PaintTiming API could be used to compute a related timestamp for all the elements within a container root (see Polyfill).

How do we try this out?

### Polyfill

- See Polyfill - See Polyfill Performance Impact

The polyfill has been deprecated in favor of trying out the API in Chrome Canary or Beta.

Chrome Canary and Chrome Beta

Container Timing is now available in Chrome Canary behind a flag. To enable it:

  1. Ensure your Chromium is recent enough (v145 and upwards)
  2. Open chrome://flags/#enable-experimental-web-platform-features
  3. Set it to Enabled
  4. Restart the browser
  5. [Optional] You can also launch Chrome with the flag --enable-blink-features=ContainerTiming
  6. Open DevTools, go to the console and run PerformanceObserver.supportedEntryTypes, you should see container in the list of supported entry types.
  7. See the Examples section for examples on using the Container Timing API

Browser Extension

You can try out the Chrome extension here. You will need to build it manually for now and load the browser with it set as an argument. For the extension to work, you will need to have the experimental web platform features flag enabled in Chrome (see above).

Once you have the browser running with Container Timing enabled, you can run:

  • $ npm i
  • $ npm run start

in the root of this repo and it should load up the examples for you to try out.

Considered alternatives

Element Timing everywhere

Element Timing can be applied to any element by the developer, but it is too limited in what it can support. Developers wanting to measure when a component is ready would need to apply the attribute to every element, which is cumbersome and sometimes infeasible (when the component is not authored by them). On top of this, Element Timing wouldn't take into account new elements coming into the DOM at a later stage.

Largest Contentful Paint

For the reasons mentioned in the Motivation, Largest Contentful Paint (LCP) isn't useful enough to time when specific parts of the page have loaded. It utilizes Element Timing for its functionality and thus has the same shortcomings as Element Timing.

User-space polyfill in JavaScript

As mentioned in the Motivation, the polyfill would need a few things in place to work properly and can add overhead to the web application. The polyfill will need to mark elements with elementtiming as soon as possible, especially before the browser begins painting. In order to achieve this, the polyfill will need to run in the head, block rendering, and scan the DOM for a containertiming attribute and modify the children elements to add the elementtiming attributes to all "eligible" child nodes.

After marking elements in the initial DOM, the polyfill will then need to setup a MutationObserver to watch for changes in the tree and tag newly-incoming elements with the elementtiming attribute before they are painted. This can lead to a race condition where the element may paint before the attribute is applied and Element Timing is taken into effect.

Finally, tracking of all rectangles in userspace may not be as efficient as the browser's built-in 2D library, and thus can incur memory overhead.

Questions

  • How do we register containers? Can it be done dynamically? (More here)
  • Setting the containertiming attribute far up the tree could cause a lot of processing. As the depth is infinite, we may need to have some limit or default depth set.
  • We will want to add some way for developers to ignore certain blocks of elements without using an inner container (which would degrade performance).
  • As most developers will be using this for startup metrics (similar to LCP), do we want to offer an option to stop tracking on user input?
  • As the browser paints in batches, lastPaintedElement may need to be an array of elements.

W3C Specification Meetings

December 7th 23:

February 15th 2024:

May 23rd 2024:

TPAC 2024:

Discussion around Shadow DOM:

TPAC 2025:

Standards Positions

Glossary

  • Region: This is an object which efficiently holds Rects and can calculate overlaps between them plus various other things such as total size etc. In Chromium this would be a Skia or SkRegion, Firefox's Moz2D and Webkit's CoreGraphics libraries should have equivalent concepts.
  • Container Root: This is an element which has the "containertiming" attribute applied

Links & Further Reading

References & acknowledgements

Many thanks for valuable feedback and advice from:

About

Container Timing

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors