1. Introduction
1.1. Overview
This section is non-normative.
When a user engages with a website, they expect their actions to cause changes to the website quickly. In fact, research suggests that any user input that is not handled within 100ms is considered slow. Therefore, it is important to surface performance timing information about input events that could not achieve those guidelines.
A
common
way
to
monitor
event
latency
consists
of
registering
an
event
listener.
The
timestamp
at
which
the
event
was
created
can
be
obtained
via
the
event’s
timeStamp
.
In
addition,
performance.now()
could
be
called
both
at
the
beginning
and
at
the
end
of
the
event
handler
logic.
By
subtracting
the
hardware
timestamp
from
the
timestamp
obtained
at
the
beginning
of
the
event
handler,
the
developer
can
compute
the
input
delay
:
the
time
it
takes
for
an
input
to
start
being
processed.
By
subtracting
the
timestamp
obtained
at
the
beginning
of
the
event
handler
from
the
timestamp
obtained
at
the
end
of
the
event
handler,
the
developer
can
compute
the
amount
of
synchronous
work
performed
in
the
event
handler.
Finally,
when
inputs
are
handled
synchronously,
the
duration
from
event
hardware
timestamp
to
the
next
paint
after
the
event
is
handled
is
a
useful
user
experience
metric.
This approach has several fundamental flaws. First, requiring event listeners precludes measuring event latency very early in the page load because listeners might not be registered yet. Second, developers who are only interested in input delay might be forced to add new listeners to events that originally did not have them. This adds unnecessary performance overhead to the event latency calculation. And lastly, it would be very hard to measure asynchronous work caused by the event via this approach.
This specification provides an alternative to event latency monitoring that solves some of these problems. Since the user agent computes the timestamps, there is no need for event listeners in order to measure performance. This means that even events that occur very early in the page load can be captured. This also enables visibility into slow events without requiring analytics providers to attempt to patch and subscribe to every conceivable event. In addition to this, the website’s performance will not suffer from the overhead of unneeded event listeners. Finally, this specification allows developers to obtain detailed information about the timing of the rendering that occurs right after the event has been processed. This can be useful to measure the overhead of website modifications that are triggered by events.
1.2. Interactions
This section is non-normative.
A
single
user
Interaction
interaction
(sometimes
called
a
Gesture)
is
typically
made
up
of
multiple
physical
hardware
input
events.
Each
physical
input
event
might
cause
the
User
Agent
to
dispatch
several
UIEvent
s,
and
each
of
those
might
trigger
multiple
custom
event
listeners,
or
trigger
distinct
default
actions.
For example, a single user "tap" interaction with a touchscreen device is actually made up of a sequence of physical input events:
-
a touch start,
-
a tiny amount of touch movement,
-
a touch end.
Those
physical
input
events
might
dispatch
a
series
of
UIEvent
s:
These
individual
UIEvent
s
will
each
become
candidates
for
their
own
PerformanceEventTiming
entry
reporting,
which
is
useful
for
detailed
timing.
Note:
pointermove
and
touchmove
are
not
currently
considered
for
Event
Timing
.
However,
this
specification
also
defines
a
mechanism
for
grouping
related
PerformanceEventTiming
s
into
Interaction
interactions
s
via
an
interactionId
.
This
mechanism
can
be
used
to
define
a
page
responsiveness
metric
called
Interaction
to
Next
Paint
(INP)
.
1.3. First Input
This section is non-normative.
The
very
first
user
Interaction
interaction
typically
has
a
disproportionate
impact
on
user
experience,
and
is
also
often
disproportionately
slow.
To
that
effect,
the
Event
Timing
API
exposes
timing
information
about
the
first
input
of
a
Window
,
defined
as
the
first
PerformanceEventTiming
entry
with
a
non-0
interactionId
.
Unlike
most
PerformanceEventTiming
s,
the
first
input
entry
is
reported
even
if
it
does
not
exceed
a
provided
durationThreshold
,
and
is
buffered
even
if
it
does
not
exceed
the
default
duration
threshold
of
104ms.
This
mechanism
can
be
used
to
define
a
page
responsiveness
metric
called
First
Input
Delay
(FID)
.
This also allows developers to better measure percentiles and performance improvements, by including data even from pages which are always very responsive, without having to register event handlers.
1.4. Events exposed
The Event Timing API exposes timing information only for certain events.
-
If event ’s
isTrustedattribute value is set to false, return false. -
If event ’s
typeis one of the following:auxclick,click,contextmenu,dblclick,mousedown,mouseenter,mouseleave,mouseout,mouseover,mouseup,pointerover,pointerenter,pointerdown,pointerup,pointercancel,pointerout,pointerleave,gotpointercapture,lostpointercapture,touchstart,touchend,touchcancel,keydown,keypress,keyup,beforeinput,input,compositionstart,compositionupdate,compositionend,dragstart,dragend,dragenter,dragleave,dragover,drop, return true. -
Return false.
Note:
mousemove
,
pointermove
,
pointerrawupdate
,
touchmove
,
wheel
,
and
drag
are
excluded
because
these
are
"continuous"
events.
The
current
API
does
not
have
enough
guidance
on
how
to
count
and
aggregate
these
events
to
obtain
meaningful
performance
metrics
based
on
entries.
Therefore,
these
event
types
are
not
exposed.
1.5. When events are measured
This
section
is
non-normative.
It
explains
at
a
high
level
the
information
that
is
exposed
in
the
§ 3
§ 4
Processing
model
section.
Event timing information is only exposed for certain events, and only when the time difference between user input and paint operations that follow input processing exceeds a certain duration threshold.
The
Event
Timing
API
exposes
a
duration
value,
which
is
meant
to
be
the
time
from
when
the
physical
user
input
occurs
(estimated
via
the
Event
’s
timeStamp
)
to
the
next
time
the
rendering
of
the
Event
’s
relevant
global
object
’s
associated
Document
is
updated.
This
value
is
provided
with
8
millisecond
granularity.
By
default,
the
Event
Timing
API
buffers
and
exposes
entries
when
the
duration
is
104
or
greater,
but
a
developer
can
set
up
a
PerformanceObserver
to
observe
future
entries
with
a
different
threshold.
Note
that
this
does
not
change
the
entries
that
are
buffered
and
hence
the
buffered
flag
only
enables
receiving
past
entries
with
duration
greater
than
or
equal
to
the
default
threshold.
An
Event
’s
delay
is
the
difference
between
the
time
when
the
browser
is
about
to
run
event
handlers
for
the
event
and
the
Event
’s
timeStamp
.
The
former
point
in
time
is
exposed
as
the
PerformanceEventTiming
’s
processingStart
,
whereas
the
latter
is
exposed
as
PerformanceEventTiming
’s
startTime
.
Therefore,
an
Event
’s
delay
can
be
computed
as
.
processingStart
startTime
Note
that
the
Event
Timing
API
creates
entries
for
events
regardless
of
whether
they
have
any
event
listeners.
In
particular,
the
first
click
or
the
first
key
might
not
be
the
user
actually
trying
to
interact
with
the
page
functionality;
many
users
do
things
like
select
text
while
they’re
reading
or
click
in
blank
areas
to
control
what
has
focus.
This
is
a
design
choice
to
capture
problems
with
pages
which
register
their
event
listeners
too
late
and
to
capture
performance
of
inputs
that
are
meaningful
despite
not
having
event
listeners,
such
as
hover
effects.
Developers
can
choose
to
ignore
such
entries
by
ignoring
those
with
essentially
zero
values
of
.
processingEnd
processingStart
,
as
processingEnd
is
the
time
when
the
event
dispatch
algorithm
algorithm
has
concluded.
1.6. Usage example
const observer= new PerformanceObserver( function ( list, obs) { for ( let entryof list. getEntries()) { // Input Delay const inputDelay= entry. processingStart- entry. startTime; // Processing duration const processingDuration= entry. processingEnd- entry. processingStart; // Presentation Delay (approximate) const presentationDelay= Math. max( 0 , entry. startTime+ entry. duration- entry. processingEnd); // Obtain some information about the target of this event, such as the id. const targetId= entry. target? entry. target. id: 'unknown-target' ; console. log( entry. entryType, entry. name, entry. duration, { inputDelay, processingDuration, presentationDelay}); } }); observer. observe({ type: 'first-input' , buffered: true }); observer. observe({ type: 'event' , buffered: true , durationThreshold: 40 });
The
following
example
computes
a
dictionary
mapping
interactionId
to
the
maximum
duration
of
any
of
its
events.
This
dictionary
can
later
be
aggregated
and
reported
to
analytics.
let maxDurations= {}; new PerformanceObserver( list=> { for ( let entryof list. getEntries()) { if ( entry. interactionId> 0 ) { let id= entry. interactionId; if ( ! maxDurations[ id]) { maxDurations[ id] = entry. duration; } else { maxDurations[ id] = Math. max( maxDurations[ id], entry. duration); } } } }). observe({ type: 'event' , buffered: true , durationThreshold: 16 });
The following are sample use cases that could be achieved by using this API:
-
Gather first input delay data on a website and track its performance over time.
-
Clicking a button changes the sorting order on a table. Measure how long it takes from the click until we display reordered content.
-
A user drags a slider to control volume. Measure the latency to drag the slider.
-
Hovering a menu item triggers a flyout menu. Measure the latency for the flyout to appear.
-
Measure the 75’th percentile of the latency of the first user click (whenever click happens to be the first user interaction).
2. Event Timing
Event Timing adds the following interfaces:
2.1.
PerformanceEventTiming
interface
[Exposed =Window ]interface :PerformanceEventTiming PerformanceEntry {; ; ;readonly attribute DOMHighResTimeStamp ;processingStart readonly attribute DOMHighResTimeStamp ;processingEnd readonly attribute boolean ;cancelable readonly attribute Node ?;target ; ;readonly attribute DOMString ;targetSelector readonly attribute unsigned long long ; [interactionId Default ]object (); };toJSON
PerformanceEventTiming
object
reports
timing
information
about
one
associated
Event
.
Each
PerformanceEventTiming
object
has
these
associated
concepts,
all
of
which
are
initially
set
to
null
:
concepts:
-
An eventTarget containing the associatedANode. The targetattribute’s getter must perform the following steps:eventTarget , initially null. -
If this ’s eventTarget is not exposed for paint timingA DOMHighResTimeStampgiven null, return null.processing start timestamp , initially 0. -
Return thisA DOMHighResTimeStamp’s eventTarget .processing end timestamp , initially 0.Note: -
A
user agent implementing the Event Timing API would need to include " first - input " and " event " in supportedEntryTypes for WindowDOMHighResTimeStampcontexts. This allows developers to detect support for event timing.render time , initially 0. -
This remainder of this section is non-normative. The values of the attributes of PerformanceEventTiming are set in the processing model in § 3 Processing model . This section provides an informative summary of how they will be set.An integer or null interactionId , initially null. -
PerformanceEventTiming extends the following attributes of the PerformanceEntry interface:A string entry type .name
The
name
attribute’s
getter
provides
the
must
return
this
’s
associated
event
’s
type
attribute
value.
.
entryType
The
entryType
attribute’s
getter
returns
"
event
"
(for
long
events)
or
"
first
-
input
"
(for
the
first
user
interaction).
startTime
must
return
this
’s
entry
type
.
The
startTime
attribute’s
getter
returns
the
must
return
this
’s
associated
event
’s
timeStamp
attribute
value.
.
duration
The
duration
attribute’s
getter
returns
the
difference
between
the
next
time
the
update
the
rendering
steps
are
completed
for
the
associated
event
must
return
this
’s
Document
render
time
after
the
associated
event
minus
this
has
been
dispatched,
and
the
’s
startTime
,
rounded
to
the
nearest
8ms.
PerformanceEventTiming
has
the
following
additional
attributes:
The
processingStart
The
processingStart
attribute’s
getter
returns
a
must
return
this
’s
processing
start
timestamp
captured
at
the
beginning
of
the
event
dispatch
algorithm
.
This
is
when
event
handlers
are
about
to
be
executed.
The
processingEnd
The
processingEnd
attribute’s
getter
returns
a
timestamp
captured
at
the
must
return
this
’s
processing
end
of
the
event
dispatch
algorithm
timestamp
.
This
is
when
event
handlers
have
finished
executing.
It’s
equal
to
processingStart
when
there
are
no
such
event
handlers.
The
cancelable
The
cancelable
attribute’s
getter
returns
the
must
return
this
’s
associated
event
’s
cancelable
attribute
value.
target
The
target
attribute’s
getter
returns
must
perform
the
associated
event
following
steps:
If this ’s
last target when such NodeeventTarget is notdisconnected nor in the shadow DOM.exposed for paint timing given null, return null.Return this ’s eventTarget .
The
targetSelector
The
targetSelector
attribute’s
getter
returns
a
string
that
identifies
must
return
the
associated
event
result
of
generate
a
CSS
selector
’s
last
target
given
this
.
’s
eventTarget
.
The
interactionId
The
interactionId
attribute’s
getter
returns
a
number
that
uniquely
identifies
the
user
Interaction
which
triggered
the
associated
event
.
This
attribute
is
0
unless
the
associated
event
must
return
this
’s
type
interactionId
attribute
value
if
it
is
one
of:
A
pointerdown
,
pointerup
,
not
null,
or
click
0
otherwise.
,
and
belongs
Note:
A
user
agent
implementing
the
Event
Timing
API
would
need
to
a
tap
or
drag
gesture.
Note
that
pointerdown
include
"
first
that
ends
"
and
"
event
"
in
scroll
is
excluded.
A
keydown
supportedEntryTypes
or
for
contexts.
This
allows
developers
to
keyup
Window
,
belongs
a
user
key
press.
detect
support
for
event
timing.
2.2.
EventCounts
interface
[Exposed =Window ]interface {EventCounts readonly maplike <DOMString ,unsigned long long >; };
The
EventCounts
object
is
a
map
where
the
keys
are
event
types
and
the
values
are
the
number
of
events
that
have
been
dispatched
that
are
of
that
type
.
Only
events
whose
type
is
supported
by
PerformanceEventTiming
entries
(see
section
§ 1.4
Events
exposed
)
are
counted
via
this
map.
2.3.
Extensions
to
the
Performance
interface
[Exposed =Window ]partial interface Performance { [SameObject ]readonly attribute EventCounts ;eventCounts readonly attribute unsigned long long ; };interactionCount
The
eventCounts
attribute’s
getter
returns
this
’s
relevant
global
object
’s
eventCounts
event
counts
.
The
interactionCount
attribute’s
getter
returns
this
’s
relevant
global
object
’s
interactionCount
interaction
count
.
3.
Processing
model
Modifications
to
other
specifications
3.1. Modifications to the DOM specification
This section will be removed once [DOM] has been modified.
Right after step 1, we add the following steps:
-
Let
interactionId be the result of computing interactionId given event . LettimingEntry be the result of initializing and recording event timing processing start given event,and the current high resolution time, and interactionId ..
Right before the returning step of that algorithm, add the following step:
-
FinalizeRecord event timing processing endpassinggiven timingEntry ,event ,target , and the current high resolution time as inputs.
Note:
If
a
user
agent
skips
the
event
dispatch
algorithm
,
it
can
still
choose
to
include
an
entry
for
that
Event
.
In
this
case,
it
will
estimate
the
value
of
processingStart
and
set
the
processingEnd
to
the
same
value.
3.2. Modifications to the HTML specification
This section will be removed once [HTML] has been modified.
Window
has
the
following
associated
concepts:
-
pending Event Timing entries
to be queued, a listthat storesofPerformanceEventTimingobjects, whichwillis initiallybeempty. -
has dispatched input event , a boolean which is initially set to false.
-
user interactionhas queued first-input , a boolean which is initially set to false. initial interactionId value , an integer which is initially set to a random integer between 100 and 10000.
-
interactionId increment , a small positive integer chosen by the user agent.
Note: The
user interactioninitial interactionId valueis setand interactionId increment are used toa random integer instead of 0 so thatcalculateinteractionIdvalues (see compute interactionId ). This discourages developersdo not relyfrom relying on it to count the exact number of interactionsin the page. By startingor assuming it starts at zero. This also allows the user agent to eagerly assign arandom value, developersvalue (e.g., atpointerdown) and then discard it (e.g., afterpointercancel), rather than lazily computing it. User agents areless likelyexpected not to useit as the source of truth for the number of interactions that have occurred in the page.shared global interaction values across differentWindowobjects to prevent cross-origin leaks. -
pendingactive keydownsinteraction map , a mapoffrom integers toPerformanceEventTimingsintegers, which is initially empty. -
active pointer interaction
valuemap , a mapoffrom integers to integers, which is initially empty.Note: User agents can periodically clear the active key interaction map and active pointer interaction map to prevent memory leaks (e.g., if an expected
keyuporclickevent is not dispatched within a reasonable timeframe). -
pending
pointer downspointerdown map , a mapoffrom integers toobjects, which is initially empty.PerformanceEventTimingsPerformanceEventTiming -
last keydown interactionId , an integer which is
contextmenu triggeredinitially set to 0. last keyup interactionId ,
a booleanan integer which is initially set tofalse.0.-
eventCountsevent counts , a mapwith entries offrom strings to integers, which is initially empty. The keys in this map represent event types and theform type → numEvents . This meansvalues represent the number of events thattherehave beennumEventsdispatchedsuchfor thattheir type attribute value is equal to type .type. Upon construction of aPerformanceobject whose relevant global object is aWindow, itseventCountsevent counts must be initialized to a map containing 0s for all event types that the user agent supports from the list described in § 1.4 Events exposed . -
interactionCountinteraction count , an integer which counts the total number of distinct user interactions, for which there was a uniqueinteractionIdcomputed via computing interactionId .
-
For each
fully activeDocumentdoc that is either removed from docs (due to being non-renderable or unnecessary) or is fully active in docs, invoke(after calling mark paint timing ), run thealgorithm to dispatch pendingfollowing steps:Record event timing render time for doc with the current high resolution time .
Flush Event Timing entries for
that Document .doc .
Note: To ensure timely reporting, event timing entries are updated and flushed even when a rendering update is skipped (e.g., if the document is non-renderable). In these cases, the current high resolution time serves as a "fallback" for the render time. Formally distinguishing between this fallback and a true "paint" time is left as a potential future extension.
3.3. Modifications to the Performance Timeline specification
This section will be removed once [PERFORMANCE-TIMELINE-2] had been modified.
The
PerformanceObserverInit
dictionary
is
augmented:
partial dictionary PerformanceObserverInit {DOMHighResTimeStamp ; };durationThreshold
3.4.
4.
Should
add
PerformanceEventTiming
Processing
model
Note:
4.1.
The
following
algorithm
is
used
in
the
[PERFORMANCE-TIMELINE-2]
specification
to
determine
when
a
PerformanceEventTiming
entry
needs
to
be
added
to
the
buffer
of
a
PerformanceObserver
Initialize
and
record
event
timing
processing
start
or
to
the
performance
timeline,
as
described
in
the
registry
.
Given
a
PerformanceEventTiming
If event should not be considered for Event Timing , return null.
Let timingEntry be a new
PerformanceObserverInitPerformanceEventTimingoptions , to determine if we should add PerformanceEventTiming ,object withentryeventand optionally’s relevant realm .Set
optionstimingEntryas inputs, run the following steps:’s associated event to event .-
IfSetentrytimingEntry ’sentryTypeentry typeattribute value equalsto "first - inputevent", return true.". -
Assert thatSetentrytimingEntry ’sentryTypeprocessing start timestampattribute value equals "to processingStartTimestamp . Set timingEntry ’s interactionId to the result of compute interactionId given event
".and timingEntry .-
Let
minDurationwindow becomputed as follows:event ’s relevant global object . -
If
optionswindowisdoes notpresent or ifimplementWindow, return null. Let
optionstype be event ’s.durationThresholdtypeis not present, let-
Let
minDurationevent counts be104.window ’s event counts . -
Otherwise, letAssert thatminDurationevent countsbe the maximum between 16Contains type . Set event counts [ type ] to event counts [ type ] + 1.
If window ’s has dispatched input event is false and
optionstimingEntry ’sdurationThresholdinteractionIdvalue.is not 0, run the following steps:-
IfSetentrywindow ’s has dispatched input event to true.
Note: has dispatched input event is set to true as soon as an interactive event is initialized. For
durationpointerdownattribute valueentries, interactionId isgreater than or equal to minDuration , return true.still unknown at this point, but other specifications (such as Largest Contentful Paint ) which use this, will observe both pointer interactions and scroll as input, anyway.-
-
Otherwise, return false.Return timingEntry .
3.5.
4.2.
Increasing
interaction
count
Record
event
timing
processing
end
-
IncreaseIfwindowtimingEntry’s user interaction value value by a small number chosen by the user agent.is null, then return. -
Let
interactionCountrelevantGlobal bewindowtarget ’sinteractionCountrelevant global object . -
Set
interactionCounttimingEntry ’s processing end timestamp tointeractionCountprocessingEndTimestamp . Assert that target
+ 1.implementsNode.Note:
The user interaction value is increased by a small number chosen by the user agent instead of 1This assertion holds due todiscourage developers from considering it as a counter ofthenumbertypes ofuser interactions that have occurred in the web application. This allowsevents supported by theuser agent to choose to eagerly assign a user interaction valueEvent Timing API.Set timingEntry ’s eventTarget
(i.e. at pointerdown) and then discard it (i.e. after pointercancel), rather thantolazily compute it.target .-
A user agent may chooseAppend timingEntry toincrease it by a small random integer every time, or choose a constant. A user agent must not use a shared global user interaction valuerelevantGlobal ’s pending Event Timing entries .
4.3.
Record
event
timing
render
time
s
for
all
Windows
Document
,
because
this
could
introduce
cross-origin
leaks.
doc
and
a
renderingTimestamp
:Let window be doc ’s relevant global object .
-
For each timingEntry in window ’s pending Event Timing entries :
If timingEntry ’s render time is 0, set timingEntry ’s render time to renderingTimestamp .
3.6.
4.4.
Computing
Compute
interactionId
Event
event
isTrusted
PerformanceEventTiming
-
Let type be event ’s
typeattribute value. -
If type is not one among
,keyupkeydown,compositionstartkeypresskeyup,input,pointerdown,pointercancel,pointerup,click, orcontextmenu, return 0.Note: keydown and pointerdown are marked pending in finalize event timing , and then updated later when computing interactionId for future events (like keyup and pointerup ). -
Let window be event ’s relevant global object .
-
Let
pendingKeyDowns be window ’s pending key downs . Let pointerMap be window ’s pointer interaction value map . Let pendingPointerDownsnewInteractionId bewindow ’s pending pointer downs .null. -
If
typeevent is aand event ’skeyupPointerEvent: IfisComposingpointerIdattribute valueistrue, return 0.greater than or equal to 0:-
Let code beSeteventnewInteractionId’s keyCodeto the result of compute pointer interactionIdattribute value. IfgivenpendingKeyDowns [window ,code ] does not exist, return 0. Letevent , and entrybe pendingKeyDowns [ code ]. Increase interaction count on window.
-
-
Let interactionId be window ’s user interaction value value.Else:-
Set
entrynewInteractionId’s interactionIdto the result of computing the keyboard interactionId. Add entry to window ’s entries to be queued . RemovegivenpendingKeyDowns [ codewindow]. ReturnandinteractionIdevent .
If -
Note:
At
this
point,
newInteractionId
can
be
null
only
if
type
is
.
Keyboard
interactions
and
other
pointer
interactions
either
compute
a
valid
ID
or
fallback
to
0
directly
in
their
respective
sub-algorithms.
compositionstart
pointerdown
:
-
For eachReturnentry innewInteractionId .
Window
of
-
AppendSetentrywindow ’s interaction count to window ’sentries to be queued . Clearinteraction countpendingKeyDowns .plus 1. -
Return
0. Iftypewindowis input’s initial interactionId value: Ifplus (eventwindowis not an instance of InputEvent’s interaction count, return 0. Note: This check is done to excludetimes window ’s interactionId increment ).
Events
Window
type
Event
input
PerformanceEventTiming
-
IfLet type be event ’sattributeisComposingtypevalue is false, return 0.value. -
Let pointerId be event ’s
attribute value.Increase interaction countpointerIdon window . -
ReturnLet activePointerInteractions be window ’suseractive pointer interactionvaluemap . -
Otherwise (LettypependingPointerDownsis pointercancel , pointerup , click , or contextmenu ):be window ’s pending pointerdown map . -
Let
pointerIdnewInteractionId beevent ’s pointerId attribute value.null. -
If type is
:clickpointerdown-
If
pointerMappendingPointerDowns [ pointerId ]does not exist, return 0.exists:-
LetRun resolve pending pointerdown givenvaluewindowbeandpointerMappendingPointerDowns [ pointerId ].
-
-
RemoveSetpointerMappendingPointerDowns [ pointerId]. Return] tovalueentry .
-
-
Assert thatElse if type ispointerup,, orpointercancelcontextmenu:contextmenuclick.-
If pendingPointerDowns [ pointerId ]
does not exist:exists:-
IfSettypenewInteractionIdis contextmenuto the result of resolve pending pointerdown, returngiven window’s user interaction value .and pendingPointerDowns [ pointerId ].
-
-
If
typenewInteractionId ispointerupnull andwindowactivePointerInteractions’s is contextmenu triggered flag is true:[ pointerId ] exists:-
Set
windownewInteractionId’s is contextmenu triggered flagtofalse. Returnwindow ’s user interaction value . Otherwise, return 0. Let pointerDownEntry be pendingPointerDownsactivePointerInteractions [ pointerId ].
-
-
Assert thatIfpointerDownEntrynewInteractionId isanull:Set newInteractionId to the result of getting the next interactionId given window .
Note: Most pointer interactions are expected to have an associated
PerformanceEventTimingpointerdownentry.event, which would have assigned an interactionId in the steps above. However, some special input devices (for example, accessibility-related software) can simulate certain trusted pointer events without following the usual event sequence.
-
-
IfElse if type is:pointerup or contextmenupointercancel-
Increase interaction count on window . SetIfpointerMappendingPointerDowns [ pointerId ]to window ’s user interaction value .exists:-
Set
pointerDownEntry ’s interactionId to pointerMappendingPointerDowns [ pointerId]. Append pointerDownEntry to window ’s entries]'s interactionId tobe queued .0. -
Remove pendingPointerDowns [ pointerId ].
-
-
If type is contextmenu , setSetwindownewInteractionId’s is contextmenu triggeredtotrue. If type is pointercancel , return0.
-
-
Return
pointerMap [ pointerId ].newInteractionId .
keyup
Window
pointerdown
we
have
to
wait
until
pointercancel
PerformanceEventTiming
Let newInteractionId be the result of getting the next interactionId
occur to know itsgiven window .Set pointerDownEntry ’s interactionId
. We trytomatch click with a previous interaction ID from a pointerdown . If pointercancel or pointerup happens, we’llnewInteractionId .Let pointerId be
ready to set the interactionIdpointerDownEntry ’s associated eventfor the stored entry corresponding to’s.pointerdownpointerIdIf it is pointercancel , this means we do not want to assign a new-
Set window ’s active pointer interaction
IDmap [ pointerId ] tothenewInteractionId . pointerdownRemove. If it is pointerupwindow ’s pending pointerdown map, we[ pointerId ].Return newInteractionId .
pointerdown
Window
window
and
click
Event
-
If the algorithm to determine ifLeteventtypeshouldbeconsidered for Event Timingevent ’stypeattribute value.returns false, then return null. -
Let
timingEntryactiveKeyInteractions bea new PerformanceEventTiming object witheventwindow ’srelevant realmactive key interaction map . -
SetLettimingEntrynewInteractionId’s name tobe null. If
eventtype’sis:typekeydownattribute value.-
Set
timingEntrynewInteractionId’s entryTypeto" event ".the result of getting the next interactionId given window . -
Set
timingEntryactiveKeyInteractions’s startTime to[ event ’stimeStampkeyCodeattribute value.] to newInteractionId . -
Set
timingEntrywindow ’sprocessingStartlast keydown interactionId toprocessingStartnewInteractionId .Set timingEntry ’s
Note: last keydown interactionId is used to attribute simulated
cancelableclickto event ’sorcancelablecontextmenuattribute value. Set timingEntry ’sevents (which lack ainteractionIdkeyCodeto interactionId . Return timingEntry . 3.8.), as well asevents (which follow aFinalize event timinginputkeydown), back toWhen askedfinalize event timing , with timingEntry , event , target , and processingEnd as inputs, runthefollowing steps:originating keyboard interaction.-
-
IfElse iftimingEntrytype isnull, then return.keypress:-
Let
relevantGlobalkeyCode betargetevent ’srelevant global object .keyCodeattribute value. -
If
relevantGlobalactiveKeyInteractionsdoes not implement Window , return.[ keyCode ] exists:-
Set
timingEntrynewInteractionId’s processingEndtoprocessingEnd .activeKeyInteractions [ keyCode ].
-
-
Assert thatElse iftargetwindowimplements Node’s last keydown interactionId.is not 0:Note: This assertion holds due-
Set newInteractionId to
the types of events supported by the Event Timing API.window ’s last keydown interactionId .
-
-
Else:
-
Set
timingEntrynewInteractionId’s eventTargettotarget .0.
-
Note: This
will set eventTarget to the last event target. So if retargetingfallback forkeypressevents is necessary because theoccurs, the last target, closest toroot , will be used. Set timingEntry ’stargetSelectorkeyCodeto the resultofrunning the algorithm to generateaCSS selectorkeypresseventwith target as input. If’smight differ from that of the precedingtypekeydownattribute value isevent. [UIEVENTS] recommends using theattribute to avoid such inconsistencies (see UI Events section onpointerdowncode:codemotivation ). -
-
Let pendingPointerDowns beElse ifrelevantGlobaltype’s pending pointer downs .iskeyup:-
Let
pointerIdkeyCode be event ’sattribute value.pointerIdkeyCode. -
If
pendingPointerDownsactiveKeyInteractions [pointerIdkeyCode ] exists:-
LetSetpreviousPointerDownEntrynewInteractionIdbetopendingPointerDownsactiveKeyInteractions [pointerIdkeyCode ].
-
-
Add previousPointerDownEntry toElse:Set
relevantGlobalnewInteractionId’s entriestobe queued .0.
-
Set
pendingPointerDowns [ pointerIdwindow]’s last keyup interactionId totimingEntrynewInteractionId . -
Set
relevantGlobalwindow ’sis contextmenu triggeredlast keydown interactionId tofalse.0.
-
-
Otherwise,Else ifevent ’stypeattribute valueis:keydowninput-
If
eventwindow ’sisComposinglast keydown interactionIdattribute valueistrue :not 0:-
AppendSettimingEntrynewInteractionId torelevantGlobalwindow ’sentries to be queuedlast keydown interactionId . -
Return.Set window ’s last keydown interactionId to 0.
-
-
LetElse:Set
pendingKeyDownsnewInteractionIdbeto the result of getting the next interactionId givenrelevantGlobal ’s pending key downs .window .
-
-
Let code beElse ifeventtype’siskeyCodeclickattribute value.orcontextmenu:-
If
pendingKeyDowns [ codewindow] exists:’s last keydown interactionId is not 0:-
Let previousKeyDownEntry beSetpendingKeyDownsnewInteractionId[tocodewindow].’s last keydown interactionId .
-
-
IfElse ifcodewindow ’s last keyup interactionId is not229:0:-
Increase relevantGlobal ’s user interaction value value by a small number chosen by the user agent.SetpreviousKeyDownEntrynewInteractionId’s interactionIdtorelevantGlobalwindow ’suser interaction valuelast keyup interactionId .
Note: 229 -
-
If newInteractionId is
a special case since it corresponds to IME keyboard events. Sometimes multiple of these are sent by the user agent, and they donotcorrespond to holding a key down repeatedly.null:-
Add previousKeyDownEntry toSetrelevantGlobalwindow ’sentrieslast keydown interactionId tobe queued .0. -
Set
pendingKeyDowns [ codewindow]’s last keyup interactionId totimingEntry .0.
-
-
Otherwise:Else:-
AppendSettimingEntrynewInteractionId to the result of getting the next interactionId givenrelevantGlobal ’s entrieswindow .
-
Note: For
clickevents, it is expected that thekeyCoderepresents "Enter" or "Space", and an implementation can enforce this constraint. -
Return newInteractionId .
Note:
During
a
composition
session,
keyboard
and
input
events
are
still
expected
to
be
queued
dispatched
according
to
the
UI
Events
section
on
keyboard
events
during
composition
.
These
events
are
assigned
an
interactionId
corresponding
to
the
active
keyboard
interaction,
whereas
the
composition
events
themselves
(e.g.,
compositionstart
,
compositionupdate
)
are
not
assigned
an
interactionId
.
3.9.
4.5.
Dispatch
pending
Flush
Event
Timing
entries
Document
doc
-
Let window be doc ’s relevant global object .
-
LetWhilerenderingTimestampwindowbe the current high resolution time .’s pending Event Timing entries is not empty:-
For eachLet timingEntryinbe the first element of window ’s pending Event Timing entriesto be queued :. -
Set event timing entry duration passingIf timingEntry, window , and renderingTimestamp .’s interactionId is null, break. -
If
timingEntrywindow ’sdurationhas queued first-inputattribute valueisgreater than or equal to 16, then queuefalse, and timingEntry.’s interactionId is not 0, run the following steps:-
Set window ’s
entries to behas queued first-input toan empty list.true. -
For eachLetpendingPointerDownEntryfirstInputEntryin the values frombe a copy ofwindowtimingEntry . Set firstInputEntry ’s
pending pointer downs :entry type to "first".- input-
Set event timing entry durationqueuepassing pendingPointerDownEntry ,window , and renderingTimestampfirstInputEntry .
-
-
For each pendingKeyDownEntry in the values fromIfwindowtimingEntry ’spending key downs :is greater than or equal to 16, then queueSet event timing entrydurationpassingpendingKeyDownEntry ,timingEntry . Remove the first element of window
, and renderingTimestamp .’s pending Event Timing entries .
-
4.6.
Should
add
PerformanceEventTiming
timingEntry
,
Note:
The
following
algorithm
is
used
in
the
[PERFORMANCE-TIMELINE-2]
specification
to
determine
when
a
Window
PerformanceEventTiming
window
,
and
entry
needs
to
be
added
to
the
buffer
of
a
DOMHighResTimeStamp
PerformanceObserver
renderingTimestamp
,
perform
or
to
the
following
steps:
If
timingEntry
’s
performance
timeline,
as
described
in
the
registry
.
duration
PerformanceEventTiming
startTime
PerformanceObserverInit
-
SetIftimingEntryentry ’sattribute value equals todurationentryTypea DOMHighResTimeStamp resulting from"", return true.renderingTimestampfirst- start , with granularity of 8ms or less.input -
Let name beAssert thattimingEntryentry ’sattributenameentryTypevalue. Perform the following steps to update thevalue equals "event".counts: -
Let
eventCountsminDuration bewindow ’s eventCounts . Assert that eventCounts contains name . Set eventCounts [ name ] to eventCounts [ name ] + 1.computed as follows:-
If
windowoptions’s has dispatched input eventisfalse, andnot present or iftimingEntryoptions ’sis notinteractionIddurationThreshold0, run the following steps: Letpresent, letfirstInputEntryminDuration bea copy of timingEntry .104. -
SetOtherwise, letfirstInputEntryminDuration be the maximum between 16 and options ’sentryTypedurationThresholdto " first - input ". queue firstInputEntry .value.
-
-
SetIfwindowentry ’shas dispatched input eventdurationattribute value is greater than or equal to minDuration , return true. -
Otherwise, return false.
3.10.
4.7.
Target
Selectors
EventTarget
target
,
run
the
following
steps:
-
If target is a
Node, run the following steps:-
Let selector be a string with an initial value of target ’s
nodeName. -
If target is an
Element, run the following steps:-
If target has an
`id`idattribute, set selector to the concatenation of « selector , "#", the value of the `id` attribute ». -
Otherwise, if target has a
`src`srcattribute, set selector to the concatenation of « selector , "[src=", the value of the `src` attribute, "]" ».
-
-
Return selector .
-
-
Otherwise, return an empty string.
4.
5.
Security
&
privacy
considerations
We
would
not
like
to
introduce
more
high
resolution
timers
to
the
web
platform
due
to
the
security
concerns
entailed
by
such
timers.
Event
handler
timestamps
have
the
same
accuracy
as
performance.now()
.
Since
processingStart
and
processingEnd
could
be
computed
without
using
this
API,
exposing
these
attributes
does
not
produce
new
attack
surfaces.
Thus,
duration
is
the
only
one
which
requires
further
consideration.
The
duration
has
an
8
millisecond
granularity
(it
is
computed
as
such
by
performing
rounding).
Thus,
a
high
resolution
timer
cannot
be
produced
from
these
timestamps.
However,
it
does
introduce
new
information
that
is
not
readily
available
to
web
developers:
the
time
pixels
draw
after
an
event
has
been
processed.
We
do
not
find
security
or
privacy
concerns
with
exposing
the
timestamp,
especially
given
its
granularity.
In
an
effort
to
expose
the
minimal
amount
of
new
information
that
is
useful,
we
decided
to
pick
8
milliseconds
as
the
granularity.
This
allows
relatively
precise
timing
even
for
120Hz
displays.
The
choice
of
104ms
as
the
default
cutoff
value
for
the
duration
is
just
the
first
multiple
of
8
greater
than
100ms.
An
event
whose
rounded
duration
is
greater
than
or
equal
to
104ms
will
have
its
pre-rounded
duration
greater
than
or
equal
to
100ms.
Such
events
are
not
handled
within
100ms
and
will
likely
negatively
impact
user
experience.
The
choice
of
16ms
as
the
minimum
value
allowed
for
durationThreshold
is
because
it
enables
the
typical
use-case
of
making
sure
that
the
response
is
smooth.
In
120Hz
displays,
a
response
that
skips
more
than
a
single
frame
will
be
at
least
16ms,
so
the
entry
corresponding
to
this
user
input
will
be
surfaced
in
the
API
under
the
minimum
value.