├── .pr-preview.json ├── loaf-explainer.md ├── w3c.json ├── LICENSE.md ├── CODE_OF_CONDUCT.md ├── raf.css ├── style.css ├── .github └── workflows │ ├── build.yml │ └── auto-publish.yml ├── Makefile ├── demo-child.html ├── render-jank-demo.html ├── demo.html ├── CONTRIBUTING.md ├── init.js ├── README.md └── index.bs /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed" 4 | } 5 | -------------------------------------------------------------------------------- /loaf-explainer.md: -------------------------------------------------------------------------------- 1 | # Long Animation Frames (LoAF) 2 | 3 | Please see https://github.com/w3c/long-animation-frames 4 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 45211, 3 | "contacts": [ 4 | "carine" 5 | ], 6 | "shortName": "longtasks", 7 | "repo-type": "rec-track" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/copyright-software). 4 | 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /raf.css: -------------------------------------------------------------------------------- 1 | div { 2 | width: 7px; 3 | height:7px; 4 | background: green; 5 | float: left; 6 | } 7 | 8 | button { 9 | position: absolute; 10 | top: 70px; 11 | left: 20px; 12 | } 13 | #stop { 14 | left: 100px; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | button { 2 | font-size: 15px; 3 | } 4 | iframe { 5 | padding: 20px; 6 | border: 20px solid rebeccapurple; 7 | } 8 | .eventLog { 9 | width: 90%; 10 | height: 200px; 11 | border: 1px solid; 12 | overflow: auto; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: make ci 17 | - name: Deploy 18 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} 19 | uses: peaceiris/actions-gh-pages@v3 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | publish_dir: ./out 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | local: index.bs 4 | bikeshed --die-on=warning spec index.bs index.html 5 | 6 | index.html: index.bs 7 | @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \ 8 | --output index.html \ 9 | --write-out "%{http_code}" \ 10 | --header "Accept: text/plain, text/html" \ 11 | -F die-on=warning \ 12 | -F file=@index.bs) && \ 13 | [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ 14 | echo ""; cat index.html; echo ""; \ 15 | rm -f index.html; \ 16 | exit 22 \ 17 | ); 18 | 19 | remote: index.html 20 | 21 | ci: index.bs 22 | mkdir -p out 23 | make remote 24 | mv index.html out/index.html 25 | 26 | clean: 27 | rm index.html 28 | 29 | 30 | -------------------------------------------------------------------------------- /demo-child.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
2 | Title: Long Tasks API
3 | Group: webperf
4 | Status: ED
5 | Shortname: longtasks
6 | Level: 1
7 | URL: https://w3c.github.io/longtasks/
8 | Former Editor: Shubhie Panicker, Google https://google.com, panicker@chromium.org, w3cid 92587
9 | Former Editor: Ilya Grigorik, Google https://google.com, igrigorik@chromium.org, w3cid 56102
10 | Former Editor: Domenic Denicola, Google https://google.com, domenic@chromium.org, w3cid 52873
11 | Editor: Noam Rosenthal, Google https://google.com, nrosenthal@google.com, w3cid 121539
12 | Repository: w3c/longtasks
13 | Test Suite: http://w3c-test.org/longtask-timing/
14 | Abstract: This document defines an API that web page authors can use to detect presence of "long tasks" that monopolize the UI thread for extended periods of time and block other critical tasks from being executed - e.g. reacting to user input.
15 | Status Text: If you wish to make comments regarding this document, please send them to public-web-perf@w3.org (subscribe, archives) with [LongTasks] at the start of your email's subject.
16 | Default Highlight: js
17 |
18 |
19 |
24 |
25 | 26 | urlPrefix: https://w3c.github.io/performance-timeline/; spec: PERFORMANCE-TIMELINE-2; 27 | type: interface; url: #the-performanceentry-interface; text: PerformanceEntry; 28 | type: attribute; for: PerformanceEntry; 29 | text: name; url: #dom-performanceentry-name 30 | text: entryType; url: #dom-performanceentry-entrytype 31 | text: startTime; url: #dom-performanceentry-starttime 32 | text: duration; url: #dom-performanceentry-duration 33 | type: dfn; url: #dfn-queue-a-performanceentry; text: Queue the PerformanceEntry 34 | type: dfn; url: #dfn-register-a-performance-entry-type; text: register a performance entry type 35 | type: attribute; for: PerformanceObserver; 36 | text: supportedEntryTypes; url: #supportedentrytypes-attribute; 37 | urlPrefix: https://w3c.github.io/hr-time/; spec: HR-TIME-2; 38 | type: typedef; url: #idl-def-domhighrestimestamp; text: DOMHighResTimeStamp; 39 | type: interface; url: #dfn-performance; text: Performance; 40 | type: attribute; for: Performance; 41 | text: now(); url: #dom-performance-now 42 | type: dfn; text: current high resolution time; url: #dfn-current-high-resolution-time; 43 | urlPrefix: https://html.spec.whatwg.org/multipage/; spec: HTML; 44 | type: dfn; url: #event-loop; text: event loop; 45 | type: dfn; url: #event-loop-processing-model; text: event loop processing model; 46 | type: dfn; url: #browsing-context; text: browsing context; 47 | type: dfn; url: #calling-scripts; text: calling scripts; 48 | type: dfn; url: #list-of-the-descendant-browsing-contexts; text: list of the descendant browsing contexts; 49 | type: dfn; url: #ancestor-browsing-context; text: ancestor; 50 | type: dfn; url: #browsing-context-group-set; text: browsing context group set 51 | type: dfn; url: #script-evaluation-environment-settings-object-set; text: script evaluation environment settings object set 52 | type: dfn; url: #integration-with-the-javascript-agent-cluster-formalism; text: agent cluster 53 | type: dfn; url: #concept-task-document; for: task; text: document; 54 | type: dfn; url: #running-script; text: running script; 55 | type: dfn; url: #muted-errors; for: classic script; text: muted errors; 56 | type: dfn; url: #cors-cross-origin; text: CORS cross-origin; 57 | urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT; 58 | type: dfn; url: #sec-code-realms; text: JavaScript Realms; 59 | urlPrefix: https://dom.spec.whatwg.org/; spec: DOM; 60 | type: attribute; for: Element; 61 | text: id; url: #dom-element-id; 62 | urlPrefix: https://webidl.spec.whatwg.org/; spec: WEBIDL; 63 | type: dfn; text: identifier; url: #dfn-identifier; 64 | type: dfn; text: attribute; url: #dfn-attribute; 65 | urlPrefix: https://tc39.es/ecma262/multipage/managing-memory.html 66 | type: dfn; text: weakrefderef; url: #sec-weakrefderef; 67 |68 | 69 |
70 | spec:html; type:dfn; for:/; text:browsing context 71 |72 | 73 | Introduction {#intro} 74 | ===================== 75 | 76 | 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. 77 | 78 | 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: 79 | 80 | : Delayed "time to Interactive": 81 | :: 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. 82 | 83 | : High/variable input latency: 84 | :: critical user-interaction events (e.g. tap, click, scroll, wheel, etc.) are queued behind long tasks which yields janky and unpredictable user experience. 85 | 86 | : High/variable event handling latency: 87 | :: like input, processing event callbacks (e.g. onload events, etc.) delay application updates. 88 | 89 | : Janky animations and scrolling: 90 | :: 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. 91 | 92 | 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). 93 | 94 | 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. 95 | 96 | Usage Example {#example} 97 | ------------------------ 98 | 99 |
100 | const observer = new PerformanceObserver(function(list) {
101 | for (const entry of list.getEntries()) {
102 | // Process long task notifications:
103 | // report back for analytics and monitoring
104 | // ...
105 | }
106 | });
107 | // Register observer for previous and future long task notifications.
108 | observer.observe({type: "longtask", buffered: true});
109 | // Long script execution after this will result in queueing
110 | // and receiving "longtask" entries in the observer.
111 |
112 |
113 | Terminology {#sec-terminology}
114 | ==============================
115 |
116 | Long task refers to any of the following occurrences whose duration exceeds 50ms:
117 |
118 | * 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.
119 |
120 | * An update the rendering step within the event loop processing model.
121 |
122 | * 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.
123 |
124 | The browsing context container for a [=browsing context=] |bc| is |bc|'s [=navigable/active document=]'s [=node navigable=]'s [=navigable/container=].
125 |
126 | Note: This term is outdated, and the new terms should be reused when revamping this.
127 |
128 | Culprit browsing context container refers to the browsing context container (<{iframe}>, <{object}>, etc.) that is being implicated, on the whole, for a long task.
129 |
130 | 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.
131 |
132 | Long Task Timing {#sec-longtask-timing}
133 | =======================================
134 |
135 | Long Task timing involves the following new interfaces:
136 |
137 | {{PerformanceLongTaskTiming}} interface {#sec-PerformanceLongTaskTiming}
138 | ------------------------------------------------------------------------
139 |
140 |
141 | [Exposed=Window]
142 | interface PerformanceLongTaskTiming : PerformanceEntry {
143 | /* Overloading PerformanceEntry */
144 | readonly attribute DOMHighResTimeStamp startTime;
145 | readonly attribute DOMHighResTimeStamp duration;
146 | readonly attribute DOMString name;
147 | readonly attribute DOMString entryType;
148 |
149 | readonly attribute FrozenArray<TaskAttributionTiming> attribution;
150 | [Default] object toJSON();
151 | };
152 |
153 |
154 | The values of the attributes of a {{PerformanceLongTaskTiming}} are set in the processing model in [[#report-long-tasks]]. The following provides an informative summary of how they will be set.
155 |
156 | The {{PerformanceEntry/name}} attribute's getter will return one of the following strings:
157 |
158 | : "unknown"
159 | :: The long task originated from work that the user agent performed outside of the event loop.
160 | : "self"
161 | :: The long task originated from an event loop task within this browsing context.
162 | : "same-origin-ancestor"
163 | :: The long task originated from an event loop task within a same-origin ancestor navigable.
164 | : "same-origin-descendant"
165 | :: The long task originated from an event loop task within a same-origin descendant browsing context.
166 | : "same-origin"
167 | :: The long task originated from an event loop task within a same-origin browsing context that is not an ancestor or descendant.
168 | : "cross-origin-ancestor"
169 | :: The long task originated from an event loop task within a cross-origin ancestor navigable.
170 | : "cross-origin-descendant"
171 | :: The long task originated from an event loop task within a cross-origin descendant browsing context.
172 | : "cross-origin-unreachable"
173 | :: The long task originated from an event loop task within a cross-origin browsing context that is not an ancestor or descendant.
174 | : "multiple-contexts"
175 | :: The long task originated from an event loop task involving multiple browsing contexts.
176 |
177 | Note: There are some inconsistencies across these names, such as the "-unreachable" and the "-contexts" suffixes.
178 | These names are kept for backward compatibility reasons.
179 |
180 | The {{PerformanceLongTaskTiming/entryType}} attribute's getter step is to return "longtask".
181 |
182 | The {{PerformanceLongTaskTiming/startTime}} attribute's getter step is to return a {{DOMHighResTimeStamp}} of when the task started.
183 |
184 | The {{PerformanceLongTaskTiming/duration}} attribute's getter step is to return a {{DOMHighResTimeStamp}} equal to the elapsed time between the start and end of task, with a 1 ms granularity.
185 |
186 | The attribution attribute's getter will return a frozen array of {{TaskAttributionTiming}} entries.
187 |
188 | {{TaskAttributionTiming}} interface {#sec-TaskAttributionTiming}
189 | ----------------------------------------------------------------
190 |
191 |
192 | [Exposed=Window]
193 | interface TaskAttributionTiming : PerformanceEntry {
194 | /* Overloading PerformanceEntry */
195 | readonly attribute DOMHighResTimeStamp startTime;
196 | readonly attribute DOMHighResTimeStamp duration;
197 | readonly attribute DOMString name;
198 | readonly attribute DOMString entryType;
199 |
200 | readonly attribute DOMString containerType;
201 | readonly attribute DOMString containerSrc;
202 | readonly attribute DOMString containerId;
203 | readonly attribute DOMString containerName;
204 | [Default] object toJSON();
205 | };
206 |
207 |
208 | The values of the attributes of a {{TaskAttributionTiming}} are set in the processing model in [[#report-long-tasks]]. The following provides an informative summary of how they will be set.
209 |
210 | The {{TaskAttributionTiming/name}} attribute's getter will always return "unknown".
211 |
212 | The {{TaskAttributionTiming/entryType}} attribute's getter will always return "taskattribution".
213 |
214 | The {{TaskAttributionTiming/startTime}} attribute's getter will always return 0.
215 |
216 | The {{TaskAttributionTiming/duration}} attribute's getter will always return 0.
217 |
218 | 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".
219 |
220 | 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.
221 |
222 | 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.
223 |
224 | 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.
225 |
226 | Pointing to the culprit {#sec-PointingToCulprit}
227 | ------------------------------------------------
228 |
229 | | {{PerformanceEntry/name}} | 250 |Culprit browsing context container implicated by {{PerformanceLongTaskTiming/attribution}} | 251 |
|---|---|
"self"
254 | | empty 255 | |
"same-origin-ancestor"
257 | | same-origin culprit 258 | |
"same-origin-descendant"
260 | | same-origin culprit 261 | |
"same-origin"
263 | | same-origin culprit 264 | |
"cross-origin-ancestor"
266 | | empty 267 | |
"cross-origin-descendant"
269 | | empty 270 | |
"cross-origin-unreachable"
272 | | empty 273 | |
"multiple-contexts"
275 | | empty 276 | |
"unknown"
278 | | empty 279 | |
"longtask"
287 | in {{PerformanceObserver/supportedEntryTypes}} for {{Window}} contexts, respectively.
288 |
289 | This allows developers to detect support for long tasks.
290 |
291 | Report long tasks {#report-long-tasks}
292 | --------------------------------------------------------
293 |
294 | null.
322 | 1. Process |task|'s [=script evaluation environment settings object set=] to determine |name| and |culpritSettings| as follows:
323 |
324 | 1. If |task|'s [=script evaluation environment settings object set=] is empty: set |name| to "[=unknown=]" and |culpritSettings| to null.
325 | 1. Otherwise, if |task|'s [=script evaluation environment settings object set=]'s length is greater than one: set |name| to "[=multiple-contexts=]" and |culpritSettings| to null.
326 | 1. Otherwise, i.e. if |task|'s [=script evaluation environment settings object set=]'s length is one:
327 | 1. Set |culpritSettings| to the single item in |task|'s [=script evaluation environment settings object set=].
328 | 1. Let |destinationSettings| be |destinationRealm|'s [=relevant settings object=].
329 | 1. Let |destinationOrigin| be |destinationSettings|'s [=environment settings object/origin=].
330 | 1. Let |destinationBC| be |destinationSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
331 | 1. Let |culpritBC| be |culpritSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
332 | 1. Assert: |culpritBC| is not null.
333 | 1. If |culpritSettings| is the same as |destinationSettings|, set |name| to "[=self=]".
334 | 1. Otherwise, if |culpritSettings|'s [=environment settings object/origin=] and |destinationOrigin| are [=same origin=]:
335 | 1. If |destinationBC| is null, set |name| to "[=same-origin=]".
336 | 1. Otherwise, if |culpritBC| is an [=ancestor=] of |destinationBC|, set |name| to "[=same-origin-ancestor=]".
337 | 1. Otherwise, if |destinationBC| is an [=ancestor=] of |culpritBC|, set |name| to "[=same-origin-descendant=]".
338 | 1. Otherwise, set |name| to "[=same-origin=]".
339 | 1. Otherwise:
340 | 1. If |destinationBC| is null, set |name| to "[=cross-origin-unreachable=]".
341 | 1. Otherwise, if |culpritBC| is an [=ancestor=] of |destinationBC|, set |name| to "[=cross-origin-ancestor=]" and set |culpritSettings| to null.
342 |
343 | NOTE: this is not reported because of security. Developers should look this up themselves.
344 |
345 | 1. Otherwise, if |destinationBC| is an [=ancestor=] of |culpritBC|, set |name| to "[=cross-origin-descendant=]".
346 | 1. Otherwise, set |name| to "[=cross-origin-unreachable=]".
347 |
348 | 1. Let |attribution| be a new {{TaskAttributionTiming}} object with |destinationRealm| and set its attributes as follows:
349 | 1. Set |attribution|'s {{PerformanceEntry/name}} attribute to "[=unknown=]".
350 |
351 | NOTE: future iterations of this API will add more values to the {{PerformanceEntry/name}} attribute of a {{TaskAttributionTiming}} object, but for now it can only be a single value.
352 |
353 | 1. Set |attribution|'s {{PerformanceEntry/entryType}} attribute to "taskattribution".
354 | 1. Set |attribution|'s {{PerformanceEntry/startTime}} and {{PerformanceEntry/duration}} to 0.
355 | 1. Set |attribution|'s {{containerType}} attribute to "window".
356 | 1. Set |attribution|'s {{containerName}} and {{containerSrc}} attributes to the empty string.
357 | 1. If |culpritSettings| is not null:
358 | 1. Let |culpritBC| be |culpritSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
359 | 1. Assert: |culpritBC| is not null.
360 | 1. Let |container| be |culpritBC|'s [=browsing context container=].
361 | 1. Assert: |container| is not null.
362 | 1. Set |attribution|'s {{containerId}} attribute to the value of |container|'s [=Element/ID=], or the empty string if the ID is unset.
363 | 1. If |container| is an <{iframe}> element:
364 | 1. Set |attribution|'s {{containerType}} attribute to "iframe".
365 | 1. Set |attribution|'s {{containerName}} attribute to the value of |container|'s <{iframe/name}> content attribute, or the empty string if the attribute is absent.
366 | 1. Set |attribution|'s {{containerSrc}} attribute to the value of |container|'s <{iframe/src}> content attribute, or the empty string if the attribute is absent.
367 |
368 | NOTE: it is intentional that we record the frame's <{iframe/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.
369 |
370 | 1. If |container| is a <{frame}> element:
371 | 1. Set |attribution|'s {{containerType}} attribute to "frame".
372 | 1. Set |attribution|'s {{containerName}} attribute to the value of |container|'s name content attribute, or the empty string if the attribute is absent.
373 | 1. Set |attribution|'s {{containerSrc}} attribute to the value of |container|'s src content attribute, or the empty string if the attribute is absent.
374 | 1. If |container| is an <{object}> element:
375 | 1. Set |attribution|'s {{containerType}} attribute to "object".
376 | 1. Set |attribution|'s {{containerName}} attribute to the value of |container|'s name content attribute, or the empty string if the attribute is absent.
377 | 1. Set |attribution|'s {{containerSrc}} attribute to the value of |container|'s <{object/data}> content attribute, or the empty string if the attribute is absent.
378 | 1. If |container| is an <{embed}> element:
379 | 1. Set |attribution|'s {{containerType}} attribute to "embed".
380 | 1. Set |attribution|'s {{containerName}} attribute to the empty string.
381 | 1. Set |attribution|'s {{containerSrc}} attribute to the value of |container|'s <{embed/src}> content attribute, or the empty string if the attribute is absent.
382 |
383 | 1. Create a new {{PerformanceLongTaskTiming}} object |newEntry| with |destinationRealm| and set its attributes as follows:
384 |
385 | 1. Set |newEntry|'s {{PerformanceEntry/name}} attribute to |name|.
386 | 1. Set |newEntry|'s {{PerformanceEntry/entryType}} attribute to "longtask".
387 | 1. Set |newEntry|'s {{PerformanceEntry/startTime}} attribute to the result of [=coarsen time|coarsening=] |start time| given |crossOriginIsolatedCapability|.
388 | 1. Let |dur| be the result of [=coarsen time|coarsening=] |end time| given |crossOriginIsolatedCapability|, minus |newEntry|'s {{PerformanceEntry/startTime}}.
389 | 1. Set |newEntry|'s {{PerformanceEntry/duration}} attribute to the integer part of |dur|.
390 | 1. If |attribution| is not null, set |newEntry|'s {{PerformanceLongTaskTiming/attribution}} attribute to a new frozen array containing the single value |attribution|.
391 |
392 | NOTE: future iterations of this API will add more values to the {{PerformanceLongTaskTiming/attribution}} attribute, but for now it only contains a single value.
393 |
394 | 1. [=Queue the PerformanceEntry=] |newEntry|.
395 | unknown")
419 | for tasks from the other 2.
420 | * Allow dropping the culprit/attribution information after a certain threshold. For instance, after 10 longtasks
421 | all entries would receive no attribution and their {{PerformanceEntry/name}} would be "unknown".
422 | * Add a built-in delay to the timing information exposed to make attacks dependent on longtask volume harder
423 | to execute.
424 |
425 | What is Exposed to Observers? {#what-is-exposed}
426 | --------------------------------------------------------
427 |
428 | All observers within the top level page (i.e. all iframes in the page and the main frame) will receive
429 | notifications about presence of long tasks. We expose the start time of the task, its duration (with 1 ms
430 | granularity), and a pointer to the culprit frame. This information can already be observed today, and with
431 | higher resolution, using setTimeout. An attacker can do this by clearing everything else on the page and adding
432 | the vulnerable cross-origin resource to ensure that delays from the setTimeout are caused by that resource.
433 | Observers in other different pages (tabs or windows) should not receive notifications, regardless of the
434 | architecture of the user agent.
435 |
436 | Cross origin rules for what is exposed:
437 | * Cross-origin observers may see the direction of the culprit e.g if the culprit is a deeply nested iframe,
438 | then the host page can see the first cross-origin between itself and the culprit.
439 | * Conversely, if the culprit is the top level page, then a deeply embedded iframe can see that a longtask
440 | occurrred in its cross-origin ancestor but does not receive any information about it.
441 |
442 | Attack Scenarios Considered {#attack-scenarios}
443 | -----------------------------------------------
444 |
445 | The following are the timing attacks considered:
446 |
447 | 1. Traditional timing attacks: using external resource load time to reveal the size of
448 | private data. For instance the number of hidden pictures in a gallery, whether username is
449 | valid, etc. See an example.
450 |
451 | 1. Side-channel timing attacks: using time for video parsing, script parsing, App Cache reads
452 | or Cache API (service workers) usage to uniquely identify a user, or to create a profile of the
453 | user’s age, gender, location, and interests etc. For
454 | instance, status updates from
455 | a social network can be limited to certain demographic (eg. females of age 20-30) the file size of
456 | the permalink page can be used to determine whether the user is in the target demographic.
457 |
458 | These scenarios are addressed by the 50ms threshold AND respecting cross-origin boundary i.e. not
459 | showing task type or additional attribution to untrusted cross origin observers.
460 |
--------------------------------------------------------------------------------