1. Introduction
As the page is loading and while the user is interacting with the page afterwards, both the application and browser queue various events that are then executed by the browser -- e.g. user agent schedules input events based on user’s activity, the application schedules callbacks for requestAnimationFrame and other callbacks, etc. Once in the queue, the browser dequeues these events one-by-one and executes them.
However, some tasks can take a long time (multiple frames) and if/when that happens, the UI thread may become blocked and block all other tasks as well. To the user, this is commonly visible as a "locked up" page where the browser is unable to respond to user input; this is a major source of bad user experience on the web today:
- Delayed "time to Interactive":
-
while the page is loading, or even completely visually rendered, long tasks often tie up the main thread and prevent the user from interacting with the page. Poorly designed third-party content is frequently the culprit.
- High/variable input latency:
-
critical user-interaction events (e.g. tap, click, scroll, wheel, etc.) are queued behind long tasks which yields janky and unpredictable user experience.
- High/variable event handling latency:
-
like input, processing event callbacks (e.g. onload events, etc.) delay application updates.
- Janky animations and scrolling:
-
some animation and scrolling interactions require coordination between compositor and main threads; if a long task is blocking the main thread it can affect responsiveness of animations and scrolling.
Some applications (and RUM vendors) are already attempting to identify and track cases where "long tasks" happen. For example, one known pattern is to install a ~short periodic timer and inspect the elapsed time between the successive expirations: if the elapsed time is greater than the timer period, then there is high likelihood that one or more long tasks have delayed execution of the event loop. This approach mostly works but has several bad performance implications: by polling to detect long tasks, the application prevents quiescence and long idle blocks (see requestIdleCallback); it’s bad for battery life; there is no way to know what is causing the delay (e.g. first party or third party code).
The RAIL performance model suggests that applications should respond to user input in less than 100ms (for touch move and scrolling, the threshold is 16ms). The goal of this API is to surface notifications about tasks that may prevent the application from hitting these targets. This API surfaces tasks that take 50ms or more. A website without these tasks should respond to user input in under 100ms: it will take less than 50ms to finish the task that is being executed when the user input is received and less than 50ms to execute the task to react to such user input.
1.1. Usage Example
const observer= new PerformanceObserver( function ( list) { for ( const entryof list. getEntries()) { // Process long task notifications: // report back for analytics and monitoring // ... } }); // Register observer for previous and future long task notifications. observer. observe({ type: "longtask" , buffered: true }); // Long script execution after this will result in queueing // and receiving "longtask" entries in the observer.
2. Terminology
Long task refers to any of the following occurrences whose duration exceeds 50ms:
-
An event loop task plus the perform a microtask checkpoint that follows immediately afterwards. This captures the duration of an event loop task, including its associated microtasks.
-
An update the rendering step within the event loop processing model.
-
A pause between the last step and the next first step of the event loop processing model. This captures any work that the user agent performs in its UI thread outside of the event loop.
Culprit browsing context container refers to the browsing context container (iframe
, object
, etc.) that is being implicated, on the whole, for a long task.
Attribution refers to identifying the type of work (such as script, layout etc.) that contributed significantly to the long task, as well as identifying which culprit browsing context container is responsible for that work.
3. Long Task Timing
Long Task timing involves the following new interfaces:
3.1. PerformanceLongTaskTiming
interface
[Exposed =Window ]interface :
PerformanceLongTaskTiming PerformanceEntry {readonly attribute FrozenArray <TaskAttributionTiming >attribution ; [Default ]object (); };
toJSON
The values of the attributes of a PerformanceLongTaskTiming
are set in the processing model in § 4.1 Report long tasks. The following provides an informative summary of how they will be set.
The name
attribute’s getter will return one of the following strings:
- "
unknown
" -
The long task originated from an update the rendering step within the event loop processing model or work the user agent performed outside of the event loop.
- "
self
" -
The long task originated from an event loop task within this browsing context.
- "
same
"- origin- ancestor -
The long task originated from an event loop task within a same-origin ancestor browsing context.
- "
same
"- origin- descendant -
The long task originated from an event loop task within a same-origin descendant browsing context.
- "
same
"- origin -
The long task originated from an event loop task within a same-origin browsing context that is not an ancestor or descendant.
- "
cross
"- origin- ancestor -
The long task originated from an event loop task within a cross-origin ancestor browsing context.
- "
cross
"- origin- descendant -
The long task originated from an event loop task within a cross-origin descendant browsing context.
- "
cross
"- origin- unreachable -
The long task originated from an event loop task within a cross-origin browsing context that is not an ancestor or descendant.
- "
multiple
"- contexts -
The long task originated from an event loop task involving multiple browsing contexts.
The entryType
attribute’s getter will return
.
The startTime
attribute’s getter will return a DOMHighResTimeStamp
of when the task started.
The duration
attribute’s getter will return a DOMHighResTimeStamp
equal to the elapsed time between the start and end of task.
The attribution
attribute’s getter will return a frozen array of TaskAttributionTiming
entries.
3.2. TaskAttributionTiming
interface
[Exposed =Window ]interface :
TaskAttributionTiming PerformanceEntry {readonly attribute DOMString containerType ;readonly attribute DOMString containerSrc ;readonly attribute DOMString containerId ;readonly attribute DOMString containerName ; [Default ]object (); };
toJSON
The values of the attributes of a TaskAttributionTiming
are set in the processing model in § 4.1 Report long tasks. The following provides an informative summary of how they will be set.
The name
attribute’s getter will always return "unknown
".
The entryType
attribute’s getter will always return "taskattribution
".
The startTime
attribute’s getter will always return 0.
The duration
attribute’s getter will always return 0.
The containerType
attribute’s getter will return the type of the culprit browsing context container, such as "iframe
", "embed
", or "object
". If no single culprit browsing context container is found, it will return "window
".
The containerName
attribute’s getter will return the value of the container’s name
content attribute. If no single culprit browsing context container is found, it will return the empty string.
The containerId
attribute’s getter will return the value of the container’s id
content attribute. If no single culprit browsing context container is found, it will return the empty string.
The containerSrc
attribute’s getter will return the value of the container’s src
content attribute. If no single culprit browsing context container is found, it will return the empty string.
3.3. Pointing to the culprit
This section is non-normative.
A long task can involve different types of work (such as script, layout, style etc), and it could be executed within different browsing contexts, or it could be global in nature such as a long garbage collection that spans the entire agent cluster or unit of related browsing contexts.
Thus attribution has a couple of facets:
-
Pointing to the origin of the long task and/or the overall location of the culprit browsing context: this is referred to as minimal culprit attribution and is captured in the
name
field. -
Pointing to the type of work involved in the long task, and its associated culprit browsing context container: this is captured in
TaskAttributionTiming
objects in theattribution
field ofPerformanceLongTaskTiming
.
Therefore, name
and attribution
fields on PerformanceLongTaskTiming
together paint the picture for where the blame rests for a long task.
When delivering this information the Web’s same-origin policy must be adhered to.
These fields are not independent. The following gives an overview of how they are related:
name
| Culprit browsing context container implicated by attribution
|
---|---|
"self "
| empty |
"same "
| same-origin culprit |
"same "
| same-origin culprit |
"same "
| same-origin culprit |
"cross "
| empty |
"cross "
| empty |
"cross "
| empty |
"multiple "
| empty |
"unknown "
| empty |
4. Processing model
Note: A user agent implementing the Long Tasks API would need to include
in supportedEntryTypes
for Window
contexts.
This allows developers to detect support for long tasks.
4.1. Report long tasks
-
If end time minus start time is less than the long tasks threshold of 50 ms, abort these steps.
-
Let destinationRealms be an empty set.
-
Determine the set of JavaScript Realms to which reports will be delivered:
For each top-level browsing context topmostBC in top-level browsing contexts:
-
Add topmostBC’s Window’s relevant Realm to destinationRealms.
-
Let descendantBCs be topmostBC’s active document’s list of the descendant browsing contexts.
-
For each descendantBC in descendantBCs, add descendantBC’s Window’s relevant Realm to destinationRealms.
-
-
For each destinationRealm in destinationRealms:
-
Let name be the empty string. This will be used to report minimal culprit attribution, below.
-
Let culpritSettings be
.null -
If the task argument was not provided, set name to "
unknown
". -
Otherwise: process task’s script evaluation environment settings object set to determine name and culpritSettings as follows:
-
If task’s script evaluation environment settings object set is empty: set name to "
unknown
" and culpritSettings to
.null -
If task’s script evaluation environment settings object set’s length is greater than one: set name to "
multiple
" and culpritSettings to- contexts
.null -
If task’s script evaluation environment settings object set’s length is one:
-
Set culpritSettings to the single item in task’s script evaluation environment settings object set.
-
Let destinationSettings be destinationRealm’s relevant settings object.
-
Let destinationOrigin be destinationSettings’s origin.
-
Let destinationBC be destinationSettings’s responsible browsing context.
-
If culpritSettings is the same as destinationSettings, set name to "
self
". -
If culpritSettings’s origin and destinationOrigin are same origin:
-
If culpritSettings’s responsible browsing context is an ancestor of destinationBC, set name to "
same
".- origin- ancestor -
Otherwise, if culpritSettings’s responsible browsing context is a descendant of destinationBC, set name to "
same
".- origin- descendant -
Otherwise, set name to "
same
".- origin
-
-
Otherwise:
-
If culpritSettings’s responsible browsing context is an ancestor of destinationBC, set name to "
cross
" and set culpritSettings to- origin- ancestor
.null NOTE: this is not reported because of security. Developers should look this up themselves.
-
Otherwise, if culpritSettings’s responsible browsing context is a descendant of destinationBC, set name to "
cross
".- origin- descendant -
Otherwise, set name to "
cross
".- origin- unreachable
-
-
-
-
If task was not provided, let attribution be
.null -
Otherwise, let attribution be a new
TaskAttributionTiming
object and set its attributes as follows:-
Set attribution’s
name
attribute to "unknown
".NOTE: future iterations of this API will add more values to the
name
attribute of aTaskAttributionTiming
object, but for now it can only be a single value. -
Set attribution’s
entryType
attribute to
."taskattribution" -
Set attribution’s
containerType
attribute to
."window" -
Set attribution’s
containerName
andcontainerSrc
attributes to the empty string. -
If culpritSettings is not
:null -
Let container be culpritSettings’s responsible browsing context’s browsing context container.
-
Assert: container is not
.null -
Set attribution’s
containerId
attribute to the value of container’s ID, or the empty string if the ID is unset. -
If container is an
iframe
element:-
Set attribution’s
containerType
attribute to "iframe
". -
Set attribution’s
containerName
attribute to the value of container’sname
content attribute, or the empty string if the attribute is absent. -
Set attribution’s
containerSrc
attribute to the value of container’ssrc
content attribute, or the empty string if the attribute is absent.
NOTE: it is intentional that we record the frame’s
src
attribute here, and not its current URL, as this is meant primarily to help identify frames, and allowing discovery of the current URL of a cross-origin iframe is a security problem. -
-
If container is a
frame
element:-
Set attribution’s
containerType
attribute to "frame
". -
Set attribution’s
containerName
attribute to the value of container’sname
content attribute, or the empty string if the attribute is absent. -
Set attribution’s
containerSrc
attribute to the value of container’ssrc
content attribute, or the empty string if the attribute is absent.
-
-
If container is an
object
element:-
Set attribution’s
containerType
attribute to "object
". -
Set attribution’s
containerName
attribute to the value of container’sname
content attribute, or the empty string if the attribute is absent. -
Set attribution’s
containerSrc
attribute to the value of container’sdata
content attribute, or the empty string if the attribute is absent.
-
-
If container is an
embed
element:-
Set attribution’s
containerType
attribute to "embed
". -
Set attribution’s
containerName
attribute to the empty string. -
Set attribution’s
containerSrc
attribute to the value of container’ssrc
content attribute, or the empty string if the attribute is absent.
-
-
-
-
Create a new
PerformanceLongTaskTiming
object newEntry and set its attributes as follows:-
Set newEntry’s
name
attribute to name. -
Set newEntry’s
entryType
attribute to "longtask
". -
Set newEntry’s
startTime
attribute to start time. -
Set newEntry’s
startTime
attribute to end time minus start time. -
If attribution is not
, set newEntry’snull attribution
attribute to a new frozen array containing the single value attribution.NOTE: future iterations of this API will add more values to the
attribution
attribute, but for now it only contains a single value.
-
-
Queue the PerformanceEntry newEntry on destinationRealm.
-
5. Security & privacy considerations
Long Tasks API adheres to the same-origin policy by including origin-safe attribution information about the source of the long task. There is a 50ms threshold for long tasks. Together this provides adequate protection against security attacks against browser.
However, privacy related attacks are possible, while the API doesn’t introduce any new privacy attacks, it could make existing privacy attacks faster. Mitigations for this are possible and discussed in the security review in this document.