Skip to content

Commit

Permalink
Refactor Element Timing on top of Paint Timing (#78)
Browse files Browse the repository at this point in the history
This updates Element Timing to use the common timing algorithms exposed in Paint Timing, so that it is more closely aligned with that spec. At the same time, Element Timing no longer drives Largest Contentful Paint, as that API now relies on Paint TIming directly instead (with the PR in w3c/largest-contentful-paint#121)

This also moves npm@chromium.org to "Former Editor"
  • Loading branch information
clelland committed Jan 16, 2024
1 parent 1411766 commit a16851f
Showing 1 changed file with 41 additions and 114 deletions.
155 changes: 41 additions & 114 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Status: CG-DRAFT
Shortname: element-timing
Group: WICG
Level: 1
Editor: Nicolás Peña Moreno, Google https://google.com, npm@chromium.org
Editor: Ian Clelland, Google https://google.com, iclelland@chromium.org, w3cid 76841
Tim Dresser, Google https://google.com, tdresser@chromium.org
Former Editor: Nicolás Peña Moreno, Google https://google.com, npm@chromium.org
URL: https://wicg.github.io/element-timing
Repository: https://github.com/WICG/element-timing
Test Suite: https://github.com/web-platform-tests/wpt/tree/master/element-timing
Expand All @@ -28,16 +29,23 @@ urlPrefix: https://w3c.github.io/performance-timeline/; spec: PERFORMANCE-TIMELI
urlPrefix: https://w3c.github.io/IntersectionObserver; spec: INTERSECTION-OBSERVER;
type: dfn; url: #calculate-intersection-rect-algo; text: intersection rect algorithm;
urlPrefix: https://html.spec.whatwg.org/multipage; spec: HTML;
type: dfn; url: /images.html#img-all; text: completely available;
type: dfn; url: //webappapis.html#event-loop-processing-model; text: event loop processing model;
urlPrefix: https://drafts.csswg.org/css-backgrounds-3; spec: CSS-BACKGROUNDS-3;
type: dfn; url: #propdef-background-image; text: background-image;
urlPrefix: https://drafts.csswg.org/css2/zindex.html; spec: CSS;
type: dfn; url:#painting-order; text: painting order;
urlPrefix: https://wicg.github.io/largest-contentful-paint/; spec: LARGEST-CONTENTFUL-PAINT;
type: dfn; url:#potentially-add-a-largestcontentfulpaint-entry; text: potentially add a LargestContentfulPaint entry;
urlPrefix: https://fetch.spec.whatwg.org/; spec: FETCH;
type: dfn; url:#concept-tao-check; text: timing allow check;
urlPrefix: https://w3c.github.io/paint-timing/; spec: PAINT-TIMING;
type: dfn; url:#set-of-owned-text-nodes; text: set of owned text nodes;
type: dfn; url:#process-image-that-finished-loading; text: process image that finished loading;
type: dfn; url:#timing-eligible; text: timing-eligible
type: dfn; url:#the-paint-timing-steps; text: the paint timing steps
type: dfn; url:#exposed-for-paint-timing; text: exposed for paint timing
type: dfn; url:#pending-image-record; text: pending image record
type: dfn; for:pending image record; url:#pending-image-record-element; text: element
type: dfn; for:pending image record; url:#pending-image-record-loadtime; text: loadTime
type: dfn; for:pending image record; url:#pending-image-record-request; text: request
</pre>
<pre class=link-defaults>
spec:dom; type:dfn; text:descendant
Expand All @@ -63,12 +71,8 @@ Thus, this API exposes rendering timing information about web-developer-annotate
Elements exposed {#sec-elements-exposed}
------------------------

The Element Timing API supports timing information about the following elements:
* <{img}> elements.
* <{image}> elements inside an <{svg}>.
* The poster images of <{video}> elements.
* Elements with a <a>background-image</a>.
* Groups of text nodes, which are aggregated as described in [[#sec-modifications-CSS]].
The Element Timing API supports timing information about <a>timing-eligible</a>
elements, as defined by [[PAINT-TIMING]].

Elements that have a "<code>elementtiming</code>" content attribute are reported in the <a>report image element timing</a> and the <a>report text element timing</a> algorithms.

Expand Down Expand Up @@ -153,7 +157,11 @@ The {{PerformanceElementTiming/naturalHeight}} attribute must return the value i

The {{PerformanceElementTiming/id}} attribute's getter must return the value it was initialized to.

The {{PerformanceElementTiming/element}} attribute's getter must run the [=get an element=] algorithm <a>this</a>'s <a>element</a> and null as inputs.
The {{PerformanceElementTiming/element}} attribute's getter must perform the following steps:
<div algorithm="PerformanceElementTiming element">
1. If <a>this</a>'s <a>element</a> is not [=exposed for paint timing=] given null, return null.
1. Return <a>this</a>'s <a>element</a>.
</div>

Note: This means that an element that is no longer <a>descendant</a> of the {{Document}} will no longer be returned by {{PerformanceElementTiming/element}}'s attribute getter.

Expand Down Expand Up @@ -189,101 +197,50 @@ partial interface Element {

The {{Element/elementTiming}} attribute must <a>reflect</a> the element's "<code>elementtiming</code>" content attribute.

Each {{Element}} has an <dfn>associated image request</dfn> which is initially null.
When the processing model for an {{Element}} <em>element</em> of type {{HTMLImageElement}}, {{SVGImageElement}}, or {{HTMLVideoElement}} creates a new image resource (e.g., to be displayed as an image or poster image), <em>element</em>'s <a>associated image request</a> is set to the <a>image request</a> of the created image resource.

Note: Every image resource that is obtained from a URL whose <a spec=url>scheme</a> is equal to "data" has an associated <a>image request</a> which is not fetched but still needs to be loaded.
This request can be the <a>associated image request</a> of an {{Element}}.

Note: The current language is vague since it does not point to specific algorithms.
This can be made more rigorous when the corresponding processing models have a more unified processing model.

Each {{Element}} has a <dfn>set of owned text nodes</dfn>, which is initially an empty <a>ordered set</a>.

Each {{Document}} has <dfn>images pending rendering</dfn>, a list of triples ({{Element}}, <a>image request</a>, DOMHighResTimeStamp) which are considered loaded but not yet rendered.
When an {{Element}} <em>element</em>'s <a>associated image request</a> has become <a>completely available</a>, run the algorithm to <a>process an image that finished loading</a> passing in <em>element</em> and its <a>associated image request</a> as inputs.

Each {{Document}} also has a <dfn>set of elements with rendered text</dfn>, which is initially an empty, <a>ordered set</a>.

Modifications to the CSS specification {#sec-modifications-CSS}
--------------------------------------------------------

When the user agent is executing the <a>painting order</a>, it must populate the <a>set of owned text nodes</a> of the painted {{Element|Elements}} so that the following is true:

<div algorithm="text aggregation">
* If a {{Text}} object |text| will not be painted due to the font face being in its <a>font block period</a>, then it is not <a for="set">appended</a> to the <a>set of owned text nodes</a> of any {{Element}}.
* Otherwise, |text| is <a for="set">appended</a> to the <a>set of owned text nodes</a> of the {{Element}} which determines the <a>containing block</a> of |text|.
</div>

NOTE: A user agent might want to use a stack to efficiently compute the <a>set of owned text nodes</a> while implementing the <a>painting order</a>.

Every {{Element}} has a list of <dfn>associated background image requests</dfn> which is initially an empty array.
When the processing model for the {{Element}} <em>element</em>'s style requires a new image resource (to be displayed as background image), the <a>image request</a> created by the new resource is appended to <em>element</em>'s <a>associated background image requests</a>.
Whenever an <a>image request</a> in an {{Element}} <em>element</em>'s <a>associated background image requests</a> becomes <a>completely available</a>, run the algorithm to <a>process an image that finished loading</a> with <em>element</em> and <a>image request</a> as inputs.

NOTE: we assume that there is one <a>image request</a> for each {{Element}} that a <a>background-image</a> property affects and for each URL that the <a>background-image</a> property specifies.
So, for example, if there is a style with two URLs affecting all <{div}>s, and there are two <{div}>s, then there will be four <a>image requests</a>.
This means that a single <a>background-image</a> property could produce multiple {{PerformanceElementTiming}} entries because it can affect multiple elements and because it can specify multiple URLs.

Modifications to the HTML specification {#sec-modifications-HTML}
--------------------------------------------------------

<em>This section will be removed once the [[HTML]] specification has been modified.</em>

In the <a>update the rendering</a> step of the <a>event loop processing model</a>, add the following substep at the end:
Report Element Timing {#sec-report-element-timing}
--------------------------------------------------

1. For each <a>fully active</a> {{Document}} in <em>docs</em>, run the <a>element timing processing</a> algorithm passing in the {{Document}} and <em>now</em>.
<div export algorithm="report element timing">
When asked to <dfn>report element timing</dfn> given a {{Document}} |doc|, a timestamp |now|, an [=ordered set=] of [=pending image records=] |paintedImages|, and an [=ordered set=] of [=/elements=] |paintedTextNodes|, perform the following steps:

Process image that finished loading {#sec-process-loaded-image}
--------------------------------------------------------

<div algorithm="image element loaded">
To <dfn>process an image that finished loading</dfn> given |element| and |imageRequest| as inputs:
1. Let |element| be the input {{Element}}.
1. Let |imageRequest| be |element|'s <a>associated image request</a>.
1. Let |root| be |element|'s <a for="tree">root</a>.
1. If |root| is not a {{Document}}, return.
1. Let |now| be the <a>current high resolution time</a> given |element|'s <a>relevant global object</a>.
1. If |imageRequest| is not a data URL [[RFC2397]] and if the <a>timing allow check</a> fails for |imageRequest|'s resource, run the <a>report image element timing</a> algorithm, passing in the triple (|element|, |imageRequest|, |now|), 0, and |root| as inputs.
1. Otherwise, add the triple (|element|, |imageRequest|, |now|) to |root|'s <a>images pending rendering</a>.
1. For each |record| in |paintedImages|:
1. Run the <a>report image element timing</a> algorithm passing in |record|, |now|, and |doc|.
1. For each {{Element}} |element| in |paintedTextNodes|:
1. Run the <a>report text element timing</a> given |element|, |now|, and |doc|.
</div>

Report image Element Timing {#sec-report-image-element}
Report Image Element Timing {#sec-report-image-element}
--------------------------------------------------------

<div algorithm="report image element timing">
When asked to <dfn>report image element timing</dfn> given a triple (|element|, |imageRequest|, |loadTime|), a DOMHighResTimestamp |renderTime| and a {{Document}} |document|, perform the following steps:
When asked to <dfn>report image element timing</dfn> given a [=pending image record=] |record|, a DOMHighResTimestamp |renderTime| and a {{Document}} |document|, perform the following steps:

1. Let |intersectionRect| be the value returned by the <a>intersection rect algorithm</a> using |element| as the target and viewport as the root.
1. Let |exposedElement| be the result of running [=get an element=] with |element| and |document| as input.
1. If |exposedElement| is not null, call the <a>potentially add a LargestContentfulPaint entry</a> algorithm with |intersectionRect|, |imageRequest|, |renderTime|, |loadTime|, |element|, and |document|.
1. If |element|'s "<code>elementtiming</code>" content attribute is absent, then abort these steps.
1. Create and initialize a {{PerformanceElementTiming}} object |entry|.
1. Initialize |entry|'s <a>request</a> to |imageRequest|.
1. Initialize |entry|'s <a>element</a> to |element|.
1. If |record|'s [=pending image record/element=]'s "<code>elementtiming</code>" content attribute is absent, then abort these steps.
1. Let |intersectionRect| be the value returned by the <a>intersection rect algorithm</a> using |record|'s [=pending image record/element=] as the target and viewport as the root.
1. Create and initialize a {{PerformanceElementTiming}} object |entry| with |document|'s [=relevant realm=].
1. Initialize |entry|'s <a>request</a> to |record|'s [=pending image record/request=].
1. Initialize |entry|'s <a>element</a> to |record|'s [=pending image record/element=].
1. Initialize |entry|'s {{PerformanceEntry/name}} to the {{DOMString}} "image-paint".
1. Initialize |entry|'s {{renderTime}} to |renderTime|.
1. Initialize |entry|'s {{loadTime}} to |loadTime|.
1. Initialize |entry|'s {{loadTime}} to |record|'s [=pending image record/loadTime=].
1. Initialize |entry|'s {{intersectionRect}} to |intersectionRect|.
1. Initialize |entry|'s {{identifier}} to |element|'s "<code>elementtiming</code>" content attribute.
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 |imageRequest| as the image.
1. Initialize |entry|'s {{id}} to |element|'s "<code>id</code>" content attribute.
1. Initialize |entry|'s {{identifier}} to |record|'s [=pending image record/element=]'s "<code>elementtiming</code>" content attribute.
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.
1. Initialize |entry|'s {{id}} to |record|'s [=pending image record/element=]'s "<code>id</code>" content attribute.
1. <a>Queue the PerformanceEntry</a> |entry|.
</div>

Report text Element Timing {#sec-report-text}
Report Text Element Timing {#sec-report-text}
--------------------------------------------------------

<div algorithm="report text element timing">
When asked to <dfn>report text element timing</dfn> given an {{Element}} |element|, a DOMHighResTimestamp |renderTime| and a {{Document}} |document|, perform the following steps:

1. If |element|'s "<code>elementtiming</code>" content attribute is absent, then abort these steps.
1. Let |intersectionRect| be an empty rectangle.
1. For each {{Text}} <a>node</a> |text| in |element|'s <a>set of owned text nodes</a>:
1. Augment |intersectionRect| to be smallest rectangle containing the border box of |text| and |intersectionRect|.
1. Intersect |intersectionRect| with the visual viewport.
1. Let |exposedElement| be the result of running [=get an element=] with |element| and |document| as input.
1. If |exposedElement| is not null, call the <a>potentially add a LargestContentfulPaint entry</a> algorithm with |intersectionRect|, null, |renderTime|, 0, |exposedElement|, and |document|.
1. If |element|'s "<code>elementtiming</code>" content attribute is absent, then abort these steps.
1. Create and initialize a {{PerformanceElementTiming}} object |entry| with |document|'s [=relevant realm=].
1. Initialize |entry|'s <a>element</a> to |element|.
1. Initialize |entry|'s {{PerformanceEntry/name}} to the {{DOMString}} "text-paint".
Expand All @@ -296,36 +253,6 @@ Report text Element Timing {#sec-report-text}
1. <a>Queue the PerformanceEntry</a> |entry|.
</div>

Element Timing processing {#sec-element-processing}
--------------------------------------------------------

<div algorithm="process element timing">
The <dfn>element timing processing</dfn> algorithm receives a {{Document}} |doc| and a timestamp |now| and performs the following steps:

1. For each |imagePendingRenderingTriple| in |doc|'s <a>images pending rendering</a> list:
1. Let |imageRequest| be the <a>image request</a> of |imagePendingRenderingTriple|.
1. If |imageRequest| is fully decoded, then run the following steps:
1. Run the <a>report image element timing</a> algorithm passing in |imagePendingRenderingTriple|, |now|, and |doc|.
1. Remove |imagePendingRenderingTriple| from |doc|'s <a>images pending rendering</a> list.
1. For each {{Element}} |element| in |doc|'s <a>descendants</a>:
1. If |element| is contained in |doc|'s <a>set of elements with rendered text</a>, continue.
1. If |element|'s <a>set of owned text nodes</a> is empty, continue.
1. <a for="set">Append</a> |element| to |doc|'s <a>set of elements with rendered text</a>.
1. Run the <a>report text element timing</a> given |element|, |now|, and |doc|.
</div>

Get an element algorithm {#sec-get-an-element}
---------------------------------

<div algorithm="PerformanceElementTiming element">
When asked to run the <dfn>get an element</dfn> algorithm with {{Element}} |element| and {{Document}} |document| as inputs, run these steps:
1. If |element| is not <a>connected</a>, return <code>null</code>.
1. Let |settings| be <a>this</a>'s <a>relevant settings object</a>.
1. if |document| is null, let |document| be |settings|'s <a>relevant global object</a>'s <a>associated <code>Document</code></a>.
1. If |element|'s <a for="tree">root</a> is not equal to |document| or if |document| is not [=fully active=], return <code>null</code>.
1. Return |element|.
</div>

Security & privacy considerations {#sec-security}
===============================================

Expand Down Expand Up @@ -375,7 +302,7 @@ Computing it at this point in time allows exposing the entry at that time.
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.

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.
Suppose we waited and exposed all the entries during the <a>element timing processing</a> algorithm.
Suppose we waited and exposed all the entries during the <a>report element timing</a> algorithm.
An attacker could infer nontrivial information about the rendering timestamp of an image.
It would do so by only observing the timing for that image.
Even though the timestamp is not exposed as a member of the {{PerformanceElementTiming}} entry received,
Expand Down

0 comments on commit a16851f

Please sign in to comment.