├── .github └── workflows │ └── build.yml ├── .gitignore ├── .pr-preview.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── index.bs └── w3c.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: make ci 17 | - name: Deploy 18 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} 19 | uses: peaceiris/actions-gh-pages@v3 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | publish_dir: ./out 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | index.html 2 | -------------------------------------------------------------------------------- /.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 | Contributions to this repository are intended to become part of Recommendation-track documents governed by the W3C Patent Policy and Software and Document License. To make substantive contributions to specifications, you must either participate in the relevant W3C Working Group or make a non-member patent licensing commitment. 2 | 3 | For our editing test-driven process, see CONTRIBUTING.md. 4 | 5 | If you are not the sole contributor to a contribution (pull request), please identify all contributors in the pull request comment. 6 | 7 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 8 | 9 | +@github_username 10 | If you added a contributor by mistake, you can remove them in a comment with: 11 | 12 | -@github_username 13 | If you are making a pull request on behalf of someone else but you had no part in designing the feature, you can remove yourself with the above syntax. 14 | 15 | # Tests 16 | 17 | For normative changes, a corresponding 18 | [web-platform-tests](https://github.com/web-platform-tests/wpt) PR is highly appreciated. Typically, 19 | both PRs will be merged at the same time. Note that a test change that contradicts the spec should 20 | not be merged before the corresponding spec change. If testing is not practical, please explain why 21 | and if appropriate [file a web-platform-tests issue](https://github.com/web-platform-tests/wpt/issues/new) 22 | to follow up later. Add the `type:untestable` or `type:missing-coverage` label as appropriate. 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | local: index.bs 4 | bikeshed --die-on=warning spec index.bs index.html 5 | 6 | index.html: index.bs 7 | @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \ 8 | --output index.html \ 9 | --write-out "%{http_code}" \ 10 | --header "Accept: text/plain, text/html" \ 11 | -F die-on=warning \ 12 | -F file=@index.bs) && \ 13 | [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ 14 | echo ""; cat index.html; echo ""; \ 15 | rm -f index.html; \ 16 | exit 22 \ 17 | ); 18 | 19 | remote: index.html 20 | 21 | ci: index.bs 22 | mkdir -p out 23 | make remote 24 | mv index.html out/index.html 25 | 26 | clean: 27 | rm index.html 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Element Timing: Explainer 2 | 3 | The **Element Timing** API enables monitoring when developer-specified image elements or groups of text nodes are displayed on screen. 4 | The way text nodes are grouped is described below. 5 | 6 | 7 | ### Objectives 8 | 9 | 1. Inform developers when specific elements are first displayed on the screen. 10 | We support the following types of images: \, \ inside \, and background-images. 11 | We also support observing certain groups of text nodes. Web developers can better understand which are the critical images and text of their sites, so after annotating them the browser can provide timing information about those. 12 | This image and text converage means that the majority of web content can be timed via this API. 13 | 1. Enable analytics providers to measure display time of key images or text, without explicit opt in from web developers. 14 | In many cases it's not feasible for developers to modify their HTML just to get better performance insights, so it's important to provide basic information even for websites that cannot annotate their elements. 15 | 16 | 17 | ### How do we register elements for observation? 18 | 19 | An element can be registered for observation via the `elementtiming` HTML attribute. 20 | Having this attribute set will signal the browser to expose render timing information for this element (its source image, background image, and/or text). 21 | It should be noted that setting the `elementtiming` attribute does not work retroactively: once an element has loaded and is rendered, setting the attribute will have no effect. 22 | Thus, it is strongly encouraged to set the attribute before the element is added to the document (in HTML, or if set on Javascript, before adding it to the document). 23 | Having the attribute implies that: 24 | 25 | * If the element is an image, there will be an entry for the image. 26 | * If the element is affected by (possibly multiple) background images, there will be one entry for each of those background images. 27 | * If the element is associated to at least one text node, there will be one entry for the group of associated text nodes. 28 | 29 | ### Image considerations 30 | 31 | We define the **image rendering timestamp** as the next paint that occurs after the image has become fully loaded. 32 | This is important to distinguish as progressively rendered images may have multiple initial renderings before the image has even been fully received by the user agent. 33 | 34 | Allowing third-party origins to measure the time an arbitrary image resource takes to render could expose sensitive information such as whether a user is logged into a website. 35 | Therefore, for privacy and security reasons, the [image rendering timestamp](#image-time) is only exposed in entries corresponding to resources that pass the [timing allow check](https://w3c.github.io/resource-timing/#dfn-timing-allow-check). 36 | However, to enable a more holistic picture, the rest of the information is exposed for arbitrary images. 37 | 38 | ### Text considerations 39 | 40 | We say that a text node **belongs to** the closest block-level [Element](https://dom.spec.whatwg.org/#element) ancestor of the node. 41 | This means that an element could have 0 or many associated text nodes with it. 42 | 43 | We say that an element is *text-painted* if at least one text node [belongs to](#belong) and has been painted at least once. 44 | Thus, the **text rendering timestamp** of an element is the time when it becomes *text-painted*. 45 | 46 | Let the *text rect* of a text node be the display rectangle of that node within the viewport. 47 | We define the **text rect** of an element as the smallest rectangle which contains the geometric union of the text rects of all text nodes which [belong to](#belong) the element. 48 | 49 | ### What information is exposed? 50 | 51 | A `PerformanceElementTiming` entry has the following attributes: 52 | * `name`: for images, "image-paint". For text: "text-paint". 53 | * `entryType`: it will always be the string "element". 54 | * `startTime`: equal to `renderTime` if it is nonzero, otherwise equal to `loadTime`. 55 | * `duration`: always be set to 0. 56 | * `renderTime`: for images, the [image rendering timestamp](#image-time), or 0 when the resource does not pass the [timing allow check](https://w3c.github.io/resource-timing/#dfn-timing-allow-check). For text, the [text rendering timestamp](#text-time). 57 | * `loadTime`: for images, the latest between the time when the image resource is loaded and the time when the image resource is associated to the element. For text, 0. 58 | * `intersectionRect`: for images, the display rectangle of the image within the viewport. For text, the [text rect](#text-rect) of the associated text (only counting text nodes which have been painted at least once). 59 | * `identifier`: the value of the `elementtiming` attribute of the element. 60 | * `naturalWidth`: the [intrinsic](https://drafts.csswg.org/css2/conform.html#intrinsic) width of the image. It matches with the corresponding DOM [attribute](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalwidth) for img. 0 for text. 61 | * `naturalHeight`: the [intrinsic](https://drafts.csswg.org/css2/conform.html#intrinsic) height of the image. It matches with the corresponding DOM [attribute](https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalheight) for img. 0 for text. 62 | * `id`: the element's ID. 63 | * `element`: points to the element. This will be "null" if the element is [disconnected](https://dom.spec.whatwg.org/#connected). 64 | * `url`: for images, the initial URL for the resource request. For text, this will be an empty string. 65 | 66 | Note: for background images, the element is the one being affected by the background image style. 67 | 68 | Sample code: 69 | 70 | ```javascript 71 | 72 | 73 | const observer = new PerformanceObserver((list) => { 74 | let perfEntries = list.getEntries().forEach(function(entry) { 75 | // Send the information to analytics, or in this case just log it to console. 76 | if (entry.identifier === 'foobar') { 77 | if (entry.renderTime) 78 | console.log("My image took " + entry.renderTime + " to render!"); 79 | else 80 | console.log("Rendering time not available. Image took " + entry.loadTime + " to load!"); 81 | } 82 | }); 83 | }); 84 | observer.observe({entryTypes: ['element']}); 85 | ``` 86 | 87 | ### Questions 88 | 89 | #### What about Shadow DOM? 90 | 91 | Unfortunately Shadow DOM elements are not currently supported. 92 | 93 | #### What about invisible or occluded elements? 94 | 95 | The entry creation might be affected by _visibility_: for instance, elements are not exposed if the style visibility is set to none, or the opacity is 0. 96 | However, occlusion will not affect entry creation: an entry is seen if the element is there, but hidden by a full-screen pop-up on the page. 97 | 98 | #### What are some differences between text and image observation? 99 | 100 | * Whereas for images it is OK to care only about those which are associated to a resource, for text that is definitely not the case. 101 | An image not associated to a resource will need to be constructed manually and it is uncommon for such an image to be part of the key content of a website. 102 | Note that we consider inline images (those with data URIs) to have an associated resource, it is just inlined. 103 | On the other hand, text is relevant regardless of whether it is associated to a webfont or not. 104 | 105 | * Image rendering steps are different from text rendering steps. 106 | For images, initial paints may not include all of the image content and may be low quality. 107 | It is only once we have fully loaded and decoded the image that we can be certain that the content being displayed is meaningful. 108 | In contrast, any text painted is meaningful. 109 | It should be noted that this is not perfect because there could be block level elements containing some text that renders quickly and some text that is blocked on webfonts. 110 | In this case, for the purposes of Element Timing, the text blocked by webfonts will be completely ignored. 111 | 112 | * Text is actually simpler when it comes to security and privacy considerations. 113 | Cross-origin images could reveal a lot of information about users, but text and webfonts embedded in a website do not. 114 | Therefore, whereas for images we had to consider resources that passed the timing allow check versus resources that did not, for text this distinction is not needed. 115 | 116 | -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 | 16 | 17 |
 18 | urlPrefix: https://w3c.github.io/performance-timeline/; spec: PERFORMANCE-TIMELINE-2;
 19 |     type: interface; url: #the-performanceentry-interface; text: PerformanceEntry;
 20 |     type: attribute; for: PerformanceEntry;
 21 |         text: name; url: #dom-performanceentry-name;
 22 |         text: entryType; url: #dom-performanceentry-entrytype;
 23 |         text: startTime; url: #dom-performanceentry-starttime;
 24 |         text: duration; url: #dom-performanceentry-duration;
 25 |     type: dfn; url: #dfn-queue-a-performanceentry; text: queue the PerformanceEntry;
 26 |     type: interface; url: #the-performanceobserver-interface; text: PerformanceObserver;
 27 |     type: attribute; for: PerformanceObserver;
 28 |         text: supportedEntryTypes; url: #supportedentrytypes-attribute;
 29 | urlPrefix: https://w3c.github.io/IntersectionObserver; spec: INTERSECTION-OBSERVER;
 30 |     type: dfn; url: #calculate-intersection-rect-algo; text: intersection rect algorithm;
 31 | urlPrefix: https://html.spec.whatwg.org/multipage; spec: HTML;
 32 |     type: dfn; url: //webappapis.html#event-loop-processing-model; text: event loop processing model;
 33 | urlPrefix: https://drafts.csswg.org/css-backgrounds-3; spec: CSS-BACKGROUNDS-3;
 34 |     type: dfn; url: #propdef-background-image; text: background-image;
 35 | urlPrefix: https://wicg.github.io/largest-contentful-paint/; spec: LARGEST-CONTENTFUL-PAINT;
 36 |     type: dfn; url:#potentially-add-a-largestcontentfulpaint-entry; text: potentially add a LargestContentfulPaint entry;
 37 | urlPrefix: https://w3c.github.io/paint-timing/; spec: PAINT-TIMING;
 38 |     type: dfn; url:#set-of-owned-text-nodes; text: set of owned text nodes;
 39 |     type: dfn; url:#process-image-that-finished-loading; text: process image that finished loading;
 40 |     type: dfn; url:#timing-eligible; text: timing-eligible
 41 |     type: dfn; url:#the-paint-timing-steps; text: the paint timing steps
 42 |     type: dfn; url:#exposed-for-paint-timing; text: exposed for paint timing
 43 |     type: dfn; url:#pending-image-record; text: pending image record
 44 |     type: dfn; for:pending image record; url:#pending-image-record-element; text: element
 45 |     type: dfn; for:pending image record; url:#pending-image-record-loadtime; text: loadTime
 46 |     type: dfn; for:pending image record; url:#pending-image-record-request; text: request
 47 |     type: dfn; url:#paint-timing-info; text: paint timing info
 48 |     type: dfn; for: paint timing info; text: default paint timestamp;
 49 |     type: dfn; for:PaintTimingMixin; url:#painttimingmixin-paint-timing-info; text: paint timing info
 50 |     type: interface; text:PaintTimingMixin; url:#painttimingmixin
 51 | 
