├── .github └── workflows │ └── auto-publish.yml ├── .pr-preview.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── filmstrip.png ├── filmstrip.svg ├── index.bs ├── presentation-timestamps.md └── w3c.json /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | # Configuration options https://w3c.github.io/spec-prod/ 2 | name: Node CI 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: {} 9 | 10 | jobs: 11 | validate-and-publish: 12 | name: Validate and Publish 13 | runs-on: ubuntu-latest # only linux supported at present 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: w3c/spec-prod@v2 17 | with: 18 | TOOLCHAIN: bikeshed 19 | GH_PAGES_BRANCH: gh-pages 20 | W3C_ECHIDNA_TOKEN: ${{ secrets.ECHIDNA_TOKEN }} 21 | W3C_WG_DECISION_URL: "https://lists.w3.org/Archives/Public/public-web-perf/2021Apr/0005.html" 22 | W3C_NOTIFICATIONS_CC: "${{ secrets.CC }}" 23 | W3C_BUILD_OVERRIDE: | 24 | status: WD 25 | TR: http://www.w3.org/TR/paint-timing/ 26 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed" 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/copyright-software). 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PerformancePaintTiming 2 | 3 | Web developers require more information on page load performance in the wild. 4 | No single moment in time completely captures the "loading experience". To give developers insight into the loading experience, we propose a set of key progress metrics to capture the series of key moments during pageload which developers care about. 5 | 6 | For detailed motivation, see the [Why First Paint?](https://docs.google.com/document/d/1wdxSXo_jctZjdPaJeTtYYFF-rLtUFxrU72_7h9qbQaM/edit) doc. 7 | 8 | First Paint (FP), is the first of these key moments, followed by First Contentful Paint (FCP). In the future, we may add additional metrics, such as First Meaningful Paint (FMP), and Time to Interactive (TTI). 9 | 10 | ## Interface 11 | We propose introducing the `PerformancePaintTiming` interface, extending the PerformanceEntry interface, to report the time to first paint and time to first contentful paint. 12 | 13 | ```javascript 14 | interface PerformancePaintTiming : PerformanceEntry {}; 15 | ``` 16 | 17 | Entries will have a `name` of "first-paint" and "first-contentful-paint" respectively, and an `entryType` of `"paint"`. `startTime` is the `DOMHighResTimeStamp` indicating when the paint occurred, and the `duration` will always be 0. 18 | 19 | ## Definition 20 | 21 | - `"first-paint"` entries contain a `DOMHighResTimeStamp` reporting the time when the browser first rendered after navigation. This excludes the default background paint, but includes non-default background paint. This is the first key moment developers care about in page load – when the browser has started to render the page. 22 | 23 | - `"first-contentful-paint"` contain a `DOMHighResTimestamp` reporting the time when the browser first rendered any text, image (including background images), non-white canvas or SVG. This includes text with pending webfonts. This is the first time users could start consuming page content. 24 | 25 | _WIP: [define processing algorithm and integration with HTML](https://github.com/w3c/paint-timing/issues/4)_ 26 | 27 | --- 28 | 29 | The browser has performed a "paint" or "render" when it has converted the render tree to pixels on the screen. 30 | 31 | More formally, we consider the browser to have "rendered" a document when it has updated "the rendering or user interface of that Document and its browsing context to reflect the current state". See the HTML spec's section on the event loop processing model – [section 7.12](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model). 32 | 33 | The rendering pipeline is very complex, and the timestamp should be the latest timestamp the browser is able to note in this pipeline (best effort). Typically the time at which the frame is submitted to the OS for display is recommended for this API. 34 | 35 | 36 | ## Usage 37 | 38 | ```javascript 39 | 40 | const observer = new PerformanceObserver(function(list) { 41 | const perfEntries = list.getEntries(); 42 | for (const perfEntry of perfEntries) { 43 | // Process entries 44 | // report back for analytics and monitoring 45 | // ... 46 | } 47 | }); 48 | 49 | // register observer for paint timing notifications 50 | observer.observe({entryTypes: ["paint"]}); 51 | 52 | ``` 53 | These entries will be buffered for the cases where PerformanceObserver is unable to register itself early enough on the page. 54 | For this case paint entries can be accessed as follows: 55 | ```javascript 56 | 57 | performance.getEntriesByType('paint'); 58 | ``` 59 | 60 | ## Examples 61 | 62 | These examples are hand annotated, based on the definitions given above. 63 | 64 | ![Web page filmstrips with annotated first paint times.](filmstrip.png) 65 | 66 | Some rough bulk data can be seen [here](https://docs.google.com/spreadsheets/d/1i0-tOtZP21m3DjBJflUJYao9-WAKwWV2p9WFlVhVivg/edit#gid=1447332636) or [here](https://docs.google.com/spreadsheets/d/1nGauGA3EvN8NBC3ErWjLd8Bz-NzmmEa6q6UP5KhfgeA/edit#gid=0). This data was collected using a somewhat different definition than we're currently using – it includes white paints in `first-paint` and only looks at text and image paints for `first-contentful-paint`. 67 | 68 | #### Why not add this to Navigation Timing? 69 | This belongs outside Navigation Timing because Navigation Timing is spec'd as queueing the entry on document load end; however, FCP (or FMP in the future) may not have triggered at that point. 70 | -------------------------------------------------------------------------------- /filmstrip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w3c/paint-timing/c754f44f5e325435997a6396e5a3eb71ae617245/filmstrip.png -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 |
  2 | Title: Paint Timing
  3 | Group: webperf
  4 | Shortname: paint-timing
  5 | Level: none
  6 | ED: https://w3c.github.io/paint-timing/
  7 | TR: https://www.w3.org/TR/paint-timing/
  8 | Status: ED
  9 | Editor: Ian Clelland, Google https://google.com, iclelland@chromium.org, w3cid 76841
 10 |         Noam Rosenthal, Invited Expert, noam@webkit.org, w3cid 121539
 11 | Former Editor: Shubhie Panicker, Google https://google.com, panicker@google.com, w3cid 92587
 12 |                Nicolás Peña Moreno, Google https://google.com, npm@chromium.org, w3cid 103755
 13 | Repository: w3c/paint-timing
 14 | Abstract: This document defines an API that can be used to capture a series of key moments (first paint, first contentful paint) during pageload which developers care about.
 15 | Default Highlight: js
 16 | 
