Event Timing API

Draft Community Group Report,

This version:
https://wicg.github.io/event-timing
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/event-timing
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

This document defines an API that provides web page authors with insights into the latency of certain events triggered by user interactions.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

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 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 will likely not be registered yet at that point in time. Second, developers that are only interested in the input delay might be forced to add new listeners to events that originally did not have one. 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.

The very first user interaction has a disproportionate impact on user experience, and is often disproportionately slow. It’s slow because it’s often blocked on JavaScript execution that is not properly split into chunks during page load. The latency of the website’s response to the first user interaction can be considered a key responsiveness and loading metric. To that effect, this API surfaces all the timing information about this interaction, even when this interaction is not handled slowly. This allows developers to measure percentiles and improvements without having to register event handlers. In particular, this API enables measuring the first input delay, the delay in processing for the first "discrete" input event.

1.1. Events exposed

The Event Timing API exposes timing information for certain events. Certain types of events are considered, and timing information is exposed when the time difference between user input and paint operations that follow input processing exceeds a certain threshold.

Given an event, to determine if it should be considered for Event Timing, run the following steps:
  1. If event’s isTrusted attribute value is set to false, return false.

  2. If event’s type is 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.

  3. 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.

it’s unclear whether dragexit should be on the list. It may be deprecated and removed.

The remainder of this section is non-normative. It explains at a high level the information that is exposed in the § 3 Processing model section.

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.

