forked from w3c/longtasks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.bs
422 lines (329 loc) · 28.9 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
<pre class=metadata>
Title: Long Tasks API
Group: webperf
Status: ED
Shortname: longtasks
Level: 1
URL: https://w3c.github.io/longtasks/
Editor: Shubhie Panicker, Google https://google.com, panicker@chromium.org
Editor: Ilya Grigorik, Google https://google.com, igrigorik@chromium.org
Editor: Domenic Denicola, Google https://google.com, domenic@chromium.org
Repository: w3c/longtasks
Test Suite: http://w3c-test.org/longtask-timing/
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.
Status Text: If you wish to make comments regarding this document, please send them to <a href="mailto:public-web-perf@w3.org?subject=%5BLongTasks%5D">public-web-perf@w3.org</a> (<a href="mailto:public-web-perf-request@w3.org?subject=subscribe">subscribe</a>, <a href="https://lists.w3.org/Archives/Public/public-web-perf/">archives</a>) with <code nohighlight>[LongTasks]</code> at the start of your email's subject.
Default Highlight: js
</pre>
<style>
dl {
margin-left: 2em;
}
</style>
<pre class=anchors>
urlPrefix: https://w3c.github.io/performance-timeline/; spec: PERFORMANCE-TIMELINE-2;
type: interface; url: #the-performanceentry-interface; text: PerformanceEntry;
type: attribute; for: PerformanceEntry;
text: name; url: #dom-performanceentry-name
text: entryType; url: #dom-performanceentry-entrytype
text: startTime; url: #dom-performanceentry-starttime
text: duration; url: #dom-performanceentry-duration
type: dfn; url: #dfn-queue-a-performanceentry; text: Queue the PerformanceEntry
type: dfn; url: #dfn-register-a-performance-entry-type; text: register a performance entry type
type: attribute; for: PerformanceObserver;
text: supportedEntryTypes; url: #supportedentrytypes-attribute;
urlPrefix: https://w3c.github.io/hr-time/; spec: HR-TIME-2;
type: typedef; url: #idl-def-domhighrestimestamp; text: DOMHighResTimeStamp;
type: interface; url: #dfn-performance; text: Performance;
type: attribute; for: Performance;
text: now(); url: #dom-performance-now
type: dfn; text: current high resolution time; url: #dfn-current-high-resolution-time;
urlPrefix: https://html.spec.whatwg.org/multipage/; spec: HTML;
type: dfn; url: #event-loop; text: event loop;
type: dfn; url: #browsing-context; text: browsing context;
type: dfn; url: #calling-scripts; text: calling scripts;
type: dfn; url: #list-of-the-descendant-browsing-contexts; text: list of the descendant browsing contexts;
type: dfn; url: #ancestor-browsing-context; text: ancestor;
type: dfn; url: #unit-of-related-browsing-contexts; text: unit of related browsing contexts
type: dfn; url: #script-evaluation-environment-settings-object-set; text: script evaluation environment settings object set
type: dfn; url: #integration-with-the-javascript-agent-cluster-formalism; text: agent cluster
urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT;
type: dfn; url: #sec-code-realms; text: JavaScript Realms;
urlPrefix: https://dom.spec.whatwg.org/; spec: DOM;
type: attribute; for: Element;
text: id; url: #dom-element-id;
</pre>
<pre class=link-defaults>
spec:html; type:dfn; for:/; text:browsing context
</pre>
Introduction {#intro}
=====================
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 <a href="https://en.wikipedia.org/wiki/Real_user_monitoring">RUM</a> 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 <a href="https://developers.google.com/web/fundamentals/performance/rail">RAIL</a> 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.
Usage Example {#example}
------------------------
<pre class="example highlight">
const observer = new PerformanceObserver(function(list) {
for (const entry of 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.
</pre>
Terminology {#sec-terminology}
==============================
<dfn export>Long task</dfn> refers to any of the following occurrences whose duration exceeds 50ms:
* An event loop <a>task</a> plus the <a>perform a microtask checkpoint</a> that follows immediately afterwards. This captures the duration of an event loop <a>task</a>, including its associated <a>microtasks</a>.
* An <a>update the rendering</a> step within the <a>event loop processing model</a>.
* A pause between the last step and the next first step of the <a>event loop processing model</a>. This captures any work that the user agent performs in its UI thread outside of the <a>event loop</a>.
<dfn>Culprit browsing context container</dfn> refers to the <a>browsing context container</a> (<{iframe}>, <{object}>, etc.) that is being implicated, on the whole, for a <a>long task</a>.
<dfn>Attribution</dfn> refers to identifying the type of work (such as script, layout etc.) that contributed significantly to the long task, as well as identifying which <a>culprit browsing context container</a> is responsible for that work.
Long Task Timing {#sec-longtask-timing}
=======================================
Long Task timing involves the following new interfaces:
{{PerformanceLongTaskTiming}} interface {#sec-PerformanceLongTaskTiming}
------------------------------------------------------------------------
<pre class="idl">
[Exposed=Window]
interface PerformanceLongTaskTiming : PerformanceEntry {
readonly attribute FrozenArray<TaskAttributionTiming> attribution;
[Default] object toJSON();
};
</pre>
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.
The {{PerformanceEntry/name}} attribute's getter will return one of the following strings:
: "<code><dfn>unknown</dfn></code>"
:: The long task originated from work that the user agent performed outside of the <a>event loop</a>.
: "<code><dfn>self</dfn></code>"
:: The long task originated from an event loop <a>task</a> within this <a>browsing context</a>.
: "<code><dfn>same-origin-ancestor</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a <a lt="same origin">same-origin</a> <a>ancestor browsing context</a>.
: "<code><dfn>same-origin-descendant</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a <a lt="same origin">same-origin</a> <a lt="list of the descendant browsing contexts">descendant browsing context</a>.
: "<code><dfn>same-origin</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a <a lt="same origin">same-origin</a> <a>browsing context</a> that is not an ancestor or descendant.
: "<code><dfn>cross-origin-ancestor</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a cross-origin <a>ancestor browsing context</a>.
: "<code><dfn>cross-origin-descendant</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a cross-origin <a lt="list of the descendant browsing contexts">descendant browsing context</a>.
: "<code><dfn>cross-origin-unreachable</dfn></code>"
:: The long task originated from an event loop <a>task</a> within a cross-origin <a>browsing context</a> that is not an ancestor or descendant.
: "<code><dfn>multiple-contexts</dfn></code>"
:: The long task originated from an event loop <a>task</a> involving multiple <a>browsing contexts</a>.
The {{PerformanceEntry/entryType}} attribute's getter will return <code>"longtask"</code>.
The {{PerformanceEntry/startTime}} attribute's getter will return a {{DOMHighResTimeStamp}} of when the task started.
The {{PerformanceEntry/duration}} attribute's getter will return a {{DOMHighResTimeStamp}} equal to the elapsed time between the start and end of task.
The <dfn attribute for=PerformanceLongTaskTiming>attribution</dfn> attribute's getter will return a frozen array of {{TaskAttributionTiming}} entries.
{{TaskAttributionTiming}} interface {#sec-TaskAttributionTiming}
----------------------------------------------------------------
<pre class="def idl">
[Exposed=Window]
interface TaskAttributionTiming : PerformanceEntry {
readonly attribute DOMString containerType;
readonly attribute DOMString containerSrc;
readonly attribute DOMString containerId;
readonly attribute DOMString containerName;
[Default] object toJSON();
};
</pre>
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.
The {{PerformanceEntry/name}} attribute's getter will always return "<code>unknown</code>".
The {{PerformanceEntry/entryType}} attribute's getter will always return "<code>taskattribution</code>".
The {{PerformanceEntry/startTime}} attribute's getter will always return 0.
The {{PerformanceEntry/duration}} attribute's getter will always return 0.
The <dfn attribute for=TaskAttributionTiming>containerType</dfn> attribute's getter will return the type of the <a>culprit browsing context container</a>, such as "<code>iframe</code>", "<code>embed</code>", or "<code>object</code>". If no single <a>culprit browsing context container</a> is found, it will return "<code>window</code>".
The <dfn attribute for=TaskAttributionTiming>containerName</dfn> attribute's getter will return the value of the <a lt="culprit browsing context container">container</a>'s <code>name</code> content attribute. If no single <a>culprit browsing context container</a> is found, it will return the empty string.
The <dfn attribute for=TaskAttributionTiming>containerId</dfn> attribute's getter will return the value of the <a lt="culprit browsing context container">container</a>'s <code>id</code> content attribute. If no single <a>culprit browsing context container</a> is found, it will return the empty string.
The <dfn attribute for=TaskAttributionTiming>containerSrc</dfn> attribute's getter will return the value of the <a lt="culprit browsing context container">container</a>'s <code>src</code> content attribute. If no single <a>culprit browsing context container</a> is found, it will return the empty string.
Pointing to the culprit {#sec-PointingToCulprit}
------------------------------------------------
<div class=non-normative>
<em>This section is non-normative.</em>
A <a>long task</a> can involve different types of work (such as script, layout, style etc), and it could be executed within different <a>browsing contexts</a>, or it could be global in nature such as a long garbage collection that spans the entire <a>agent cluster</a> or <a>unit of related browsing contexts</a>.
Thus <a>attribution</a> has a couple of facets:
* Pointing to the origin of the long task and/or the overall location of the <a lt="culprit browsing context container">culprit browsing context</a>: this is referred to as <dfn>minimal culprit attribution</dfn> and is captured in the {{PerformanceEntry/name}} field.
* Pointing to the type of work involved in the <a>long task</a>, and its associated <a>culprit browsing context container</a>: this is captured in {{TaskAttributionTiming}} objects in the {{PerformanceLongTaskTiming/attribution}} field of {{PerformanceLongTaskTiming}}.
Therefore, {{PerformanceEntry/name}} and {{PerformanceLongTaskTiming/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:
<table>
<thead>
<tr>
<th>{{PerformanceEntry/name}}</th>
<th><a>Culprit browsing context container</a> implicated by {{PerformanceLongTaskTiming/attribution}}</th>
<tbody>
<tr>
<td>"<code><a>self</a></code>"
<td>empty
<tr>
<td>"<code><a>same-origin-ancestor</a></code>"
<td>same-origin culprit
<tr>
<td>"<code><a>same-origin-descendant</a></code>"
<td>same-origin culprit
<tr>
<td>"<code><a>same-origin</a></code>"
<td>same-origin culprit
<tr>
<td>"<code><a>cross-origin-ancestor</a></code>"
<td>empty
<tr>
<td>"<code><a>cross-origin-descendant</a></code>"
<td>empty
<tr>
<td>"<code><a>cross-origin-unreachable</a></code>"
<td>empty
<tr>
<td>"<code><a>multiple-contexts</a></code>"
<td>empty
<tr>
<td>"<code><a>unknown</a></code>"
<td>empty
</table>
</div>
Processing model {#sec-processing-model}
========================================
Note: A user agent implementing the Long Tasks API would need to include <code>"longtask"</code> in {{PerformanceObserver/supportedEntryTypes}} for {{Window}} contexts.
This allows developers to detect support for long tasks.
Report long tasks {#report-long-tasks}
--------------------------------------------------------
<div algorithm="Report long tasks">
Given |start time|, |end time|, |top-level browsing contexts|, and |task|, perform the following algorithm:
1. If |end time| minus |start time| is less than the long tasks threshold of 50 ms, abort these steps.
1. Let |destinationRealms| be an empty set.
1. Determine the set of [=JavaScript Realms=] to which reports will be delivered:
For each [=top-level browsing context=] |topmostBC| in |top-level browsing contexts|:
1. Add |topmostBC|'s [=active document=]'s [=relevant Realm=] to |destinationRealms|.
1. Let |descendantBCs| be |topmostBC|'s [=active document=]'s [=list of the descendant browsing contexts=].
1. For each |descendantBC| in |descendantBCs|, add |descendantBC|'s [=active document=]'s [=relevant Realm=] to |destinationRealms|.
1. A user agent may remove some [=JavaScript Realms=] from |destinationRealms|.
Note: this removal could be used to avoid reporting long tasks for [=JavaScript Realms=] that the user agent handles in a separate process. However, this concept is not specified precisely.
Issue(75): there is some ongoing discussion regarding the scope of which {{Document|Documents}} gain visibility over which long tasks, so this logic could change in the future.
1. For each |destinationRealm| in |destinationRealms|:
1. Let |name| be the empty string. This will be used to report [=minimal culprit attribution=], below.
1. Let |culpritSettings| be <code>null</code>.
1. Process |task|'s [=script evaluation environment settings object set=] to determine |name| and |culpritSettings| as follows:
1. If |task|'s [=script evaluation environment settings object set=] is empty: set |name| to "<code>[=unknown=]</code>" and |culpritSettings| to <code>null</code>.
1. Otherwise, if |task|'s [=script evaluation environment settings object set=]'s length is greater than one: set |name| to "<code>[=multiple-contexts=]</code>" and |culpritSettings| to <code>null</code>.
1. Otherwise, i.e. if |task|'s [=script evaluation environment settings object set=]'s length is one:
1. Set |culpritSettings| to the single item in |task|'s [=script evaluation environment settings object set=].
1. Let |destinationSettings| be |destinationRealm|'s [=Realm/settings object=].
1. Let |destinationOrigin| be |destinationSettings|'s [=environment settings object/origin=].
1. Let |destinationBC| be |destinationSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
1. Let |culpritBC| be |culpritSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
1. Assert: |culpritBC| is not <code>null</code>.
1. If |culpritSettings| is the same as |destinationSettings|, set |name| to "<code>[=self=]</code>".
1. Otherwise, if |culpritSettings|'s [=environment settings object/origin=] and |destinationOrigin| are [=same origin=]:
1. If |destinationBC| is <code>null</code>, set |name| to "<code>[=same-origin=]</code>".
1. Otherwise, if |culpritBC| is an [=ancestor=] of |destinationBC|, set |name| to "<code>[=same-origin-ancestor=]</code>".
1. Otherwise, if |destinationBC| is an [=ancestor=] of |culpritBC|, set |name| to "<code>[=same-origin-descendant=]</code>".
1. Otherwise, set |name| to "<code>[=same-origin=]</code>".
1. Otherwise:
1. If |destinationBC| is <code>null</code>, set |name| to "<code>[=cross-origin-unreachable=]</code>".
1. Otherwise, if |culpritBC| is an [=ancestor=] of |destinationBC|, set |name| to "<code>[=cross-origin-ancestor=]</code>" and set |culpritSettings| to <code>null</code>.
NOTE: this is not reported because of security. Developers should look this up themselves.
1. Otherwise, if |destinationBC| is an [=ancestor=] of |culpritBC|, set |name| to "<code>[=cross-origin-descendant=]</code>".
1. Otherwise, set |name| to "<code>[=cross-origin-unreachable=]</code>".
1. Let |attribution| be a new {{TaskAttributionTiming}} object with |destinationRealm| and set its attributes as follows:
1. Set |attribution|'s {{PerformanceEntry/name}} attribute to "<code>[=unknown=]</code>".
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.
1. Set |attribution|'s {{PerformanceEntry/entryType}} attribute to <code>"taskattribution"</code>.
1. Set |attribution|'s {{PerformanceEntry/startTime}} and {{PerformanceEntry/duration}} to 0.
1. Set |attribution|'s {{containerType}} attribute to <code>"window"</code>.
1. Set |attribution|'s {{containerName}} and {{containerSrc}} attributes to the empty string.
1. If |culpritSettings| is not <code>null</code>:
1. Let |culpritBC| be |culpritSettings|'s [=environment settings object/global object=]'s [=Window/browsing context=].
1. Assert: |culpritBC| is not <code>null</code>.
1. Let |container| be |culpritBC|'s [=browsing context container=].
1. Assert: |container| is not <code>null</code>.
1. Set |attribution|'s {{containerId}} attribute to the value of |container|'s [=Element/ID=], or the empty string if the ID is unset.
1. If |container| is an <{iframe}> element:
1. Set |attribution|'s {{containerType}} attribute to "<code>iframe</code>".
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.
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.
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.
1. If |container| is a <{frame}> element:
1. Set |attribution|'s {{containerType}} attribute to "<code>frame</code>".
1. Set |attribution|'s {{containerName}} attribute to the value of |container|'s <code>name</code> content attribute, or the empty string if the attribute is absent.
1. Set |attribution|'s {{containerSrc}} attribute to the value of |container|'s <code>src</code> content attribute, or the empty string if the attribute is absent.
1. If |container| is an <{object}> element:
1. Set |attribution|'s {{containerType}} attribute to "<code>object</code>".
1. Set |attribution|'s {{containerName}} attribute to the value of |container|'s <a href="https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-object-name">name</a> content attribute, or the empty string if the attribute is absent.
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.
1. If |container| is an <{embed}> element:
1. Set |attribution|'s {{containerType}} attribute to "<code>embed</code>".
1. Set |attribution|'s {{containerName}} attribute to the empty string.
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.
1. Create a new {{PerformanceLongTaskTiming}} object |newEntry| with |destinationRealm| and set its attributes as follows:
1. Set |newEntry|'s {{PerformanceEntry/name}} attribute to |name|.
1. Set |newEntry|'s {{PerformanceEntry/entryType}} attribute to "<code>longtask</code>".
1. Set |newEntry|'s {{PerformanceEntry/startTime}} attribute to |start time|.
1. Set |newEntry|'s {{PerformanceEntry/duration}} attribute to |end time| minus |start time|.
1. If |attribution| is not <code>null</code>, set |newEntry|'s {{PerformanceLongTaskTiming/attribution}} attribute to a new frozen array containing the single value |attribution|.
NOTE: future iterations of this API will add more values to the {{PerformanceLongTaskTiming/attribution}} attribute, but for now it only contains a single value.
1. [=Queue the PerformanceEntry=] |newEntry|.
</div>
Security & privacy considerations {#priv-sec}
===============================================
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 cross-origin leaks.
The Long Tasks API provides timing information about the duration and type of tasks executed by the user,
as well as attribution such as the browsing context causing the function calls. This could enable an attacker
to perform side-channel timing attacks to guess the user’s action, or identify the user. For example, a pattern of
long script followed by a long render could be put together to guess user’s interaction with a social
widget. Detailed function call attribution would be used to determine the user’s action.
While the API doesn’t introduce any new privacy attacks, it could make existing privacy attacks faster.
Mitigations for this are possible and can be implemented as needed:
* Further clamp the long task duration provided by the API to make attacks harder to exploit (i.e. round the
result or add random jitter to the value).
* Limit the number of origins for which longtasks are exposed by the API, and obfuscate the attribution of
any tasks afterwards. For instance, a page with 5 iframes could receive only attribution for tasks from 3
of those iframes, and would receive no attribution ({{PerformanceEntry/name}} set to <code>unknown</code>")
for tasks from the other 2.
* Allow dropping the culprit/attribution information after a certain threshold. For instance, after 10 longtasks
all entries would receive no attribution and their {{PerformanceEntry/name}} would be "<code>unknown</code>".
* Add a built-in delay to the timing information exposed to make attacks dependent on longtask volume harder
to execute.
What is Exposed to Observers? {#what-is-exposed}
--------------------------------------------------------
All observers within the top level page (i.e. all iframes in the page and the main frame) will receive
notifications about presence of long tasks. We expose the start time of the task, its duration, and a
pointer to the culprit frame. This information can already be observed today, and with higher resolution,
using setTimeout. An attacker can do this by clearing everything else on the page and adding the
vulnerable cross-origin resource to ensure that delays from the setTimeout are caused by that resource.
Observers in other different pages (tabs or windows) should not receive notifications, regardless of the
architecture of the user agent.
Cross origin rules for what is exposed:
* Cross-origin observers may see the direction of the culprit e.g if the culprit is a deeply nested iframe,
then the host page can see the first cross-origin between itself and the culprit.
* Conversely, if the culprit is the top level page, then a deeply embedded iframe can see that a longtask
occurrred in its cross-origin ancestor but does not receive any information about it.
Attack Scenarios Considered {#attack-scenarios}
--------------------------------------------------------
The following are the timing attacks considered:
1. <b>Traditional timing attacks</b>: using external resource load time to reveal the size of
private data. For instance the number of hidden pictures in a gallery, whether username is
valid, etc. See an <a href="http://crypto.stanford.edu/~dabo/papers/webtiming.pdf">example</a>.
1. <b>Side-channel timing attacks</b>: using time for video parsing, script parsing, App Cache reads
or Cache API (service workers) usage to uniquely identify a user, or to create a profile of the
user’s age, gender, location, and interests etc. For
<a href="https://tom.vg/papers/timing-attacks_ccs2015.pdf">instance</a>, status updates from
a social network can be limited to certain demographic (eg. females of age 20-30) the file size of
the permalink page can be used to determine whether the user is in the target demographic.
These scenarios are addressed by the 50ms threshold AND respecting cross-origin boundary i.e. not
showing task type or additional attribution to untrusted cross origin observers.