17 | 22 |
 23 | urlPrefix: https://html.spec.whatwg.org/multipage/images.html
 24 |     type: dfn; text: available; url: #img-available;
 25 |     type: dfn; text: image; url: #images;
 26 | urlPrefix: https://www.w3.org/TR/SVG2/render.html; spec: CR-SVG2
 27 |     type: dfn; url: #Rendered-vs-NonRendered; text: svg element with rendered descendants;
 28 | urlPrefix: https://www.w3.org/TR/css-backgrounds-3/; spec: CSS-BACKGROUNDS-3;
 29 |     type: dfn; text: background-image; url: #propdef-background-image;
 30 |     type: dfn; text: background-size; url: #background-size;
 31 | urlPrefix: https://html.spec.whatwg.org/multipage/canvas.html; spec: HTML;
 32 |     type: dfn; text: context mode; url: #concept-canvas-context-mode;
 33 | urlPrefix: https://html.spec.whatwg.org/multipage/images.html; spec: HTML;
 34 |     type: dfn; text: completely available; url: #img-all;
 35 | urlPrefix: https://html.spec.whatwg.org/multipage/rendering.html; spec: HTML;
 36 |     type: dfn; text: being rendered; url: #being-rendered;
 37 | urlPrefix: https://w3c.github.io/IntersectionObserver/
 38 |     type: dfn; text: Intersection rect algorithm; url: #calculate-intersection-rect-algo
 39 | urlPrefix: https://html.spec.whatwg.org/multipage/dom.html
 40 |     type: dfn; text: represents; url: #represents;
 41 | urlPrefix: https://drafts.csswg.org/css-pseudo-4
 42 |     type: dfn; text: generated content pseudo-element; url: #generated-content;
 43 |     type: dfn; text: typographical pseudo-element; url: #typographic-pseudos;
 44 | urlPrefix: https://drafts.csswg.org/css2/zindex.html; spec: CSS;
 45 |     type: dfn; url:#painting-order; text: painting order;
 46 | urlPrefix: https://www.w3.org/TR/cssom-view
 47 |     type: dfn; text: scrolling area; url: #scrolling-area;
 48 | urlPrefix: https://www.w3.org/TR/css3-values/
 49 |     type: dfn; text: url valued; url: #url-value;
 50 | urlPrefix: https://drafts.fxtf.org/css-masking-1/
 51 |     type: dfn; text: clip-path; url: #the-clip-path;
 52 | urlPrefix: https://www.w3.org/TR/css-images-3/
 53 |     type: dfn; text: CSS image; url: #typedef-image;
 54 | urlPrefix: https://html.spec.whatwg.org/multipage/media.html
 55 |     type: dfn; text: poster frame; url: #poster-frame;
 56 | urlPrefix: https://html.spec.whatwg.org/multipage/browsers.html
 57 |     type: dfn; text: browsing context; url: #browsing-context;
 58 |     type: dfn; text: nested browsing context; url: #nested-browsing-context;
 59 | urlPrefix: https://fetch.spec.whatwg.org/; spec: FETCH;
 60 |     type: dfn; url:#concept-tao-check; text: timing allow check;
 61 | urlPrefix: https://wicg.github.io/largest-contentful-paint/; spec: ELEMENT-TIMING
 62 |     type: dfn; url:#report-largest-contentful-paint; text: Report largest contentful paint
 63 | urlPrefix: https://wicg.github.io/element-timing/; spec: ELEMENT-TIMING
 64 |     type: dfn; url:#report-element-timing; text: Report element timing
 65 | urlPrefix: https://w3c.github.io/long-animation-frames/; spec: LONG-ANIMATION-FRAME
 66 |     type: dfn; url:#queue-a-long-animation-frame-entry; text: Queue a long animation frame entry
 67 |     type: dfn; text: current frame timing info
 68 | 
