1. Introduction
This section is non-normative.
Knowing when critical elements are displayed on screen is key to understanding page load performance. While fast rendering of the essential components is not sufficient for a satisfactory loading experience, it is necessary. Therefore, monitoring these rendering timestamps is important to improve and prevent regressions in page loads.
This specification gives developers and analytics providers an API to measure rendering timestamps of critical elements. There is currently no good way to measure these timestamps for real users. Existing approaches would require either registering observers too early or significant DOM manipulation. These approaches are discussed on the § 4 Security & privacy considerations section.
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. Thus, this API exposes rendering timing information about web-developer-annotated elements.
1.1. Elements exposed
The Element Timing API supports timing information about the following elements:
-
imgelements. -
The poster images of
videoelements. -
Elements with a background-image .
-
Groups of text nodes, which are aggregated as described in § 3.2 Modifications to the CSS specification .
Elements
that
have
a
"
elementtiming
"
content
attribute
are
reported
in
the
report
image
element
timing
and
the
report
text
element
timing
algorithms.
1.2. Usage example
The
following
example
shows
an
image
that
is
registered
for
observation
via
its
elementtiming
attribute,
and
an
observer
gathering
the
timing
information.
< img... elementtiming = 'foobar' /> < p elementtiming = 'important-paragraph' > This is text I care about.</ p > ...< script > const observer= new PerformanceObserver(( list) => { let perfEntries= list. getEntries(); // Process the entries by iterating over them. }); observer. observe({ type: 'element' , buffered: true }); </ script >
The following are sample elements whose rendering timestamps could be measured by using this API and which should be compared to page navigation:
-
The images in the image carousel of a shopping site.
-
The main photo in a story of a news site.
-
The title of a blog post.
-
The first paragraph in an entry of an encyclopedia site.
The API could have use cases outside of page load by comparing the rendering timestamps with input timestamps. For example, developers could monitor the time it takes for a widget to show up after a click that triggers it.
2. Element Timing
Element Timing involves the following new interfaces:
2.1.
PerformanceElementTiming
interface
[Exposed =Window ]interface :PerformanceElementTiming PerformanceEntry {readonly attribute DOMHighResTimeStamp ;renderTime readonly attribute DOMHighResTimeStamp ;loadTime readonly attribute DOMRectReadOnly ;intersectionRect readonly attribute DOMString ;identifier readonly attribute unsigned long ;naturalWidth readonly attribute unsigned long ;naturalHeight readonly attribute DOMString ;id readonly attribute Element ?;element readonly attribute DOMString ; [url Default ]object (); };toJSON
A
PerformanceElementTiming
object
reports
timing
information
about
one
associated
element.
Each
PerformanceElementTiming
object
has
these
associated
concepts,
all
of
which
are
initially
set
to
:
-
A request containing the image request (if the entry is for image content).
-
An element containing the associated
Element.
The
associated
concepts
and
some
attributes
for
PerformanceElementTiming
are
specified
in
the
processing
model
in
§ 3.5
Report
image
Element
Timing
and
§ 3.6
Report
text
Element
Timing
.
The
entryType
attribute’s
getter
must
return
the
DOMString
.
The
name
attribute’s
getter
must
return
the
value
it
was
initialized
to.
The
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.
The
duration
attribute’s
getter
must
return
0.
The
renderTime
attribute
must
return
the
value
it
was
initialized
to.
The
loadTime
attribute’s
getter
must
return
the
the
value
it
was
initialized
to.
The
intersectionRect
attribute
must
return
the
value
it
was
initialized
to.
The
identifier
attribute’s
getter
must
return
the
value
it
was
initialized
to.
The
naturalWidth
attribute
must
return
the
value
it
was
initialized
to.
The
naturalHeight
attribute
must
return
the
value
it
was
initialized
to.
The
id
attribute’s
getter
must
return
the
value
it
was
initialized
to.
The
element
attribute’s
getter
must
run
the
get
an
element
algorithm
this
’s
element
and
null
as
inputs.
Note:
This
means
that
an
element
that
is
no
longer
descendant
of
the
Document
will
no
longer
be
returned
by
element
's
attribute
getter.
The
url
attribute’s
getter
must
perform
the
following
steps:
Note: The URL is trimmed for data URLs to avoid excessive memory in the entry.
3. Processing model
Note:
A
user
agent
implementing
the
Element
Timing
API
would
need
to
include
in
supportedEntryTypes
for
Window
contexts.
This
allows
developers
to
detect
support
for
element
timing.
3.1. Modifications to the DOM specification
This section will be removed once the [DOM] specification has been modified.
We
extend
the
Element
interface
as
follows:
partial interface Element { [CEReactions ]attribute DOMString ; };elementTiming
The
elementTiming
attribute
must
reflect
the
element’s
"
elementtiming
"
content
attribute.
Each
Element
has
an
associated
image
request
which
is
initially
null.
When
the
processing
model
for
an
Element
element
of
type
HTMLImageElement
,
SVGImageElement
,
or
HTMLVideoElement
creates
a
new
image
resource
(e.g.,
to
be
displayed
as
an
image
or
poster
image),
element
’s
associated
image
request
is
set
to
the
image
request
of
the
created
image
resource.
Note:
Every
image
resource
that
is
obtained
from
a
URL
whose
scheme
is
equal
to
"data"
has
an
associated
image
request
which
is
not
fetched
but
still
needs
to
be
loaded.
This
request
can
be
the
associated
image
request
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
set
of
owned
text
nodes
,
which
is
initially
an
empty
ordered
set
.
Each
Document
has
images
pending
rendering
,
a
list
of
triples
(
Element
,
image
request
,
DOMHighResTimeStamp)
which
are
considered
loaded
but
not
yet
rendered.
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.
Each
Document
also
has
a
set
of
elements
with
rendered
text
,
which
is
initially
an
empty,
ordered
set
.
3.2. Modifications to the CSS specification
When
the
user
agent
is
executing
the
painting
order
,
it
must
populate
the
set
of
owned
text
nodes
of
the
painted
Elements
so
that
the
following
is
true:
-
If a
Textobject text will not be painted due to the font face being in its font block period , then it is not appended to the set of owned text nodes of anyElement. -
Otherwise, text is appended to the set of owned text nodes of the
Elementwhich determines the containing block of text .
NOTE: A user agent might want to use a stack to efficiently compute the set of owned text nodes while implementing the painting order .
Every
Element
has
a
list
of
associated
background
image
requests
which
is
initially
an
empty
array.
When
the
processing
model
for
the
Element
element
’s
style
requires
a
new
image
resource
(to
be
displayed
as
background
image),
the
image
request
created
by
the
new
resource
is
appended
to
element
’s
associated
background
image
requests
.
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.
NOTE:
we
assume
that
there
is
one
image
request
for
each
Element
that
a
background-image
property
affects
and
for
each
URL
that
the
background-image
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
image
requests
.
This
means
that
a
single
background-image
property
could
produce
multiple
PerformanceElementTiming
entries
because
it
can
affect
multiple
elements
and
because
it
can
specify
multiple
URLs.
3.3. Modifications to the HTML specification
This section will be removed once the [HTML] specification has been modified.
In the update the rendering step of the event loop processing model , add the following substep at the end:
-
For each fully active
Documentin docs , run the element timing processing algorithm passing in theDocumentand now .
3.4. Process image that finished loading
-
Let element be the input
Element. -
Let imageRequest be element ’s associated image request .
-
Let root be element ’s root .
-
If root is not a
Document, return. -
Let now be the current high resolution time given element ’s relevant global object .
-
If imageRequest is not a data URL [RFC2397] and if the timing allow check fails for imageRequest ’s resource, run the report image element timing algorithm, passing in the triple ( element , imageRequest , now ), 0, and root as inputs.
-
Otherwise, add the triple ( element , imageRequest , now ) to root ’s images pending rendering .
3.5. Report image Element Timing
Document
document
,
perform
the
following
steps:
-
Let intersectionRect be the value returned by the intersection rect algorithm using element as the target and
viewportdocument ’s initial containing block as the root. -
Let exposedElement be the result of running get an element with element and document as input.
-
If exposedElement is not null, call the potentially add a LargestContentfulPaint entry algorithm with intersectionRect , imageRequest , renderTime , loadTime , element , and document .
-
If element ’s "
elementtiming" content attribute is absent, then abort these steps. -
Create and initialize a
PerformanceElementTimingobject entry .-
Initialize entry ’s request to imageRequest .
-
Initialize entry ’s element to element .
-
Initialize entry ’s
renderTimeto renderTime . -
Initialize entry ’s
loadTimeto loadTime . -
Initialize entry ’s
intersectionRectto intersectionRect . -
Initialize entry ’s
identifierto element ’s "elementtiming" content attribute. -
Initialize entry ’s
naturalWidthandnaturalHeightby running the same steps for animg'snaturalWidthandnaturalHeightattribute getters, but using imageRequest as the image. -
Initialize entry ’s
idto element ’s "id" content attribute.
-
-
Queue the PerformanceEntry entry .
3.6. Report text Element Timing
Element
element
,
a
DOMHighResTimestamp
renderTime
and
a
Document
document
,
perform
the
following
steps:
-
Let intersectionRect be an empty rectangle.
-
For each
Textnode text in element ’s set of owned text nodes :-
Augment intersectionRect to be smallest rectangle containing the border box of text and intersectionRect .
-
-
Intersect intersectionRect with the visual viewport.
-
Let exposedElement be the result of running get an element with element and document as input.
-
If exposedElement is not null, call the potentially add a LargestContentfulPaint entry algorithm with intersectionRect , null, renderTime , 0, exposedElement , and document .
-
If element ’s "
elementtiming" content attribute is absent, then abort these steps. -
Create and initialize a
PerformanceElementTimingobject entry with document ’s relevant realm .-
Initialize entry ’s element to element .
-
Initialize entry ’s
renderTimeto renderTime . -
Initialize entry ’s
loadTimeto 0. -
Initialize entry ’s
intersectionRectto intersectionRect . -
Initialize entry ’s
identifierto element ’s "elementtiming" content attribute. -
Initialize entry ’s
naturalWidthandnaturalHeightto 0. -
Initialize entry ’s
idto element ’s "id" content attribute.
-
-
Queue the PerformanceEntry entry .
3.7. Element Timing processing
Document
doc
and
a
timestamp
now
and
performs
the
following
steps:
-
For each imagePendingRenderingTriple in doc ’s images pending rendering list:
-
Let imageRequest be the image request of imagePendingRenderingTriple .
-
If imageRequest is fully decoded, then run the following steps:
-
Run the report image element timing algorithm passing in imagePendingRenderingTriple , now , and doc .
-
Remove imagePendingRenderingTriple from doc ’s images pending rendering list.
-
-
-
For each
Elementelement in doc ’s descendants :-
If element is contained in doc ’s set of elements with rendered text , continue.
-
If element ’s set of owned text nodes is empty, continue.
-
Append element to doc ’s set of elements with rendered text .
-
Run the report text element timing given element , now , and doc .
-
3.8. Get an element algorithm
Element
element
and
Document
document
as
inputs,
run
these
steps:
-
If element is not connected , return
.null -
Let settings be this ’s relevant settings object .
-
if document is null, let document be settings ’s relevant global object ’s associated
Document. -
If element ’s root is not equal to document or if document is not fully active , return
.null -
Return element .
4. Security & privacy considerations
This API exposes some information about cross-origin images. In particular, images that do not pass the timing allow check still have their resource load time exposed, which could be a source of privacy concerns.
However,
this
is
considered
to
not
add
new
attacks
to
the
web
platform
because
the
ResourceTiming
API
exposes
a
similar
timestamp
already.
In
addition,
the
onload
handler
exposes
load
timing
when
it
is
available,
and
the
resource
load
time
is
a
close
proxy
to
this.
The
current
high
resolution
time
computed
at
the
beginning
of
the
onload
handler
would
provide
the
image
load
time.
We
choose
to
expose
the
loadTime
because
it
is
very
easy
to
obtain
even
without
an
onload
handler.
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.
The
renderTime
(display
timestamp)
can
also
be
polyfilled
via
the
PaintTiming
API.
To
do
this,
add
an
iframe
that
contains
trivial
content
on
the
onload
handler
of
the
target
image
or
text
content.
Then,
query
the
first
paint
of
that
iframe
to
obtain
the
rendering
timestamp
of
the
content.
This
is
quite
inefficient
and
the
polyfill
itself
might
affect
the
timing
obtained.
Due
to
the
difficulty
in
obtaining
this
information
today,
we
choose
not
to
expose
the
display
timestamp
for
images
that
fail
the
timing
allow
check
.
For
clarity,
here
is
a
code
snippet
using
the
PaintTiming
API:
// In the attacker frame.< iframe src = attack.html ></ iframe > < script > window. onmessage= e=> { let timestamp= e. data; // Acquired the display timestamp for 'victim.jpg'! } </ script > // In the attack.html iframe.< img src = 'victim.jpg' /> < script > // Wait until onload or some time when the PaintTiming entries will be visible. onload() => { let entry= performance. getEntriesByType( 'paint' )[ 0 ]; top. postMessage( entry. startTime, '*' ); } </ script >
The
other
nontrivial
parameter
being
exposed
here
is
the
intersectionRect
.
This
can
already
be
polyfilled,
for
example
using
IntersectionObserver
.
The
polyfill
process
would
be
similar:
add
an
IntersectionObserver
on
the
onload
handler
of
the
target
image
or
text
content.
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.
For
images,
we
compute
the
intersectionRect
once
the
image
has
loaded
if
it
does
not
pass
the
timing
allow
check
.
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
element
timing
processing
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,
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.
This
would
unintentionally
leak
some
of
the
display
timing
of
the
image.