The Event Timing API exposes a duration value, which is meant to be the time from when user interaction occurs (estimated via the Event's timeStamp) to the next time the rendering of the Event's relevant global object's associated Document’s 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.

The Event Timing API also exposes timing information about the first input of a Window window, defined as the first Event (with isTrusted set) whose relevant global object is window and whose type is one of the following:

This enables computing the first input delay, the delay of the first input.

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.2. Usage example

    const observer = new PerformanceObserver(function(list) {
        const perfEntries = list.getEntries().forEach(entry => {
            const inputDelay = entry.processingStart - entry.startTime;
            // Report the input delay when the processing start was provided.
            // Also report the full input duration via entry.duration.
        });
    });
    // Register observer for event.
    observer.observe({entryTypes: ["event"]});
    ...
    // We can also directly query the first input information.
    new PerformanceObserver(function(list, obs) {
        const firstInput = list.getEntries()[0];

        // Measure the delay to begin processing the first input event.
        const firstInputDelay = firstInput.processingStart - firstInput.startTime;
        // Measure the duration of processing the first input event.
        // Only use when the important event handling work is done synchronously in the handlers.
        const firstInputDuration = firstInput.duration;
        // Obtain some information about the target of this event, such as the id.
        const targetId = firstInput.target ? firstInput.target.id : 'unknown-target';
        // Process the first input delay and perhaps its duration...

        // Disconnect this observer since callback is only triggered once.
        obs.disconnect();
    }).observe({type: 'first-input', buffered: true});

}

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 => {
    list.getEntries().forEach(entry => {
        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:

2. Event Timing

Event Timing adds the following interfaces:

2.1. PerformanceEventTiming interface

PerformanceEventTiming

Firefox89+SafariNoneChrome77+
OperaYesEdge79+
Edge (Legacy)NoneIENone
Firefox for Android89+iOS SafariNoneChrome for Android77+Android WebView77+Samsung Internet12.0+Opera MobileYes
[Exposed=Window]
interface PerformanceEventTiming : PerformanceEntry {
    readonly attribute DOMHighResTimeStamp processingStart;
    readonly attribute DOMHighResTimeStamp processingEnd;
    readonly attribute boolean cancelable;
    readonly attribute Node? target;
    readonly attribute unsigned long long interactionId;
    [Default] object toJSON();
};

Each PerformanceEventTiming object has an associated eventTarget, which is initially set to null.

The target attribute’s getter returns the result of the get an element algorithm, passing this’s eventTarget and null as inputs.

Note: A user agent implementing the Event Timing API would need to include "first-input" and "event" in supportedEntryTypes for Window contexts. This allows developers to detect support for event timing.

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.

Each PerformanceEventTiming object reports timing information about an associated Event.

PerformanceEventTiming extends the following attributes of the PerformanceEntry interface:

name
The name attribute’s getter provides the associated event’s type.
entryType
The entryType attribute’s getter returns "event" (for long events) or "first-input" (for the first user interaction).
startTime
The startTime attribute’s getter returns the associated event’s timeStamp.
duration
The duration attribute’s getter returns the difference between the next time the update the rendering steps are completed for the associated event’s Document after the associated event has been dispatched, and the startTime, rounded to the nearest 8ms.

PerformanceEventTiming has the following additional attributes:

processingStart
The processingStart attribute’s getter returns a timestamp captured at the beginning of the event dispatch algorithm. This is when event handlers are about to be executed.
processingEnd
The processingEnd attribute’s getter returns a timestamp captured at the end of the event dispatch algorithm. This is when event handlers have finished executing. It’s equal to processingStart when there are no such event handlers.
cancelable
The cancelable attribute’s getter returns the associated event’s cancelable attribute value.
target
The target attribute’s getter returns the associated event’s last target when such Node is not disconnected nor in the shadow DOM.
interactionId
The interactionId attribute’s getter returns the ID that uniquely identifies the user interaction which triggered the associated event. This attribute is 0 unless the associated event’s type attribute value is one of:

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.1 Events exposed) are counted via this map.

2.3. Extensions to the Performance interface

[Exposed=Window]
partial interface Performance {
    [SameObject] readonly attribute EventCounts eventCounts;
};

The eventCounts attribute’s getter returns a map with entries of the form typenumEvents. This means that there have been numEvents dispatched such that their type attribute value is equal to type.

Upon construction of a Performance object whose relevant global object is a Window, the user agent must initialize the eventCounts attribute value to a map containing 0s for all event types that the user agent supports from the list described in § 1.1 Events exposed.

3. Processing model

3.1. Modifications to the DOM specification

This section will be removed once [DOM] has been modified.

We modify the event dispatch algorithm as follows.

Right after step 1, we add the following steps:

  1. Let interactionId be the result of computing interactionId given event.

  2. Let timingEntry be the result of initializing event timing given event, the current high resolution time, and interactionId.

Right before the returning step of that algorithm, add the following step:

  1. Finalize event timing passing 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.

Each Window has entries to be queued, a list that stores PerformanceEventTiming objects, which will initially be empty.

Each Window also has pending first pointer down, a pointer to a PerformanceEventTiming entry which is initially null.

Each Window has has dispatched input event, a boolean which is initially set to false.

Each Window has user interaction value, an integer which is initially set to a random integer between 100 and 10000.

Note: the user interaction value is set to a random integer instead of 0 so that developers do not rely on it to count the number of interactions in the page. By starting at a random value, developers are less likely to use it as the source of truth for the number of interactions that have occurred in the page.

Each Window has pending key downs, a map of integers to PerformanceEventTimings which is initially empty.

Each Window has pointer interaction value map, a map of integers which is initially empty.

Finally, each Window has pending pointer downs, a map of integers to PerformanceEventTimings which is initially empty.

In the update the rendering step of the event loop processing model, add a step right after the step that calls mark paint timing:
  1. For each fully active Document in docs, invoke the algorithm to dispatch pending Event Timing entries for that Document.

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. Should add PerformanceEventTiming

Note: 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 or to the performance timeline, as described in the registry.

Given a PerformanceEventTiming entry and a PerformanceObserverInit options, to determine if we should add PerformanceEventTiming, with entry and optionally options as inputs, run the following steps:
  1. If entry’s entryType attribute value equals to "first-input", return true.

  2. Assert that entry’s entryType attribute value equals "event".

  3. Let minDuration be computed as follows:

    1. If options is not present or if options’s durationThreshold is not present, let minDuration be 104.

    2. Otherwise, let minDuration be the maximum between 16 and options’s durationThreshold value.

  4. If entry’s duration attribute value is greater than or equal to minDuration, return true.

  5. Otherwise, return false.

3.5. Computing interactionId

When asked to compute interactionId with event as input, run the following steps:
  1. If event’s isTrusted attribute value is false, return 0.

  2. Let type be event’s type attribute value.

  3. If type is not one among keyup, compositionstart, input, pointercancel, pointerup, or click, return 0.

    Note: keydown and pointerdown are handled in finalize event timing.

  4. Let window be event’s relevant global object.

  5. Let pendingKeyDowns be window’s pending key downs.

  6. Let pointerMap be window’s pointer interaction value map.

  7. Let pendingPointerDowns be window’s pending pointer downs.

  8. If type is keyup:

    1. If event’s isComposing attribute value is true, return 0.

    2. Let code be event’s keyCode attribute value.

    3. If pendingKeyDowns[code] does not exist, return 0.

    4. Let entry be pendingKeyDowns[code].

    5. Increase window’s user interaction value value by a small number chosen by the user agent.

    6. Let interactionId be window’s user interaction value value.

    7. Set entry’s interactionId to interactionId.

    8. Add entry to window’s entries to be queued.

    9. Remove pendingKeyDowns[code].

    10. Return interactionId.

  9. If type is compositionstart:

    1. For each entry in the values of pendingKeyDowns:

      1. Append entry to window’s entries to be queued.

    2. Clear pendingKeyDowns.

    3. Return 0.

  10. If type is input:

    1. If event is not an instance of InputEvent, return 0. Note: this check is done to exclude Events for which the type is input but that are not about modified text content.

    2. If event’s isComposing attribute value is false, return 0.

    3. Increase window’s user interaction value value by a small number chosen by the user agent.

    4. Return window’s user interaction value.

  11. Otherwise (type is pointercancel, pointerup, or click):

    1. Let pointerId be event’s pointerId attribute value.

    2. If type is click:

      1. If pointerMap[pointerId] does not exist, return 0.

      2. Let value be pointerMap[pointerId].

      3. Remove pointerMap[pointerId].

      4. Return value.

    3. Assert that type is pointerup or pointercancel.

    4. If pendingPointerDowns[pointerId] does not exist, return 0.

    5. Let pointerDownEntry be pendingPointerDowns[pointerId].

    6. Assert that pointerDownEntry is a PerformanceEventTiming entry.

    7. If type is pointerup:

      1. Increase window’s user interaction value value by a small number chosen by the user agent.

      2. Set pointerMap[pointerId] to window’s user interaction value.

      3. Set pointerDownEntry’s interactionId to pointerMap[pointerId].

    8. Append pointerDownEntry to window’s entries to be queued.

    9. Remove pendingPointerDowns[pointerId].

    10. If type is pointercancel, return 0.

    11. Return pointerMap[pointerId].

Note: the algorithm attempts to assign events to the corresponding interactiond IDs. For keyboard events, a keydown triggers a new interaction ID, whereas a keyup has to match its ID with a previous keydown. For pointer events, when we get a pointerdown we have to wait until pointercancel or pointerup occur to know its interactionId. We try to match click with a previous interaction ID from a pointerdown. If pointercancel or pointerup happens, we’ll be ready to set the interactionId for the stored entry corresponding to pointerdown. If it is pointercancel, this means we do not want to assign a new interaction ID to the pointerdown. If it is pointerup, we compute a new interaction ID and set it on both the pointerdown and the pointerup (and later, the click if it occurs).

The user interaction value is increased by a small number chosen by the user agent instead of 1 to discourage developers from considering it as a counter of the number of user interactions that have occurred in the web application. A user agent may choose to increase it by a small random integer every time. A user agent must not pick a global random integer and increase the user interaction values of all Windows by that amount because this could introduce cross origin leaks.

3.6. Initialize event timing

When asked to initialize event timing, with event, processingStart, and interactionId as inputs, run the following steps:
  1. If the algorithm to determine if event should be considered for Event Timing returns false, then return null.

  2. Let timingEntry be a new PerformanceEventTiming object with event’s relevant realm.

  3. Set timingEntry’s name to event’s type attribute value.

  4. Set timingEntry’s entryType to "event".

  5. Set timingEntry’s startTime to event’s timeStamp attribute value.

  6. Set timingEntry’s processingStart to processingStart.

  7. Set timingEntry’s cancelable to event’s cancelable attribute value.

  8. Set timingEntry’s interactionId to interactionId.

  9. Return timingEntry.

3.7. Finalize event timing

When asked to to finalize event timing, with timingEntry, event, target, and processingEnd as inputs, run the following steps:
  1. If timingEntry is null, return.

  2. Let relevantGlobal be target’s relevant global object.

  3. If relevantGlobal does not implement Window, return.

  4. Set timingEntry’s processingEnd to processingEnd.

  5. Assert that target implements Node.

    Note: this assertion holds due to the types of events supported by the Event Timing API.

  6. Set timingEntry’s eventTarget to the result of calling the get an element algorithm with target and relevantGlobal’s associated document as inputs.

    Note: This will set eventTarget to the last event target. So if retargeting occurs, the last target, closest to the root, will be used.

    Change the linking of the "get an element" algorithm once it is moved to the DOM spec.

  7. If event’s type attribute value is not keydown nor pointerdown, append timingEntry to relevantGlobal’s entries to be queued.

  8. If event’s type attribute value is pointerdown:

    1. Let pendingPointerDowns be relevantGlobal’s pending pointer downs.

    2. Let pointerId be event’s pointerId.

    3. If pendingPointerDowns[pointerId] exists, append pendingPointerDowns[pointerId] to relevantGlobal’s entries to be queued.

    4. Set pendingPointerDowns[pointerId] to timingEntry.

  9. Otherwise (event’s type attribute value is keydown):

    1. If event’s isComposing attribute value is true:

      1. Append timingEntry to relevantGlobal’s entries to be queued.

      2. Return.

    2. Let pendingKeyDowns be relevantGlobal’s pending key downs.

    3. Let code be event’s keyCode attribute value.

    4. If pendingKeyDowns[code] exists:

      1. Let entry be pendingKeyDowns[code].

      2. If code is not 229: Note: 229 is a special case since it corresponds to IME keyboard events. Sometimes multiple of these are sent by the user agent, and they do not correspond holding a key down repeatedly.

        1. Increase window’s user interaction value value by a small number chosen by the user agent.

        2. Set entry’s interactionId to window’s user interaction value.

      3. Add entry to window’s entries to be queued.

      4. Remove pendingKeyDowns[code].

    5. Set pendingKeyDowns[code] to timingEntry.

3.8. Dispatch pending Event Timing entries

When asked to dispatch pending Event Timing entries for a Document doc, run the following steps:
  1. Let window be doc’s relevant global object.

  2. Let renderingTimestamp be the current high resolution time.

  3. For each timingEntry in window’s entries to be queued:

    1. Set event timing entry duration passing timingEntry, window, and renderingTimestamp.

    2. If timingEntry’s duration attribute value is greater than or equal to 16, then queue timingEntry.

  4. Clear window’s entries to be queued.

  5. For each pendingDown in the values from window’s pending pointer downs:

    1. Set event timing entry duration passing pendingDown, window, and renderingTimestamp.

When asked to set event timing entry duration given a PerformanceEventTiming timingEntry, a Window window, and a DOMHighResTimeStamp renderingTimestamp, perform the following steps:
  1. If timingEntry’s duration attribute value is nonzero, return.

  2. Let start be timingEntry’s startTime attribute value.

  3. Set timingEntry’s duration to a DOMHighResTimeStamp resulting from renderingTimestamp - start, with granularity of 8ms or less.

  4. Let name be timingEntry’s name attribute value.

  5. Perform the following steps to update the event counts:

    1. Let performance be window’s performance attribute value.

    2. If performance’s eventCounts attribute value does not contain a map entry whose key is name, then:

      1. Let mapEntry be a new map entry with key equal to name and value equal to 1.

      2. Add mapEntry to performance’s eventCounts attribute value.

    3. Otherwise, increase the map entry’s value by 1.

  6. If window’s has dispatched input event is false, run the following steps:

    1. If name is "pointerdown", run the following steps:

      1. Set window’s pending first pointer down to a copy of timingEntry.

      2. Set the entryType of window’s pending first pointer down to "first-input".

    2. Otherwise, run the following steps:

      1. If name is "pointerup" AND if window’s pending first pointer down is not null, then:

        1. Set window’s has dispatched input event to true.

        2. Queue window’s pending first pointer down.

      2. Otherwise, if name is one of "click", "keydown" or "mousedown", then:

        1. Set window’s has dispatched input event to true.

        2. Let newFirstInputDelayEntry be a copy of timingEntry.

        3. Set newFirstInputDelayEntry’s entryType to "first-input".

        4. Queue the entry newFirstInputDelayEntry.

4. 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 this 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 on 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.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ELEMENT-TIMING]
Element Timing API. cg-draft. URL: https://wicg.github.io/element-timing/
[HR-TIME-2]
Ilya Grigorik. High Resolution Time Level 2. 21 November 2019. REC. URL: https://www.w3.org/TR/hr-time-2/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PAINT-TIMING]
Shubhie Panicker. Paint Timing 1. 7 September 2017. WD. URL: https://www.w3.org/TR/paint-timing/
[PERFORMANCE-TIMELINE-2]
Nicolas Pena Moreno; Ilya Grigorik. Performance Timeline Level 2. 19 August 2021. WD. URL: https://www.w3.org/TR/performance-timeline-2/
[POINTEREVENTS]
Jacob Rossi; Matt Brubeck. Pointer Events. 4 April 2019. REC. URL: https://www.w3.org/TR/pointerevents/
[POINTEREVENTS3]
Patrick Lauke; et al. Pointer Events. 20 October 2021. WD. URL: https://www.w3.org/TR/pointerevents3/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[TOUCH-EVENTS]
Doug Schepers; et al. Touch Events. 10 October 2013. REC. URL: https://www.w3.org/TR/touch-events/
[UIEVENTS]
Gary Kacmarcik; Travis Leithead. UI Events. 20 October 2021. WD. URL: https://www.w3.org/TR/uievents/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[Exposed=Window]
interface PerformanceEventTiming : PerformanceEntry {
    readonly attribute DOMHighResTimeStamp processingStart;
    readonly attribute DOMHighResTimeStamp processingEnd;
    readonly attribute boolean cancelable;
    readonly attribute Node? target;
    readonly attribute unsigned long long interactionId;
    [Default] object toJSON();
};

[Exposed=Window]
interface EventCounts {
    readonly maplike<DOMString, unsigned long long>;
};

[Exposed=Window]
partial interface Performance {
    [SameObject] readonly attribute EventCounts eventCounts;
};

partial dictionary PerformanceObserverInit {
    DOMHighResTimeStamp durationThreshold;
};

Issues Index

it’s unclear whether dragexit should be on the list. It may be deprecated and removed.
Change the linking of the "get an element" algorithm once it is moved to the DOM spec.