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 timing-eligible elements, as defined by [PAINT-TIMING] .
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 USVString ; [url Default ]object (); };toJSON PerformanceElementTiming includes PaintTimingMixin ;
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.3
Report
Image
Element
Timing
and
§ 3.4
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
getter
step
is
to
return
the
default
paint
timestamp
given
this
’s
paint
timing
info
.
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
perform
the
following
steps:
-
If this ’s element is not exposed for paint timing given null, return null.
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.
3.2. Report Element Timing
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:
-
For each record in paintedImages :
-
Run the report image element timing algorithm passing in record , paintTimingInfo , and doc .
-
-
For each
Elementelement in paintedTextNodes :-
Run the report text element timing given element , paintTimingInfo , and doc .
-
3.3. Report Image Element Timing
Document
document
,
perform
the
following
steps:
-
If record ’s element ’s "
elementtiming" content attribute is absent, then abort these steps. -
Let intersectionRect be the value returned by the intersection rect algorithm using record ’s element as the target and viewport as the root.
-
Create and initialize a
PerformanceElementTimingobject entry with document ’s relevant realm , whose paint timing info is paintTimingInfo .-
Initialize entry ’s
intersectionRectto intersectionRect . -
Initialize entry ’s
identifierto record ’s element ’s "elementtiming" content attribute. -
Initialize entry ’s
naturalWidthandnaturalHeightby running the same steps for animg’snaturalWidthandnaturalHeightattribute getters, but using record ’s request as the image. -
Initialize entry ’s
idto record ’s element ’s "id" content attribute.
-
Queue the PerformanceEntry entry .
3.4. Report Text Element Timing
Element
element
,
a
paint
timing
info
paintTimingInfo
and
a
Document
document
,
perform
the
following
steps:
-
If element ’s "
elementtiming" content attribute is absent, then abort these 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.
-
Create and initialize a
PerformanceElementTimingobject entry with document ’s relevant realm , whose paint timing info is paintTimingInfo .-
Initialize entry ’s element to element .
-
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 .
4. Security & privacy considerations
This API exposes some information about cross-origin images. In particular, images 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)
is
indeed
newly
exposed
information.
Implementations
are
advised
to
coarsen
that
timestamp
further,
to
a
4
milliseconds
resolution
at
least,
to
avoid
exposing
differences
in
decoding
time
between
cross-origin
images.
Note
that
other
checks,
such
as
`Timing-Allow-Origin`,
does
not
work
here
due
to
same-origin
and
cross-origin
images
being
rendered
at
the
same
time.
Exposing
a
coarse
renderTime
is
anyway
not
a
substantial
attack
vector,
given
that
image
natural
size
and
loading
time
are
exposed
in
other
ways.
// 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.
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
report
element
timing
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.