diff --git a/.gitignore b/.gitignore index a6de460..3c7ef84 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ polyfill.js polyfill.js.map demo-overlays.js demo-overlays.js.map +public diff --git a/ORIGIN_TRIAL.md b/ORIGIN_TRIAL.md index 5523a90..98b5e87 100644 --- a/ORIGIN_TRIAL.md +++ b/ORIGIN_TRIAL.md @@ -10,9 +10,9 @@ This document describes the Chrome experimental feature enabling developers to m - **Issue Tracker**: [GitHub Issues](https://github.com/WICG/container-timing/issues) - **Blog Post**: [Container Timing: Measuring Web Components Performance](https://blogs.igalia.com/dape/2026/02/10/container-timing-measuring-web-components-performance/) -## Implementation in Chrome v147+ +## Origin Trial available from m148 - m153 -Chrome v147 introduced support for the Container Timing API behind the experimental web platform features flag. The API allows developers to mark sections of the DOM with the `containertiming` attribute and receive performance entries when those sections are painted. +The Chrome Origin Trial introduces support for the Container Timing API behind the experimental web platform features flag. The API allows developers to mark sections of the DOM with the `containertiming` attribute and receive performance entries when those sections are painted. ### Basic Usage diff --git a/README.md b/README.md index 6e99318..9c21d93 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Container Timing: Explainer -_Note: This API plans to go to [Origin Trial](./ORIGIN_TRIAL.md) during Chrome v147-152, please try it out!_ +_Note: This API plans to go to [Origin Trial](./ORIGIN_TRIAL.md) during Chrome m148-153, please try it out!_ ## Authors diff --git a/examples/adding-content/index.shadowed.html b/examples/adding-content/index.shadowed.html deleted file mode 100644 index 2edd9f7..0000000 --- a/examples/adding-content/index.shadowed.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - Adding Content to a component - - - -
- -
-

Some title for the container

-

- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum." -

-
-
-

Some inner content

-

- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum." -

-
-
- - - diff --git a/examples/adding-content/index.transparent.html b/examples/adding-content/index.transparent.html deleted file mode 100644 index bae2ff4..0000000 --- a/examples/adding-content/index.transparent.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - Adding Content to a component - - - -
- -
-

Some title for the container

-

- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum." -

-
-
-

Some inner content

-

- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad - minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum." -

-
-
- - - diff --git a/examples/canvas-2d/index.html b/examples/canvas-2d/index.html new file mode 100644 index 0000000..c939173 --- /dev/null +++ b/examples/canvas-2d/index.html @@ -0,0 +1,14 @@ + + + + + + Canvas 2D - Container Timing + + +
+ +
+ + + diff --git a/examples/canvas-2d/index.js b/examples/canvas-2d/index.js new file mode 100644 index 0000000..28c1d88 --- /dev/null +++ b/examples/canvas-2d/index.js @@ -0,0 +1,35 @@ +const canvas = document.getElementById("canvas"); +const ctx = canvas.getContext("2d"); + +// Gradient background +const gradient = ctx.createLinearGradient(0, 0, 400, 300); +gradient.addColorStop(0, "#1a1a2e"); +gradient.addColorStop(1, "#16213e"); +ctx.fillStyle = gradient; +ctx.fillRect(0, 0, 400, 300); + +// Colored circles +const colors = ["#e94560", "#0f3460", "#533483", "#e94560", "#0f3460"]; +for (let i = 0; i < 5; i++) { + ctx.beginPath(); + ctx.arc(50 + i * 75, 150, 40, 0, Math.PI * 2); + ctx.fillStyle = colors[i]; + ctx.fill(); +} + +// Label +ctx.fillStyle = "#ffffff"; +ctx.font = "bold 18px Arial"; +ctx.textAlign = "center"; +ctx.fillText("2D Canvas", 200, 270); + +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..5dbebfa --- /dev/null +++ b/examples/index.html @@ -0,0 +1,153 @@ + + + + + + Container Timing Examples + + + +

Container Timing Examples

+

Demos for the Container Timing API.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExampleDescriptionChromium support
Adding contentDynamically adds elements inside a containertiming container and observes the resulting timing entries, including intersectionRect and damagedRects.Yes (with flag) ?
Canvas 2DTracks container timing for a <canvas> element painted with the 2D API.Not supported
Shadow DOMShows container timing behaviour with a declarative shadow root (shadowrootmode="open") containing observed elements.Not supported
Skeleton layoutReplaces a shimmer skeleton with real content after a delay. Tests that the low-entropy filter correctly ignores solid-colour placeholder blocks.Yes (with flag) ?
SVGMeasures container timing for an inline SVG with nested groups, gradients, and decorative paths.Not supported
SVG as imageTracks timing when an SVG file is loaded via an <img src="…svg"> tag rather than inlined.Yes (with flag) ?
SVG with imageMeasures timing for an SVG that embeds a raster image deep within its group hierarchy.Yes (with flag) ?
SVG with multiple imagesLike the above, but with multiple raster images embedded inside the SVG via <image> elements.Yes (with flag) ?
SVG with textMeasures timing for an SVG containing text elements at multiple nesting levels.Yes (with flag) ?
TableContainer timing for a complex flexbox-based virtualized table layout with transform-positioned rows and columns.Yes (with flag) ?
Video (no poster)Tracks container timing for a <video> element without a poster image.Not supported
Video (with poster)Like the above, but with a poster attribute to test timing when an initial poster frame is displayed.Yes (with flag) ?
WebGLTracks container timing for a <canvas> element rendered with WebGL.Not supported
+ + + diff --git a/examples/svg-as-image/image.svg b/examples/svg-as-image/image.svg new file mode 100644 index 0000000..d16d9fb --- /dev/null +++ b/examples/svg-as-image/image.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + SVG as image src + diff --git a/examples/svg-as-image/index.html b/examples/svg-as-image/index.html new file mode 100644 index 0000000..395f5dc --- /dev/null +++ b/examples/svg-as-image/index.html @@ -0,0 +1,14 @@ + + + + + + SVG as image src - Container Timing + + +
+ +
+ + + diff --git a/examples/svg-as-image/index.js b/examples/svg-as-image/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/svg-as-image/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/svg-with-image/index.html b/examples/svg-with-image/index.html new file mode 100644 index 0000000..a8ec010 --- /dev/null +++ b/examples/svg-with-image/index.html @@ -0,0 +1,57 @@ + + + + + + SVG with image - Container Timing + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/examples/svg-with-image/index.js b/examples/svg-with-image/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/svg-with-image/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/svg-with-images/index.html b/examples/svg-with-images/index.html new file mode 100644 index 0000000..0773529 --- /dev/null +++ b/examples/svg-with-images/index.html @@ -0,0 +1,25 @@ + + + + + + Images within SVG - Container Timing + + +
+ + + + + +
+ + + diff --git a/examples/svg-with-images/index.js b/examples/svg-with-images/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/svg-with-images/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/svg-with-images/sample.jpg b/examples/svg-with-images/sample.jpg new file mode 100644 index 0000000..1f48466 Binary files /dev/null and b/examples/svg-with-images/sample.jpg differ diff --git a/examples/svg-with-text/index.html b/examples/svg-with-text/index.html new file mode 100644 index 0000000..c9e9af9 --- /dev/null +++ b/examples/svg-with-text/index.html @@ -0,0 +1,66 @@ + + + + + + SVG with text - Container Timing + + +
+ + + + + + + + + + + + + + + Container + Timing API + + + + +
+ + + diff --git a/examples/svg-with-text/index.js b/examples/svg-with-text/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/svg-with-text/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/svg/index.js b/examples/svg/index.js index 90e7f02..6dea166 100644 --- a/examples/svg/index.js +++ b/examples/svg/index.js @@ -5,6 +5,18 @@ const observer = new PerformanceObserver((list) => { }); observer.observe({ - entryTypes: ["element", "paint", "largest-contentful-paint", "container"], + type: ["element"], + buffered: true, +}); +observer.observe({ + type: ["paint"], + buffered: true, +}); +observer.observe({ + type: ["largest-contentful-paint"], + buffered: true, +}); +observer.observe({ + type: ["container"], buffered: true, }); diff --git a/examples/video/no-poster/index.html b/examples/video/no-poster/index.html new file mode 100644 index 0000000..4e11930 --- /dev/null +++ b/examples/video/no-poster/index.html @@ -0,0 +1,17 @@ + + + + + + Video (no poster) - Container Timing + + +
+ +
+ + + diff --git a/examples/video/no-poster/index.js b/examples/video/no-poster/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/video/no-poster/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/video/video.mp4 b/examples/video/video.mp4 new file mode 100644 index 0000000..084efec Binary files /dev/null and b/examples/video/video.mp4 differ diff --git a/examples/video/video.webm b/examples/video/video.webm new file mode 100644 index 0000000..c49cbe0 Binary files /dev/null and b/examples/video/video.webm differ diff --git a/examples/video/with-poster/index.html b/examples/video/with-poster/index.html new file mode 100644 index 0000000..55fcc7a --- /dev/null +++ b/examples/video/with-poster/index.html @@ -0,0 +1,17 @@ + + + + + + Video (with poster) - Container Timing + + +
+ +
+ + + diff --git a/examples/video/with-poster/index.js b/examples/video/with-poster/index.js new file mode 100644 index 0000000..fb11b9c --- /dev/null +++ b/examples/video/with-poster/index.js @@ -0,0 +1,10 @@ +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/examples/video/with-poster/poster.jpg b/examples/video/with-poster/poster.jpg new file mode 100644 index 0000000..5f19687 Binary files /dev/null and b/examples/video/with-poster/poster.jpg differ diff --git a/examples/webgl/index.html b/examples/webgl/index.html new file mode 100644 index 0000000..062f19f --- /dev/null +++ b/examples/webgl/index.html @@ -0,0 +1,14 @@ + + + + + + WebGL - Container Timing + + +
+ +
+ + + diff --git a/examples/webgl/index.js b/examples/webgl/index.js new file mode 100644 index 0000000..962a1fd --- /dev/null +++ b/examples/webgl/index.js @@ -0,0 +1,70 @@ +const canvas = document.getElementById("canvas"); +const gl = canvas.getContext("webgl"); + +const vertexShaderSource = ` + attribute vec2 a_position; + attribute vec3 a_color; + varying vec3 v_color; + void main() { + gl_Position = vec4(a_position, 0.0, 1.0); + v_color = a_color; + } +`; + +const fragmentShaderSource = ` + precision mediump float; + varying vec3 v_color; + void main() { + gl_FragColor = vec4(v_color, 1.0); + } +`; + +function createShader(type, source) { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + return shader; +} + +const program = gl.createProgram(); +gl.attachShader(program, createShader(gl.VERTEX_SHADER, vertexShaderSource)); +gl.attachShader(program, createShader(gl.FRAGMENT_SHADER, fragmentShaderSource)); +gl.linkProgram(program); +gl.useProgram(program); + +// Interleaved position (x, y) and color (r, g, b) for a triangle +const data = new Float32Array([ + // x y r g b + 0.0, 0.8, 1.0, 0.2, 0.2, // top — red + -0.8, -0.8, 0.2, 1.0, 0.2, // bottom-left — green + 0.8, -0.8, 0.2, 0.2, 1.0, // bottom-right — blue +]); + +const buffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, buffer); +gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); + +const stride = 5 * Float32Array.BYTES_PER_ELEMENT; + +const posLoc = gl.getAttribLocation(program, "a_position"); +gl.enableVertexAttribArray(posLoc); +gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, stride, 0); + +const colorLoc = gl.getAttribLocation(program, "a_color"); +gl.enableVertexAttribArray(colorLoc); +gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, stride, 2 * Float32Array.BYTES_PER_ELEMENT); + +gl.clearColor(0.1, 0.1, 0.1, 1.0); +gl.clear(gl.COLOR_BUFFER_BIT); +gl.drawArrays(gl.TRIANGLES, 0, 3); + +const observer = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + console.log(entry); + }); +}); + +observer.observe({ type: ["element"], buffered: true }); +observer.observe({ type: ["paint"], buffered: true }); +observer.observe({ type: ["largest-contentful-paint"], buffered: true }); +observer.observe({ type: ["container"], buffered: true }); diff --git a/index.bs b/index.bs index f12fba2..1aecca7 100644 --- a/index.bs +++ b/index.bs @@ -183,7 +183,7 @@ For each {{Document}}, the user agent must maintain a container root Extensions to the {{Element}} Interface {#extensions-to-element} ---------------------------------------------------------------- -This section will be removed once the [[DOM]] specification had been modified. +This section will be removed once the [[DOM]] specification had been modified. We extend the {{Element}} interface as follows: diff --git a/package-lock.json b/package-lock.json index 050ee99..37bd89f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -187,9 +187,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", diff --git a/package.json b/package.json index d17885c..832e40a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "http-server ./examples -c-1 -a 127.0.0.1 -p 8080 -o" + "build": "rm -rf public && mkdir -p public/demo-overlays && cd demo-overlays && npm run build && cd .. && cp -r examples public/examples && cp demo-overlays/demo-overlays.js demo-overlays/demo-overlays.js.map demo-overlays/demo-overlays.css public/demo-overlays/", + "start": "npm run build && http-server ./public -c-1 -a 127.0.0.1 -p 8080 -o /examples" }, "author": "", "license": "ISC"