69 | 70 | Introduction {#intro} 71 | ===================== 72 | 73 |
74 | 75 | This section is non-normative. 76 | 77 | Much of the purpose of a web browser is to translate HTML, CSS and image 78 | resources into pixels on a screen for users. Measuring the performance of a web 79 | page often involves measuring the time it takes to perform these tasks - to 80 | render content, whether text or image, to the screen. There are many different 81 | ways to use this timing to make statemements about the performance of a page, 82 | or about the user experience of loading, but fundamentally all of those ways 83 | begin with a common means of measuring time. 84 | 85 | This is a foundational document which specifies how to measure paint timing as a 86 | general-purpose mechanism. That foundation is then used to define the First 87 | Paint and First Contentful Paint metrics. Other specific instances of paint 88 | measurement may be specified in other documents. 89 | 90 | Specifically, this specification covers: 91 | * Measuring the time when images are decoded and ready for painting 92 | * Measuring the time when elements are painted 93 | * Measuring the size of the painted elements 94 | * Determining whether a painted element contains any visible content. 95 | 96 | First Paint and First Contentful Paint {#first-paint-and-first-contentful-paint} 97 | -------------------------------------------------------------------------------- 98 | 99 | Load is not a single moment in time — it's an experience that no one metric can fully capture. There are multiple moments during the load experience that can affect whether a user perceives it as "fast" or "slow". 100 | 101 | First paint (FP) is the first of these key moments, followed by first contentful paint (FCP). These metrics mark the points in time when the browser renders a given document. This is important to the user because it answers the question: is it happening? 102 | 103 | The primary difference between the two metrics is FP marks the first time the browser renders anything for a given document. By contrast, FCP marks the time when the browser renders the first bit of image or text content from the DOM. 104 | 105 | Usage example {#example} 106 | ------------------------ 107 | 108 |
109 |     const observer = new PerformanceObserver(function(list) {
110 |         const perfEntries = list.getEntries();
111 |         for (const perfEntry of perfEntries) {
112 |             // Process entries
113 |             // report back for analytics and monitoring
114 |             // ...
115 |         }
116 |     });
117 | 
118 |     // register observer for paint timing notifications
119 |     observer.observe({entryTypes: ["paint"]});
120 | 
121 | 122 |
123 | 124 | Terminology {#sec-terminology} 125 | ============================== 126 | 127 | Paint: the user agent has performed a "paint" (or "render") when it has converted the render tree to pixels on the screen. 128 | Formally, we consider the user agent to have "rendered" a document when it has performed the [=update the rendering=] steps of the event loop. 129 | 130 | NOTE: The rendering pipeline is very complex, and the timestamp should be the latest timestamp the user agent is able to note in this pipeline (best effort). Typically the time at which the frame is submitted to the OS for display is recommended for this API. 131 | 132 | A [=generated content pseudo-element=] is a paintable pseudo-element when all of the following apply: 133 | * The pseudo-element's [=used value|used=] [=visibility=] is visible. 134 | * The pseudo-element's [=used value|used=] [=opacity=] is greater than zero. 135 | * The pseudo-element generates a non-empty [=box=]. 136 | 137 | A [=CSS image=] |img| is a contentful image when all of the following apply: 138 | * |img| is [=url valued=]. 139 | * |img| is [=available=]. 140 | 141 | A {{DOMString}} is non-empty if it contains at least one character excluding [=document white space characters=]. 142 | 143 | An [=/element=] |target| is contentful when one or more of the following apply: 144 | * |target| has a [=text node=] child, representing [=non-empty=] text, and the node's [=used value|used=] [=opacity=] is greater than zero. 145 | 146 | NOTE: this covers the case where a [=typographical pseudo-element=] would override the opacity of the text node. 147 | 148 | * |target| is a [=replaced element=] representing an [=available=] [=image=]. 149 | * |target| has a [=background-image=] which is a [=contentful image=], and its [=used value|used=] [=background-size=] has non-zero width and height values. 150 | * |target| is a <{canvas}> with its [=context mode=] set to any value other than none. 151 | * |target| is a <{video}> element that [=represents=] its [=poster frame=] or the first video frame and the frame is available. 152 | * |target| is an [=svg element with rendered descendants=]. 153 | * |target| is an <{input}> element with a [=non-empty=] <{input/value}> attribute. 154 | * |target| is an [=originating element=] for a [=paintable pseudo-element=] that represents a [=contentful image=] or [=non-empty=] text. 155 | 156 | An [=/element=] is timing-eligible if it is one of the following: 157 | 158 | * an <{img}> element. 159 | * an <{image}> element inside an <{svg}> element. 160 | * a <{video}> element with a [=poster frame=]. 161 | * an element with a [=contentful image|contentful=] background-image. 162 | * a text node. 163 | 164 | To compute the paintable bounding rect of [=/element=] |target|, run the following steps: 165 | 1. Let |boundingRect| be the result of running the {{Element/getBoundingClientRect()}} on |target|. 166 | 1. Clip |boundingRect| with the [=document=]'s [=scrolling area=]. 167 | 1. Return |boundingRect|. 168 | 169 | NOTE: elements contained by boxes with overflow: scroll or overflow: hidden don't have their [=paintable bounding rect=] clipped, as in both cases the [=/element=] can become visible by scrolling. 170 | 171 | An [=/element=] |el| is paintable when all of the following apply: 172 | * |el| is [=being rendered=]. 173 | * |el|'s [=used value|used=] [=visibility=] is visible. 174 | * |el| and all of its ancestors' [=used value|used=] [=opacity=] is greater than zero. 175 | 176 | NOTE: there could be cases where a paintable [=/element=] would not be visible to the user, for example in the case of text that has the same color as its background. 177 | Those elements would still considered as paintable for the purpose of computing [=first contentful paint=]. 178 | 179 | * |el|'s [=paintable bounding rect=] intersects with the [=scrolling area=] of the [=document=]. 180 | 181 | NOTE: This covers the cases where the element is scaled to zero size, has display: none, or display: contents where the contents resolve to an empty rect. 182 | 183 | NOTE: As a general rule, an [=/element=] is paintable if it is within the viewport, or can potentially be in the viewport as a result of scrolling or zooming. 184 | 185 | First paint entry contains a {{DOMHighResTimeStamp}} reporting the time when the user agent first rendered after navigation. This excludes the default background paint, but includes non-default background paint and the enclosing box of an iframe. This is the first key moment developers care about in page load – when the user agent has started to render the page. 186 | 187 | A [=browsing context=] |ctx| is paint-timing eligible when one of the following apply: 188 | * |ctx| is a [=top-level browsing context=]. 189 | * |ctx| is a [=nested browsing context=], and the user agent has configured |ctx| to report paint timing. 190 | 191 | NOTE: this allows user agents to enable paint-timing only for some of the frames, in addition to the main frame, if they so choose. 192 | For example, a user agent may decide to disable paint-timing for cross-origin iframes, as in some scenarios their paint-timing might reveal information about the main frame. 193 | 194 | The {{PaintTimingMixin}} interface {#sec-PaintTimingMixin} 195 | ======================================= 196 | 197 |
198 |     [Exposed=Window]
199 |     interface mixin PaintTimingMixin {
200 |         readonly attribute DOMHighResTimeStamp paintTime;
201 |         readonly attribute DOMHighResTimeStamp? presentationTime;
202 |     };
203 | 
204 | 205 | Objects including the {{PaintTimingMixin}} interface mixin have an associated paint timing info (null or a [=/paint timing info=]). 206 | 207 | paint timing info is a [=struct=]. It has the following [=struct/items=]: 208 | 209 |
210 | : rendering update end time 211 | :: A {{DOMHighResTimeStamp}} 212 | : implementation-defined presentation time 213 | :: Null or a {{DOMHighResTimeStamp}} 214 |
215 | 216 | The {{PaintTimingMixin/paintTime}} attribute's getter step is to return [=/this=]'s [=PaintTimingMixin/paint timing info=]'s [=paint timing info/rendering update end time=]. 217 | 218 | The {{PaintTimingMixin/presentationTime}} attribute's getter step, if exists, is to return [=/this=]'s [=PaintTimingMixin/paint timing info=]'s [=paint timing info/implementation-defined presentation time=]. 219 | 220 | To get the default paint timestamp for a [=/paint timing info=] |paintTimingInfo|, return |paintTimingInfo|'s [=implementation-defined presentation time=] if it is non-null, otherwise |paintTimingInfo|'s [=rendering update end time=]. 221 | 222 | 223 | The {{PerformancePaintTiming}} interface {#sec-PerformancePaintTiming} 224 | ======================================= 225 | 226 |
227 |     [Exposed=Window]
228 |     interface PerformancePaintTiming : PerformanceEntry {
229 |         [Default] object toJSON();
230 |     };
231 |     PerformancePaintTiming includes PaintTimingMixin;
232 | 
233 | 234 | {{PerformancePaintTiming}} extends the following attributes of {{PerformanceEntry}} interface: 235 | 236 | * The {{PerformanceEntry/name}} attribute's getter must return a {{DOMString}} for minimal frame attribution. Possible values of name are: 237 | * "first-paint": for [=first paint=] 238 | * "first-contentful-paint": for [=first contentful paint=] 239 | * The {{PerformanceEntry/entryType}} attribute's getter must return "paint". 240 | * The {{PerformanceEntry/startTime}} attribute's getter must return a {{DOMHighResTimeStamp}} of when the paint occured. 241 | * The {{PerformanceEntry/duration}} attribute's getter must return 0. 242 | * When toJSON is called, run the [=default toJSON steps=] for {{PerformancePaintTiming}}. 243 | 244 | NOTE: A user agent implementing {{PerformancePaintTiming}} would need to include "paint" in {{PerformanceObserver/supportedEntryTypes}} of a [=realm/global object=] whose [=Window/browsing context=] is [=paint-timing eligible=]. 245 | This allows developers to detect support for paint timing for a particular [=browsing context=]. 246 | 247 | Processing model {#sec-processing-model} 248 | ======================================== 249 | 250 | Associated Image Requests {#sec-associated-image-requests} 251 | ---------------------------------------------------------- 252 | 253 | Each {{Element}} has an associated image request which is an [=image 254 | request=] or null, initially null. 255 | 256 | When the processing model for an {{Element}} element of type 257 | {{HTMLImageElement}}, {{SVGImageElement}}, or {{HTMLVideoElement}} creates a 258 | new image resource (e.g., to be displayed as an image or poster image), 259 | element's associated image request is set to the image 260 | request of the created image resource. 261 | 262 | Note: Every image resource that is obtained from a URL whose 263 | scheme is equal to "data" has an associated image request 264 | which is not fetched but still needs to be loaded. This request can be the 265 | associated image request of an {{Element}}. 266 | 267 | Note: The current language is vague since it does not point to specific 268 | algorithms. This can be made more rigorous when the corresponding processing 269 | models have a more unified processing model. 270 | 271 | Every {{Element}} has a list of associated background image requests 272 | which is initially an empty array. When the processing model for the {{Element}} 273 | element's style requires a new image resource (to be displayed as 274 | background image), the image request created by the new resource is 275 | appended to element's associated background image requests. 276 | 277 | NOTE: An {{Element}} can have several [=image requests=], e.g. if its 278 | background-image property has multiple values. For instance, in the 279 | following example, a single background-image property produces four 280 | [=image requests=], each of which will be recorded and reported by the 281 | algorithms below. 282 | 283 | ```html 284 | 285 | 291 |
292 |
293 | ``` 294 | 295 | Recording paint timing {#sec-recording-paint-timing} 296 | -------------------------------------------------------- 297 | 298 | A pending image record is a [=struct=] with the following 299 | [=struct/items=]: 300 | 301 | * element, an {{Element}} 302 | * request, an [=image request=] 303 | * loadTime, a {{DOMHighResTimeStamp}} 304 | 305 | Each {{Element}} has a set of owned text nodes, which is an [=ordered set=] of {{Text}} nodes, initially empty. 306 | 307 | Each {{Document}} has a set of previously reported paints, which is an [=ordered set=] of [=strings=], initially empty. 308 | 309 | Each {{Document}} has an images pending rendering, which is a [=/list=] of [=pending image records=], initally empty. 310 | 311 | Each {{Document}} has a set of elements with rendered text, which is an [=ordered set=] of {{Element}}s, initially empty. 312 | 313 |

Modifications to the CSS specification

314 | 315 | Whenever an image request in an {{Element}} element's associated background image requests becomes completely available, run the algorithm to process an image that finished loading with element and image request as inputs. 316 | 317 | 318 |

Modifications to the HTML specification

319 | 320 | When an {{Element}} element's associated image request has become completely available, run the algorithm to process an image that finished loading passing in element and its associated image request as inputs. 321 | 322 |
323 | When the user agent paints a {{Text}} node |text| for the first time, it should execute the following steps: 324 | 325 | * If |text| will not be painted due to the font face being in its font block period, then return. 326 | * Let |element| be the {{Element}} which determines the containing block of |text|. 327 | * Append |text| to |element|'s set of owned text nodes. 328 |
329 | 330 | 331 |

Process image that finished loading

332 | 333 |
334 | To process an image that finished loading given an {{Element}} |element| and an [=image request=] |imageRequest|: 335 | 1. Let |root| be |element|'s [=tree/root=]. 336 | 1. If |root| is not a {{Document}}, return. 337 | 1. Let |now| be the [=current high resolution time=] given |element|'s relevant global object. 338 | 1. Let |record| be a [=pending image record=] with [=pending image record/element=] |element|, [=pending image record/request=] |imageRequest| and [=pending image record/loadTime=] |now|. 339 | 1. Add |record| to |root|'s [=images pending rendering=]. 340 |
341 | 342 | Reporting paint timing {#sec-reporting-paint-timing} 343 | -------------------------------------------------------- 344 | 345 |

First Contentful Paint

346 |
347 | To know whether [=Document=] |document| should report first contentful paint, perform the following steps: 348 | 1. If |document|'s [=set of previously reported paints=] contains "first-contentful-paint", then return false. 349 | 1. If |document| contains at least one [=/element=] that is both [=paintable=] and [=contentful=], then return true. 350 | 1. Otherwise, return false. 351 |
352 | 353 |

Mark paint timing

354 | 355 |
356 | When asked to [=mark paint timing=] given a [=Document=] |document| as input, perform the following steps: 357 | 1. If the [=document=]'s [=Document/browsing context=] is not [=paint-timing eligible=], return. 358 | 1. Let |paintTimingInfo| be a new [=/paint timing info=], whose [=rendering update end time=] is the [=current high resolution time=] given |document|'s [=relevant global object=]. 359 | 1. Let |paintedImages| be a new [=ordered set=] 360 | 1. Let |paintedTextNodes| be a new [=ordered set=] 361 | 1. For each |record| in |doc|'s [=images pending rendering=] list: 362 | 1. If |record|'s [=pending image record/request=] is [=available=] and ready to be painted, then run the following steps: 363 | 1. Append |record| to |paintedImages|. 364 | 1. Remove |record| from |doc|'s images pending rendering list. 365 | 1. For each {{Element}} |element| in |doc|'s descendants: 366 | 1. If |element| is contained in |doc|'s set of elements with rendered text, continue. 367 | 1. If |element|'s set of owned text nodes is empty, continue. 368 | 1. [=set/Append=] |element| to |doc|'s set of elements with rendered text. 369 | 1. [=set/Append=] |element| to |paintedTextNodes|. 370 | 1. Let |reportedPaints| be the |document|'s [=set of previously reported paints=]. 371 | 1. Let |frameTimingInfo| be |document|'s [=current frame timing info=]. 372 | 1. Set |document|'s [=current frame timing info=] to null. 373 | 1. Let |flushPaintTimings| be the following steps: 374 | 1. If |reportedPaints| does not contain "first-paint", and the user agent is configured to mark [=first paint=], then [=report paint timing=] given |document|, "first-paint", and |paintTimingInfo|. 375 | 376 | NOTE: [=First paint=] excludes the default background paint, but includes non-default background paint. 377 | 378 | ISSUE: This should be turned into a normative note. 379 | 380 | 1. If |document| [=should report first contentful paint=], then: 381 | 1. [=Report paint timing=] given |document|, "first-contentful-paint", and |paintTimingInfo|. 382 | 383 | NOTE: A parent frame should not be aware of the paint events from its child iframes, and vice versa. This means that a frame that contains just iframes will have [=first paint=] (due to the enclosing boxes of the iframes) but no [=first contentful paint=]. 384 | 385 | NOTE: A [=document=] is not guaranteed to mark "first-paint" or "first-contentful-paint". A completely blank [=document=] may never mark [=first paint=], and a [=document=] containing only elements that are not [=contentful=] may never mark [=first contentful paint=]. 386 | 387 | NOTE: The marking of [=first paint=] is optional. User-agents implementing paint timing should at the very least mark [=first contentful paint=]. 388 | 389 | 1. [=Report largest contentful paint=] given |document|, |paintTimingInfo|, 390 | |paintedImages| and |paintedTextNodes|. 391 | 1. [=Report element timing=] given |document|, |paintTimingInfo|, 392 | |paintedImages| and |paintedTextNodes|. 393 | 1. If |frameTimingInfo| is not null, then [=queue a long animation frame entry=] given |document|, |frameTimingInfo|, and |paintTimingInfo|. 394 | 395 | 1. If the user-agent does not support implementation-defined presentation times, call |flushPaintTimings| and return. 396 | 397 | 1. Run the following steps [=In parallel=]: 398 | 1. Wait until an implementation-defined time when the current frame has been presented to the user. 399 | 1. Set |paintTimingInfo|'s [=implementation-defined presentation time=] to the [=current high resolution time=] given |document|'s [=relevant global object=]. 400 | 1. If |document|'s [=environment settings object/cross-origin isolated capability=] is false, then: 401 | 1. Coarsen |paintTimingInfo|'s [=implementation-defined presentation time=] to the next multiple of 4 milliseconds, or coarser. 402 | 1. Wait until the [=current high resolution time=] is |paintTimingInfo|'s [=implementation-defined presentation time=]. 403 | 1. [=Queue a global task=] on the [=performance timeline task source=] given |document|'s [=relevant global object=] to run |flushPaintTimings|. 404 |
405 | 406 |

Report paint timing

407 | 408 |
409 | To [=report paint timing=] given |document|, |paintType|, and a [=/paint timing info=] |paintTimingInfo| as arguments, perform the following steps: 410 | 1. Create a new {{PerformancePaintTiming}} object |newEntry| with |document|'s [=relevant realm=] and set its attributes as follows: 411 | 1. Set |newEntry|'s {{PerformanceEntry/name}} attribute to |paintType|. 412 | 1. Set |newEntry|'s {{PerformanceEntry/entryType}} attribute to "paint". 413 | 1. Set |newEntry|'s {{PerformanceEntry/startTime}} attribute to the [=default paint timestamp=] given |paintTimingInfo|. 414 | 1. Set |newEntry|'s {{PerformanceEntry/duration}} attribute to 0. 415 | 1. Set |newEntry|'s [=PaintTimingMixin/paint timing info=] to |paintTimingInfo|. 416 | 1. [=queue a PerformanceEntry|Queue=] |newEntry| in |document|'s [=relevant realm=]. 417 | 1. [=list/Append=] |paintType| to |document|'s [=set of previously reported paints=]. 418 |
419 | 420 | 421 | Common algorithms {#sec-common-algorithms} 422 | ------------------------------------------ 423 | 424 |

Exposed for paint timing

425 | 426 |
427 | 428 | To determine whether an [=/Element=] |element| is [=exposed for paint timing=], given a [=Document=] or null |document|, perform the following steps: 429 | 430 | 1. If |element| is not [=connected=], return false. 431 | 1. If |document| is null, let |document| be |element|'s [=relevant settings object=]'s [=relevant global object=]'s [=associated document=]. 432 | 1. If |document| is not [=fully active=], return false. 433 | 1. If |element|'s [=tree/root=] is not equal to |document|, return false. 434 | 1. Return true. 435 |
436 | 437 | 438 | 439 | 440 |

Acknowledgements

441 | 442 | 443 | Special thanks to all the contributors for their technical input and suggestions that led to improvements to this 444 | specification. 445 | -------------------------------------------------------------------------------- /presentation-timestamps.md: -------------------------------------------------------------------------------- 1 | # `PaintTimingMixin`: Presentation timestamps and their meanings 2 | 3 | Aligning different paint-related timestamps in all the relevant specs. 4 | 5 | ## Overview 6 | When originally introduced, paint timing ("`first-paint`" and "`first-contentful-paint`") aspired to represent an important moment in terms of user experience - "The pixels on the screen" representing a certain state. 7 | However, this is tricky in terms of interopability - that moment is not always measured in the same way, and is not part of the flow of operations covered by web standards. 8 | In the paint-timing spec, a different time is used instead as the `startTime` - [the end of the "update the rendering" phase](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model%3Amark-paint-timing), 9 | where the document is done setting up the rendering and hands over rendering to the user agent. 10 | This is confusing and non-interoperable, as Chromium still reports the VSync-time, which sufficiently corresponds to "pixels on the screen". 11 | In addition, the "long animation frame" timing exposes the hand-over time as its end time, and element-timing/largest contentful paint exposes the VSync time as its `renderTime`. 12 | 13 | The `PaintTimingMixin` attempts to clean up this confusion, by always providing two timestamps, one mandatory and interoperative, one optional and somewhat implementation-defined. 14 | 15 | ## `PaintTimingMixin` 16 | 17 | `PaintTimingMixin` is a mixin that would be included in the interfaces representing all the relevant performance entries: 18 | 19 | - `event` 20 | - `element` 21 | - `largest-contentful-paint` 22 | - `first-paint` 23 | - `first-contentful-paint` 24 | - `long-animation-frame` 25 | 26 | It would have the following shape: 27 | 28 | ```webidl 29 | interface mixin PaintTimingMixin { 30 | // The time when the document finishes updating the rendering, handing over rendering to the user-agent 31 | readonly attribute DOMHighResTimeStamp paintTime; 32 | 33 | // A coarsened implementation-specific time, approximately the "VSync" time when the new information was presented to the user. 34 | readonly attribute DOMHighResTimeStamp? presentationTime; 35 | }; 36 | ``` 37 | 38 | * Note that `presentationTime` is optional: user-agents can opt to not expose them. 39 | 40 | ## Impact on current attributes 41 | Currently paint timing's `startTime` and element timing's `renderTime` use the presentation time if that's available (Chromium), and the paint time if it's not (WebKit/Gecko). 42 | In this proposal, this would be explicit: both paint timing's `startTime` and element timing's `renderTime` would return something like `presentationTime || paintTime`. 43 | This would keep compatibility with what's out there today, allowing progressive enhancement and returning the "best known value" from these attributes. 44 | 45 | ## Conclusion 46 | 47 | When exposing paint timings, we look for the right trade-off between "UX-precise" and "interoperable". 48 | By exposing those as two timestamps, and making one of them optional, we give web developers the information that can help them optimize, without compromising on interoperability. 49 | 50 | ## Security & privacy self review 51 | 52 | See [Self-Review Questionnaire: Security and Privacy](https://w3ctag.github.io/security-questionnaire/) 53 | 54 | ### 01. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? 55 | 56 | It exposes timing information of a platform/OS operation, a coarse approximation of "VSync". This is already exposed to some extend by the `requestAnimationFrame` callback timestamp. 57 | It is necessary in order to reflect to web developers the impact on user experience performance in practice, as other metrics offer approximations that lose too much information. 58 | 59 | #### 02. Do features in your specification expose the minimum amount of information necessary to enable their intended uses? 60 | 61 | Yes. 62 | 63 | #### 03. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? 64 | 65 | This feature does not deal with personal information. 66 | 67 | #### 04. How do the features in your specification deal with sensitive information? 68 | 69 | This feature does not deal with sensitive information. 70 | 71 | #### 05. Do the features in your specification introduce new state for an origin that persists across browsing sessions? 72 | 73 | No. This feature only applies to the current document. 74 | 75 | #### 06. Do the features in your specification expose information about the underlying platform to origins? 76 | 77 | To some extent, the timing of committing a frame is information about the underlying platform, like the refresh rate. 78 | However, this information is already exposed in other ways (the `requestAnimationFrame` callback timestamp), 79 | and in this specification it is coarsened on top of the usual coarsening, to avoid exposing meaningful information in terms of security/fingerprinting. 80 | 81 | #### 07. Does this specification allow an origin to send data to the underlying platform? 82 | 83 | No. 84 | 85 | #### 08. Do features in this specification allow an origin access to sensors on a user’s device? 86 | 87 | No. 88 | 89 | #### 09. What data do the features in this specification expose to an origin? Please also document what data is identical to data exposed by other features, in the same or different contexts. 90 | 91 | Timing information only. 92 | 93 | #### 10. Do feautres in this specification enable new script execution/loading mechanisms? 94 | 95 | No. 96 | 97 | #### 11. Do features in this specification allow an origin to access other devices? 98 | 99 | No. 100 | 101 | #### 12. Do features in this specification allow an origin some measure of control over a user agent's native UI? 102 | 103 | None. 104 | 105 | #### 13. What temporary identifiers do the features in this specification create or expose to the web? 106 | 107 | None. 108 | 109 | #### 14. How does this specification distinguish between behavior in first-party and third-party contexts? 110 | 111 | Timing information receives extra coarsening in documents that are not cross-origin isolated. 112 | Cross-origin isolation is more appropriate here than per-resource protections, as the same presentation timing is shared 113 | across all the resources presented in the same frame, be it cross-origin or same-origin resources. 114 | 115 | #### 15. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? 116 | 117 | The feature is unaffected by these modes. 118 | 119 | #### 16. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? 120 | 121 | Yes. 122 | 123 | #### 17. Do features in your specification enable origins to downgrade default security protections? 124 | 125 | Yes, using cross-origin isolation. 126 | 127 | #### 18. What should this questionnaire have asked? 128 | 129 | The questionnaire asked for sufficient information. 130 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 45211, 3 | "contacts": [ 4 | "yoavweiss" 5 | ], 6 | "shortName": "paint-timing", 7 | "repo-type": "rec-track" 8 | } --------------------------------------------------------------------------------