52 | 53 | 56 | 57 | Introduction {#sec-intro} 58 | ===================== 59 | 60 | This section is non-normative. 61 | 62 | Knowing when critical elements are displayed on screen is key to understanding page load performance. 63 | While fast rendering of the essential components is not sufficient for a satisfactory loading experience, it is necessary. 64 | Therefore, monitoring these rendering timestamps is important to improve and prevent regressions in page loads. 65 | 66 | This specification gives developers and analytics providers an API to measure rendering timestamps of critical elements. 67 | There is currently no good way to measure these timestamps for real users. 68 | Existing approaches would require either registering observers too early or significant DOM manipulation. 69 | These approaches are discussed on the [[#sec-security]] section. 70 | 71 | 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. 72 | Thus, this API exposes rendering timing information about web-developer-annotated elements. 73 | 74 | Elements exposed {#sec-elements-exposed} 75 | ------------------------ 76 | 77 | The Element Timing API supports timing information about timing-eligible 78 | elements, as defined by [[PAINT-TIMING]]. 79 | 80 | Elements that have a "elementtiming" content attribute are reported in the report image element timing and the report text element timing algorithms. 81 | 82 | Usage example {#sec-example} 83 | ------------------------ 84 | 85 | The following example shows an image that is registered for observation via its elementtiming attribute, and an observer gathering the timing information. 86 | 87 | 88 | <img... elementtiming='foobar'/> 89 | <p elementtiming='important-paragraph'>This is text I care about.</p> 90 | ... 91 | <script> 92 | const observer = new PerformanceObserver((list) => { 93 | let perfEntries = list.getEntries(); 94 | // Process the entries by iterating over them. 95 | }); 96 | observer.observe({type: 'element', buffered: true}); 97 | </script> 98 | 99 | 100 | The following are sample elements whose rendering timestamps could be measured by using this API and which should be compared to page navigation: 101 | * The images in the image carousel of a shopping site. 102 | * The main photo in a story of a news site. 103 | * The title of a blog post. 104 | * The first paragraph in an entry of an encyclopedia site. 105 | 106 | The API could have use cases outside of page load by comparing the rendering timestamps with input timestamps. 107 | For example, developers could monitor the time it takes for a widget to show up after a click that triggers it. 108 | 109 | Element Timing {#sec-element-timing} 110 | ======================================= 111 | 112 | Element Timing involves the following new interfaces: 113 | 114 | {{PerformanceElementTiming}} interface {#sec-performance-element-timing} 115 | ------------------------------------------------------------------------ 116 | 117 |
118 | [Exposed=Window]
119 | interface PerformanceElementTiming : PerformanceEntry {
120 |     readonly attribute DOMHighResTimeStamp renderTime;
121 |     readonly attribute DOMHighResTimeStamp loadTime;
122 |     readonly attribute DOMRectReadOnly intersectionRect;
123 |     readonly attribute DOMString identifier;
124 |     readonly attribute unsigned long naturalWidth;
125 |     readonly attribute unsigned long naturalHeight;
126 |     readonly attribute DOMString id;
127 |     readonly attribute Element? element;
128 |     readonly attribute USVString url;
129 |     [Default] object toJSON();
130 | };
131 | 
132 | PerformanceElementTiming includes PaintTimingMixin;
133 | 
134 | 135 | A {{PerformanceElementTiming}} object reports timing information about one associated element. 136 | 137 | Each {{PerformanceElementTiming}} object has these associated concepts, all of which are initially set to null: 138 | * A request containing the image request (if the entry is for image content). 139 | * An element containing the associated {{Element}}. 140 | 141 | The associated concepts and some attributes for {{PerformanceElementTiming}} are specified in the processing model in [[#sec-report-image-element]] and [[#sec-report-text]]. 142 | 143 | The {{PerformanceEntry/entryType}} attribute's getter must return the {{DOMString}} "element". 144 | 145 | The {{PerformanceEntry/name}} attribute's getter must return the value it was initialized to. 146 | 147 | The {{PerformanceEntry/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. 148 | 149 | The {{PerformanceEntry/duration}} attribute's getter must return 0. 150 | 151 | The {{PerformanceElementTiming/renderTime}} attribute getter step is to return the [=default paint timestamp=] given [=this=]'s [=PaintTimingMixin/paint timing info=]. 152 | 153 | The {{PerformanceElementTiming/loadTime}} attribute's getter must return the the value it was initialized to. 154 | 155 | The {{PerformanceElementTiming/intersectionRect}} attribute must return the value it was initialized to. 156 | 157 | The {{PerformanceElementTiming/identifier}} attribute's getter must return the value it was initialized to. 158 | 159 | The {{PerformanceElementTiming/naturalWidth}} attribute must return the value it was initialized to. 160 | 161 | The {{PerformanceElementTiming/naturalHeight}} attribute must return the value it was initialized to. 162 | 163 | The {{PerformanceElementTiming/id}} attribute's getter must return the value it was initialized to. 164 | 165 | The {{PerformanceElementTiming/element}} attribute's getter must perform the following steps: 166 |
167 | 1. If this's element is not [=exposed for paint timing=] given null, return null. 168 | 1. Return this's element. 169 |
170 | 171 | Note: This means that an element that is no longer descendant of the {{Document}} will no longer be returned by {{PerformanceElementTiming/element}}'s attribute getter. 172 | 173 | The {{PerformanceElementTiming/url}} attribute's getter must perform the following steps: 174 |
175 | 1. If this's request is null, return the empty string. 176 | 1. Let |urlString| be this's request's current URL. 177 | 1. Let |url| be the result of parsing |urlString|. 178 | 1. If |url|'s scheme is "`data`", trim |urlString| to its first 100 characters. 179 | 1. Return |urlString|. 180 |
181 | 182 | Note: The URL is trimmed for data URLs to avoid excessive memory in the entry. 183 | 184 | Processing model {#sec-processing-model} 185 | ======================================== 186 | 187 | Note: A user agent implementing the Element Timing API would need to include "element" in {{PerformanceObserver/supportedEntryTypes}} for {{Window}} contexts. 188 | This allows developers to detect support for element timing. 189 | 190 | Modifications to the DOM specification {#sec-modifications-DOM} 191 | -------------------------------------------------------- 192 | 193 | This section will be removed once the [[DOM]] specification has been modified. 194 | 195 | We extend the {{Element}} interface as follows: 196 | 197 |
198 | partial interface Element {
199 |     [CEReactions] attribute DOMString elementTiming;
200 | };
201 | 
202 | 203 | The {{Element/elementTiming}} attribute must reflect the element's "elementtiming" content attribute. 204 | 205 | Report Element Timing {#sec-report-element-timing} 206 | -------------------------------------------------- 207 | 208 |
209 | When asked to report element timing given a {{Document}} |doc|, a [/=paint timing info=] |paintTimingInfo|, an [=ordered set=] of [=pending image records=] |paintedImages|, and an [=ordered set=] of [=/elements=] |paintedTextNodes|, perform the following steps: 210 | 211 | 1. For each |record| in |paintedImages|: 212 | 1. Run the report image element timing algorithm passing in |record|, |paintTimingInfo|, and |doc|. 213 | 1. For each {{Element}} |element| in |paintedTextNodes|: 214 | 1. Run the report text element timing given |element|, |paintTimingInfo|, and |doc|. 215 |
216 | 217 | Report Image Element Timing {#sec-report-image-element} 218 | -------------------------------------------------------- 219 | 220 |
221 | When asked to report image element timing given a [=pending image record=] |record|, a [=/paint timing info=] |paintTimingInfo| and a {{Document}} |document|, perform the following steps: 222 | 223 | 1. If |record|'s [=pending image record/element=]'s "elementtiming" content attribute is absent, then abort these steps. 224 | 1. Let |intersectionRect| be the value returned by the intersection rect algorithm using |record|'s [=pending image record/element=] as the target and viewport as the root. 225 | 1. Create and initialize a {{PerformanceElementTiming}} object |entry| with |document|'s [=relevant realm=], whose [=PaintTimingMixin/paint timing info=] is |paintTimingInfo|. 226 | 1. Initialize |entry|'s request to |record|'s [=pending image record/request=]. 227 | 1. Initialize |entry|'s element to |record|'s [=pending image record/element=]. 228 | 1. Initialize |entry|'s {{PerformanceEntry/name}} to the {{DOMString}} "image-paint". 229 | 1. Initialize |entry|'s {{loadTime}} to |record|'s [=pending image record/loadTime=]. 230 | 1. Initialize |entry|'s {{intersectionRect}} to |intersectionRect|. 231 | 1. Initialize |entry|'s {{identifier}} to |record|'s [=pending image record/element=]'s "elementtiming" content attribute. 232 | 1. Initialize |entry|'s {{PerformanceElementTiming/naturalWidth}} and {{PerformanceElementTiming/naturalHeight}} by running the same steps for an <{img}>'s {{HTMLImageElement/naturalWidth}} and {{HTMLImageElement/naturalHeight}} attribute getters, but using |record|'s [=pending image record/request=] as the image. 233 | 1. Initialize |entry|'s {{id}} to |record|'s [=pending image record/element=]'s "id" content attribute. 234 | 1. Queue the PerformanceEntry |entry|. 235 |
236 | 237 | Report Text Element Timing {#sec-report-text} 238 | -------------------------------------------------------- 239 | 240 |
241 | When asked to report text element timing given an {{Element}} |element|, a [=/paint timing info=] |paintTimingInfo| and a {{Document}} |document|, perform the following steps: 242 | 243 | 1. If |element|'s "elementtiming" content attribute is absent, then abort these steps. 244 | 1. Let |intersectionRect| be an empty rectangle. 245 | 1. For each {{Text}} node |text| in |element|'s set of owned text nodes: 246 | 1. Augment |intersectionRect| to be smallest rectangle containing the border box of |text| and |intersectionRect|. 247 | 1. Intersect |intersectionRect| with the visual viewport. 248 | 1. Create and initialize a {{PerformanceElementTiming}} object |entry| with |document|'s [=relevant realm=], whose [=PaintTimingMixin/paint timing info=] is |paintTimingInfo|. 249 | 1. Initialize |entry|'s element to |element|. 250 | 1. Initialize |entry|'s {{PerformanceEntry/name}} to the {{DOMString}} "text-paint". 251 | 1. Initialize |entry|'s {{loadTime}} to 0. 252 | 1. Initialize |entry|'s {{intersectionRect}} to |intersectionRect|. 253 | 1. Initialize |entry|'s {{identifier}} to |element|'s "elementtiming" content attribute. 254 | 1. Initialize |entry|'s {{PerformanceElementTiming/naturalWidth}} and {{PerformanceElementTiming/naturalHeight}} to 0. 255 | 1. Initialize |entry|'s {{id}} to |element|'s "id" content attribute. 256 | 1. Queue the PerformanceEntry |entry|. 257 |
258 | 259 | Security & privacy considerations {#sec-security} 260 | =============================================== 261 | 262 | This API exposes some information about cross-origin images. 263 | In particular, images have their resource load time exposed, which could be a source of privacy concerns. 264 | 265 | However, this is considered to not add new attacks to the web platform because the ResourceTiming API exposes a similar timestamp already. 266 | In addition, the onload handler exposes load timing when it is available, and the resource load time is a close proxy to this. 267 | The current high resolution time computed at the beginning of the onload handler would provide the image load time. 268 | We choose to expose the {{loadTime}} because it is very easy to obtain even without an onload handler. 269 | 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. 270 | 271 | The {{renderTime}} (display timestamp) is indeed newly exposed information. Implementations are advised to coarsen that timestamp further, 272 | to a 4 milliseconds resolution at least, to avoid exposing differences in decoding time between cross-origin images. Note that other checks, 273 | such as `Timing-Allow-Origin`, does not work here due to same-origin and cross-origin images being rendered at the same time. 274 | Exposing a coarse {{renderTime}} is anyway not a substantial attack vector, given that image [=natural size=] and loading time are exposed in other ways. 275 | 276 | 277 | // In the attacker frame. 278 | <iframe src=attack.html></iframe> 279 | <script> 280 | window.onmessage = e => { 281 | let timestamp = e.data; 282 | // Acquired the display timestamp for 'victim.jpg'! 283 | } 284 | </script> 285 | 286 | // In the attack.html iframe. 287 | <img src='victim.jpg'/> 288 | <script> 289 | // Wait until onload or some time when the PaintTiming entries will be visible. 290 | onload() => { 291 | let entry = performance.getEntriesByType('paint')[0]; 292 | top.postMessage(entry.startTime, '*'); 293 | } 294 | </script> 295 | 296 | 297 | The other nontrivial parameter being exposed here is the {{intersectionRect}}. 298 | This can already be polyfilled, for example using {{IntersectionObserver}}. 299 | The polyfill process would be similar: add an {{IntersectionObserver}} on the onload handler of the target image or text content. 300 | 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. 301 | 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. 302 | 303 | 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. 304 | Suppose we waited and exposed all the entries during the report element timing algorithm. 305 | An attacker could infer nontrivial information about the rendering timestamp of an image. 306 | It would do so by only observing the timing for that image. 307 | Even though the timestamp is not exposed as a member of the {{PerformanceElementTiming}} entry received, 308 | 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. 309 | This would unintentionally leak some of the display timing of the image. 310 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 45211, 3 | "contacts": [ 4 | "caribouW3" 5 | ], 6 | "shortName": "element-timing", 7 | "repo-type": "rec-track" 8 | } 9 | --------------------------------------------------------------------------------