长动画帧 API

W3C 首次公开工作草案,

关于本文档的更多详细信息
本版本:
https://www.w3.org/TR/2026/WD-long-animation-frames-20260428/
最新发布版本:
https://www.w3.org/TR/long-animation-frames/
编辑草案:
https://w3c.github.io/long-animation-frames/
历史:
https://www.w3.org/standards/history/long-animation-frames/
测试套件:
https://wpt.fyi/results/long-animation-frames
反馈:
GitHub
规范内嵌
编辑:
Google

摘要

本文档定义了一个 API,网页作者可以用它来检测“长动画帧”的存在,这些帧会长时间独占 UI 线程并阻塞其他关键任务的执行——例如响应用户输入。

本文档的状态

本节描述本文档在发布时的状态。 当前 W3C 出版物列表 以及本技术报告的最新修订版 可在 W3C 标准和草案索引中找到。

本文档由 Web 性能工作组 作为 首次公开工作草案 发布,并使用 推荐标准轨道。 作为首次公开工作草案发布 并不意味着 W3C 及其成员认可。

这是一份草案文档, 可能会在任何时候被其他文档更新、替代 或废弃。 除作为进行中的工作引用外,不应引用本文档。

GitHub Issues 是讨论 本规范的首选方式。

本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。

本文档由一个依据 W3C 专利政策 运作的小组制作。 W3C 维护一份 公开的专利披露列表, 列出与该小组交付成果相关的任何披露; 该页面还包括披露专利的说明。 如果某个人实际知晓某项专利,并认为该专利 包含必要权利要求, 则必须按照 W3C 专利政策第 6 节 披露该信息。

如果你希望就本文档发表评论,请在 Long Animation Frames 仓库中创建 issue。

1. 引言

在页面加载时,以及之后用户与页面交互时,应用和浏览器都会将各种事件排入队列,然后由浏览器执行—— 例如,用户代理会根据用户的活动调度输入事件,应用会为 requestAnimationFrame 和其他回调 调度回调, 等等。一旦进入队列,浏览器就会逐个出队这些事件并执行它们。

然而,某些任务可能会花费很长时间(多个帧),如果/当这种情况发生时,UI 线程可能会被 阻塞,并且也阻塞所有其他任务。对用户来说,这通常表现为一个“卡死”的页面,其中 浏览器无法响应用户输入;这是当今 Web 上糟糕用户体验的一个主要来源:

延迟的“可交互时间”:

当页面正在加载,甚至已经完全视觉渲染完成时,长任务通常会占用主 线程,并阻止用户与页面交互。设计不佳的第三方内容 经常是罪魁祸首。

高/可变的输入延迟:

关键的用户交互事件(例如 tap、click、scroll、wheel 等)排在长任务之后, 这会产生卡顿且不可预测的用户体验。

高/可变的事件处理延迟:

与输入类似,处理事件回调(例如 onload 事件等)会延迟应用更新。

卡顿的动画和滚动:

某些动画和滚动交互需要合成器线程与主线程之间进行协调; 如果长任务阻塞了主线程,它就会影响动画和滚动的响应性。

一些应用(以及 RUM 供应商)已经 在尝试识别和跟踪发生“长任务”的情况。例如,一个已知模式是 安装一个较短的周期性计时器,并检查连续触发之间经过的时间:如果 经过的时间大于计时器周期,那么很可能有一个或多个长任务 延迟了事件循环的执行。这种方法大体可行,但有若干不良的性能 影响:通过轮询检测长任务,应用会阻止静止状态和长空闲块(见 requestIdleCallback);它对电池续航不利;也无法知道是什么导致了延迟(例如 第一方代码还是第三方代码)。

RAIL 性能模型 建议应用应在小于 100ms 内响应用户输入(对于触摸移动和滚动, 阈值为 16ms)。此 API 的目标是公开关于可能阻止 应用达到这些目标的任务的通知。此 API 会公开耗时 50ms 或更长的任务。没有 这些任务的网站应能在 100ms 以内响应用户输入:当接收到用户输入时, 完成正在执行的任务将少于 50ms,并且执行用于响应此类 用户输入的任务也少于 50ms。

1.1. 使用示例

const observer = new PerformanceObserver(function(list) {
    for (const entry of list.getEntries()) {
        // 处理长任务通知:
        // 回报给分析和监控系统
        // ...
    }
});
// 为过去和未来的长任务通知注册观察者。
observer.observe({type: "long-animation-frame", buffered: true});
// 此后的长脚本执行将导致在观察者中排队
// 并接收 "long-animation-frame" 条目。

// 为过去和未来的长动画帧通知注册观察者。
// 此后,主线程忙碌的长时间段将导致在观察者中排队
// 并接收 "long-animation-frame" 条目。
observer.observe({type: "long-animation-frame", buffered: true});

1.2. 长动画帧与长任务

虽然 long tasks 和长动画帧都衡量拥塞和卡顿,但长 动画帧 提供的信息与用户如何感知这类拥塞具有更好的相关性。 这是因为长动画帧衡量的是一个序列:该序列在主线程空闲时开始, 并在该帧渲染完成,或用户代理决定没有内容需要渲染时结束。

task 这个术语在某种程度上属于实现细节,而长动画 帧的补充 试图通过引入一种更以用户为中心的指标,来补救同一种主 线程拥塞/卡顿现象。

由于长动画帧保证最多只有一个渲染阶段,我们也可以 使用它们公开关于渲染阶段本身的额外信息,例如 renderStartstyleAndLayoutStart

2. 长动画帧计时

long animation frame 指以下任一持续时间超过 50ms 的情况:

长动画帧计时涉及以下新接口:

2.1. PerformanceLongAnimationFrameTiming 接口

[Exposed=Window]
interface PerformanceLongAnimationFrameTiming : PerformanceEntry {
    /* Overloading PerformanceEntry */
    readonly attribute DOMHighResTimeStamp startTime;
    readonly attribute DOMHighResTimeStamp duration;
    readonly attribute DOMString name;
    readonly attribute DOMString entryType;

    readonly attribute DOMHighResTimeStamp renderStart;
    readonly attribute DOMHighResTimeStamp styleAndLayoutStart;
    readonly attribute DOMHighResTimeStamp blockingDuration;
    readonly attribute DOMHighResTimeStamp firstUIEventTimestamp;
    [SameObject] readonly attribute FrozenArray<PerformanceScriptTiming> scripts;
    [Default] object toJSON();
};

PerformanceLongAnimationFrameTiming includes PaintTimingMixin;

一个 PerformanceLongAnimationFrameTiming 具有一个 frame timing info timing info

entryType 属性的 getter 步骤是返回 "long-animation-frame"

name 属性的 getter 步骤是返回 "long-animation-frame"

startTime 属性的 getter 步骤是返回给定 relative high resolution time,其参数为 thistiming infostart time 以及 thisrelevant global object

duration 属性的 getter 步骤是返回 duration,该持续时间位于 thisstartTime 与给定 relative high resolution time 之间,后者的参数为 thistiming infoend time 以及 thisrelevant global object

renderStart 属性的 getter 步骤是返回给定 relative high resolution time,其参数为 thistiming infoupdate the rendering start time 以及 thisrelevant global object

styleAndLayoutStart 属性的 getter 步骤是返回给定 relative high resolution time,其参数为 thistiming infostyle and layout start time 以及 thisrelevant global object

firstUIEventTimestamp 属性的 getter 步骤是返回给定 relative high resolution time,其参数为 thistiming infofirst ui event timestamp 以及 thisrelevant global object

blockingDuration 属性的 getter 步骤是:

  1. sortedTaskDurationstiming infotask durations,并按 降序排序

  2. 如果 thistiming infoupdate the rendering start time 不是零,则:

    1. renderDurationduration,位于 thisrenderStart 与 给定 relative high resolution time 之间, 该时间的参数为 thistiming infoend time

    2. sortedTaskDurations[0] 增加 renderDuration

      注: 这样可以使最长的 任务持续时间 + 渲染持续时间在其总 持续时间 >50ms 时被视为阻塞。

  3. totalBlockingDuration 为 0。

  4. sortedTaskDurations 中的每个 duration 执行,如果 duration 大于 50,则将 totalBlockingDuration 增加 duration - 50。

  5. 返回 totalBlockingDuration

scripts 属性的 getter 步骤是:

  1. scripts 为一个 list « »。

  2. entryWindowthisrelevant global object

  3. 每个 scriptInfo,其位于 thisframe timing infoscripts 中:

    1. scriptWindowscriptInfowindow

    2. scriptEntrythisrelevant realm 中的一个新的 PerformanceScriptTiming, 其 timing infoscriptInfo,且其 window attribution 是 与第一个匹配语句对应的值:

      scriptWindow 为 undefined

      other

      scriptWindowentryWindow

      self

      entryWindow 的关联 Documentnode navigableancestor navigables contains scriptWindow 的关联 Documentnode navigable

      ancestor

      scriptWindow 的关联 Documentnode navigableancestor navigables contains entryWindow 的关联 Documentnode navigable

      descendant

      entryWindow 的关联 Documentnode navigabletop-level traversablescriptWindow 的 关联 Documentnode navigabletop-level traversable

      same-page

      否则

      other

    3. scriptEntry Appendscripts

  4. 返回 scripts

2.2. PerformanceScriptTiming 接口

enum ScriptInvokerType {
    "classic-script",
    "module-script",
    "event-listener",
    "user-callback",
    "resolve-promise",
    "reject-promise"
};

enum ScriptWindowAttribution {
    "self", "descendant", "ancestor", "same-page", "other"
};

[Exposed=Window]
interface PerformanceScriptTiming : PerformanceEntry {
    /* Overloading PerformanceEntry */
    readonly attribute DOMHighResTimeStamp startTime;
    readonly attribute DOMHighResTimeStamp duration;
    readonly attribute DOMString name;
    readonly attribute DOMString entryType;

    readonly attribute ScriptInvokerType invokerType;
    readonly attribute DOMString invoker;
    readonly attribute DOMHighResTimeStamp executionStart;
    readonly attribute DOMString sourceURL;
    readonly attribute DOMString sourceFunctionName;
    readonly attribute long long sourceCharPosition;
    readonly attribute DOMHighResTimeStamp pauseDuration;
    readonly attribute DOMHighResTimeStamp forcedStyleAndLayoutDuration;
    readonly attribute Window? window;
    readonly attribute ScriptWindowAttribution windowAttribution;
    [Default] object toJSON();
};

一个 PerformanceScriptTiming 具有关联的 script timing info timing info

一个 PerformanceScriptTiming 具有关联的 ScriptWindowAttribution window attribution

entryType 属性的 getter 步骤是返回 "script"

name 属性的 getter 步骤是返回 "script"

invokerType 属性的 getter 步骤是返回 thistiming infoinvoker type

invoker 属性的 getter 步骤是:

  1. thisinvokerType 执行 switch:

    "`classic-script`"
    "`module-script`"

    返回 thistiming infosource url

    "`event-listener`"
    1. targetNamethistiming infoinvoker name

    2. 如果 thistiming infoevent target element id 不是空字符串,则: 将 targetName 设为 « targetName, "#", thistiming infoevent target element id » 的 concatenation

    3. 否则,如果 thistiming infoevent target element src attribute 不是空字符串,则: 将 targetName 设为 « targetName, "[src=", thistiming infoevent target element src attribute, "]" » 的 concatenation

    4. 返回 « targetName, ".on", thistiming infoevent type » 的 concatenation

    这感觉 有点定制化,需要讨论名称生成。

    "`user-callback`"

    返回 thistiming infoinvoker name

    "`resolve-promise`"
    "`reject-promise`"
    1. 如果 thistiming infoinvoker name 是空 字符串, 则:

      1. 如果 thisinvokerType 是 "`resolve-promise`",则返回 "`Promise.resolve`"。

      2. 否则,返回 "`Promise.reject`"。

    2. 如果 invokerType 是 "`resolve-promise`",则令 thenOrCatch 为 "`then`";否则为 "`reject-promise`"。

    3. 返回 « invoker name, ".", thenOrCatch » 的 concatenation

startTime 属性的 getter 步骤是返回给定 relative high resolution time,其参数为 thistiming infostart time 以及 thisrelevant global object

duration 属性的 getter 步骤是返回 duration,该持续时间位于 thisstartTime 与给定 relative high resolution time 之间,后者的参数为 thistiming infoend time 以及 thisrelevant global object

executionStart 属性的 getter 步骤是:如果 thistiming infoexecution start time 为 0,则返回 0;否则返回给定 relative high resolution time,其参数为 thistiming infoexecution start time 以及 thisrelevant global object

forcedStyleAndLayoutDuration 属性的 getter 步骤是返回一个 implementation-defined 值,表示同步执行 样式和布局所花费的时间,例如调用 getComputedStyle()getBoundingClientRect()

找到一种方式使其 可互操作/规范化。也许在 WebIDL 中标记这些函数为需要同步样式/布局?并且 一旦解决,也将其移动到 timing info

pauseDuration 属性的 getter 步骤是返回 thistiming infopause duration

sourceURL 属性的 getter 步骤是返回 thistiming infosource urlsourceFunctionName 属性的 getter 步骤是返回 thistiming infosource function namesourceCharPosition 属性的 getter 步骤是返回 thistiming infosource character position

window 属性的 getter 步骤是:

  1. window 为在 thistiming infowindow 上调用 deref 的结果。

  2. 如果 window 为 undefined,则返回 null;否则返回 window

windowAttribution 属性的 getter 步骤是返回 thiswindow attribution

3. 处理模型

注: 实现 Long Animation Frame API 的用户代理需要分别为 Window 上下文,在 supportedEntryTypes 中包含 "long-animation-frame"

3.1. 帧计时信息

frame timing info 是一个 struct, 被长动画帧算法用作记账细节。 它具有以下 items
start time
current task start time
update the rendering start time
style and layout start time
first ui event timestamp
end time

一个 DOMHighResTimeStamp, 初始值为 0。 注:以上所有时间都是 unsafe 的, 并且在通过 API 暴露时应当被 coarsened

task durations

一个由 DOMHighResTimeStamp 组成的 list, 初始为空。

scripts

一个由 script timing info 组成的 list, 初始为空。

pending script

Null 或一个 script timing info,初始为 null。

script timing info 是 一个 struct。它 具有以下 items

invoker type

一个 ScriptInvokerType

start time
end time
execution start time

一个 unsafe DOMHighResTimeStamp, 初始值为 0。

pause duration

一个 DOMHighResTimeStamp, 表示毫秒数,初始值为 0。

invoker name
source url
source function name
event type
event target element id
event target element src attribute

一个字符串,初始为空字符串。

source character position

一个数字,初始值为 -1。

window

一个指向 WindowWeakRef

一个 Document 具有 null 或 frame timing info current frame timing info,初始为 null。

3.2. 报告长动画帧

3.2.1. 长动画帧监控 {#loaf-monitoring}

要获取 Document documentnearest same-origin root
  1. ancestorsdocumentancestor navigables

  2. ancestors 中的每个 ancestorNavigable 执行: 如果 ancestorNavigableactive documentorigindocumentoriginsame origin, 并且 ancestorNavigableactive documentrelevant agentdocumentrelevant agent, 则返回 ancestorNavigableactive document

  3. 返回 document

Document documentrelevant frame timing info 是其 nearest same-origin rootcurrent frame timing info

要在给定一个 DOMHighResTimeStamp unsafeTaskStartTime 和一个 Document document 的情况下,record task start time
  1. rootdocumentnearest same-origin root

  2. 如果 rootcurrent frame timing info 为 null, 则将 rootcurrent frame timing info 设为一个新的 frame timing info,其 start timeunsafeTaskStartTime

  3. rootcurrent frame timing infocurrent task start time 设为 unsafeTaskStartTime

  4. 如果 rootcurrent frame timing info 的 's start time 为 0,则将 rootcurrent frame timing infostart time 设为 unsafeTaskStartTime

要在给定一个 DOMHighResTimeStamp unsafeTaskEndTime 和一个 Document document 的情况下,record task end time
  1. timingInfodocumentrelevant frame timing info

  2. 如果 timingInfo 为 null,则返回。

    注: 如果浏览器在该序列期间 变为隐藏状态,则可能发生这种情况。

  3. safeTaskEndTime 为给定 unsafeTaskEndTimedocumentrelevant global objectrelative high resolution time

  4. safeTaskStartTime 为给定 timingInfocurrent task start timedocumentrelevant global objectrelative high resolution time

  5. safeTaskStartTimesafeTaskEndTime 之间的 duration AppendtimingInfotask durations

  6. 如果用户代理认为更新 documentnode navigable 的渲染不会产生可见效果,则:

    1. documentnearest same-origin rootcurrent frame timing info 设为 null。

    2. frameDuration 为给定 timingInfostart timeglobalrelative high resolution time 与给定 unsafeTaskEndTimeglobalrelative high resolution time 之间的 duration

    3. 如果 frameDuration 大于或等于 50 毫秒,则在给定 documenttimingInfo 和一个新的 [/=paint timing info=] 的情况下,queue a long animation frame entry

    注: 尽管没有实际的视觉 更新,我们仍在这里标记一个 long animation frame,因为在它与无关的视觉更新同时发生的场景中, 它会造成阻塞。

要在给定一个 Document document 和一个 DOMHighResTimeStamp unsafeStyleAndLayoutStart 的情况下,record rendering time
  1. timingInfodocumentrelevant frame timing info

  2. 如果 timingInfo 为 null,则返回。

    注: 如果浏览器在该序列期间 变为隐藏状态,则可能发生这种情况。

  3. frameDuration 为给定 timingInfostart timeglobalrelative high resolution time 与给定 globalcurrent high resolution time 之间的 duration

  4. 如果 frameDuration 小于 50 毫秒,则将 documentnearest same-origin rootcurrent frame timing info 设为 null 并返回。

  5. timingInfoupdate the rendering start time 设为 timingInfocurrent task start time

  6. timingInfostyle and layout start time 设为 unsafeStyleAndLayoutStart

要在给定一个 Document document、一个 frame timing info timingInfo 和一个 paint timing info paintTimingInfo 的情况下,queue a long animation frame entry, 在 documentrelevant realmqueue 一个新的 PerformanceLongAnimationFrameTiming, 其 timing infotimingInfo, 且其 paint timing infopaintTimingInfo

3.2.2. 长脚本监控

要在给定一个 callback function callback 和一个 environment settings object settings 的情况下,record timing info for user callback: 给定 settings、"`user-callback`" 和以下步骤,Create script entry point,该步骤给定一个 script timing info scriptTimingInfo
  1. scriptTimingInfoinvoker name 设为 callbackidentifier

  2. 给定 callback,为 scriptTimingInfo Apply source location

要在给定一个字符串或 Function handler、一个 environment settings object settings 和一个 布尔值 repeat 的情况下,record timing info for timer handler: 给定 settings、"`user-callback`" 以及以下步骤,Create script entry point,该步骤给定一个 script timing info scriptTimingInfo
  1. 如果 repeat 为 true,则令 setTimeoutOrInterval 为 "setInterval", 否则为 "setTimeout"。

  2. scriptTimingInfoinvoker name 设为 « TimerHandler:", setTimeoutOrInterval » 的 concatenation

  3. 如果 handler 是一个 Function, 则给定 handler,为 scriptTimingInfo apply source location

要在给定一个 Event event 和一个 EventListener listener 的情况下,record timing info for event listener: 给定 listenerrelevant settings object、"`event-listener`" 以及以下步骤,Create script entry point,该步骤给定一个 script timing info scriptTimingInfo 和一个 frame timing info frameTimingInfo
  1. scriptTimingInfoevent type 设为 eventtype

  2. targeteventcurrentTarget

  3. 如果 target 是一个 Node, 则:

    1. scriptTimingInfoinvoker name 设为 targetnodeName

    2. 如果 target 是一个 Element, 则:

      1. scriptTimingInfoevent target element id 设为 targetid

      2. scriptTimingInfoevent target element src attribute 设为给定 "`src`" 和 target getting an attribute value by name 的结果。

  4. 否则,将 scriptTimingInfoinvoker name 设为 target 的 interface name。

  5. 给定 listenercallback,为 scriptTimingInfo Apply source location

  6. 如果 event 是一个 UIEvent, 并且 frameTimingInfofirst ui event timestamp 为 0, 则将 frameTimingInfofirst ui event timestamp 设为 eventtimeStamp

要在给定一个 Promise promise 和一个 "`resolve-promise`" 或 "`reject-promise`" type 的情况下,record timing info for promise resolver
  1. 给定 promiserelevant realmsettings objecttype 和以下步骤,Create script entry point,该步骤给定一个 script timing info scriptTimingInfo

    1. scriptTimingInfoinvoker name 设为 promiseinvoker name when created

    2. scriptTimingInfosource url 设为 promisescript url when created

要在给定一个 script timing info scriptTimingInfo、一个 script script 和一个 URL-or-null url 的情况下,set source url for script block
  1. 如果 url 为 null,则返回。

  2. 如果 urlscheme 是 "`http`" 或 "`https`",则将 scriptTimingInfosource url 设为 scriptbase URL

  3. 否则,如果 urlscheme 是 "`blob`" 或 "`data`",则将 scriptTimingInfosource url 设为 « urlscheme, ":"" » 的 concatenation

要在给定一个 script script 和一个 URL-or-null originalSourceURL 的情况下,record classic script creation time
  1. scriptsettings object、"`classic-script`" 和以下步骤 Create script entry point,该步骤给定一个 script timing info scriptTimingInfo: 给定 scriptTimingInfoscriptoriginalSourceURLSet source url for script block

要在给定一个 classic script script 的情况下,record classic script execution start time
  1. settingsscriptsettings object

  2. 如果 scriptmuted errors 为 true,则返回。

  3. 如果 settings 不是一个 Window, 则返回。

  4. documentsettingsdocument

  5. frameTimingInfodocumentrelevant frame timing info

  6. 如果 frameTimingInfo 为 null,或如果 frameTimingInfopending script 不是 null,则返回。

  7. 断言:frameTimingInfopending scriptinvoker type 是 "`classic-script`"。

  8. frameTimingInfopending scriptexecution start time 设为 unsafe shared current time

要在给定一个 module script script 的情况下,record module script execution start time: 用 scriptsettings object、"`module-script`" 和以下步骤 Create script entry point,该步骤给定一个 script timing info scriptTimingInfo
  1. scriptTimingInfoexecution start time 设为 scriptscriptTimingInfostart time

  2. 给定 scriptTimingInfoscriptscriptbase URLSet source url for script block

要在给定一个 environment settings object settings、 一个 ScriptInvokerType invokerTypesteps 的情况下,create script entry point, 其中 steps 是一个接收 script timing info 和可选的 frame timing info 的算法:
  1. 如果 settings 不是一个 Window, 则返回。

  2. documentsettingsdocument

  3. 如果 document 不是 fully active,或 hidden, 则返回。

  4. frameTimingInfodocumentrelevant frame timing info

  5. 如果 frameTimingInfo 为 null,则返回。

  6. 如果 frameTimingInfopending script 不是 null,则返回。

  7. scriptTimingInfo 为一个新的 script timing info, 其 start timeunsafe shared current time, 并且其 invoker typeinvokerType

  8. 给定 scriptTimingInfoframeTimingInfo,运行 steps

  9. scriptTimingInfowindow 设为 settings

  10. frameTimingInfopending script 设为 scriptTimingInfo

record timing info for microtask checkpoint
  1. scriptrunning script

  2. settingsscriptsettings object

  3. documentsettingsdocument

  4. 如果 document 不是 fully active,或 hidden, 则返回。

  5. frameTimingInfodocumentrelevant frame timing info

  6. scriptTimingInfoframeTimingInfopending script

  7. frameTimingInfopending script 设为 null。

  8. 如果 scriptTimingInfo 为 null,则返回。

  9. scriptTimingInfoend time 设为 unsafe shared current time

  10. 如果 script 是一个 classic script,且其 muted errors 为 true,则:

    1. scriptTimingInfosource url 设为空字符串。

    2. scriptTimingInfosource character position 设为 -1。

    3. scriptTimingInfosource function name 设为空字符串。

  11. 如果 scriptTimingInfostart timescriptTimingInfoend time 之间的 duration 大于 5 毫秒,则将 scriptTimingInfo appendframeTimingInfoscripts

要在给定一个 script timing info scriptTimingInfo 和一个 callback functionFunction callback 的情况下,将 apply source location 应用于 scriptTimingInfo
  1. 用户代理可以将 scriptTimingInfosource url 设为定义 callback 的脚本的源 URL。

  2. 用户代理可以将 scriptTimingInfosource function name 设为 callback 的函数名。

  3. 用户代理可以将 scriptTimingInfosource character position 设为 定义 callback 的字符位置。

要在给定一个 duration duration 的情况下,record pause duration
  1. scriptrunning script

  2. settingsscriptsettings object

  3. 如果 settings 不是一个 Window, 则返回。

  4. documentsettingsdocument

  5. 如果 document 不是 fully active,或 hidden, 则返回。

  6. frameTimingInfodocumentrelevant frame timing info

  7. 如果 frameTimingInfo 为 null,则返回。

  8. 如果 frameTimingInfopending script 为 null,则返回。

  9. frameTimingInfopending scriptpause durationduration 的 毫秒值递增。

4. 对既有标准的补充

4.1. 对 WebIDL 标准的猴子补丁

Promise 接口具有一个关联字符串 invoker name when created,初始值为 "`Promise`"。 Promise 接口具有一个关联字符串 script url when created,初始为空 字符串。

将以下步骤追加到创建新 promise中,在返回 Promise 之前:

  1. interfaceName 为一个字符串,表示负责创建此 promise 的 interface

  2. attributeName 为一个字符串,表示该接口中负责创建此 promise 的 attribute

  3. 将创建的 Promisescript url when created 设为 running scriptbase URL

  4. 用户代理可以将创建的 Promiseinvoker name when created 设为 « interfaceName, ".", attributeName » 最后已知的 concatenation

    这相当 含糊,因为这很难以规范方式做到。需要看看 是否可以改进,或者 promise 处理程序的源位置是否仍将有点 implementation-defined。

将以下步骤前置到给定 Promise presolve a promise: 给定 p 和 "`resolve-promise`", Record timing info for promise resolver

将以下步骤前置到给定 Promise preject a promise: 给定 p 和 "`reject-promise`", Record timing info for promise resolver

将以下步骤插入到invoke a callback function中, 在我们已有一个 environment settings object relevant settings 和一个 callback function F 时: 给定 Frelevant settingsRecord timing info for user callback

5. 安全和隐私考虑

Long Animation Frames API 通过包含关于 长任务来源的源安全归因信息来遵循同源策略。长任务有 50ms 阈值。持续时间仅以 1 ms 粒度提供。结合起来,这为防止跨源泄漏提供了足够保护。

Long Animation Frames API 提供关于用户执行的任务的持续时间和类型的计时信息, 以及导致函数调用的浏览上下文等归因。这可能使 攻击者能够执行侧信道计时攻击,以猜测用户的动作,或识别用户。例如, 长脚本后跟长渲染的模式可以组合起来猜测用户与社交 小部件的交互。详细的函数调用归因会被用于确定用户的动作。

5.1. 向观察者暴露了什么?

顶层页面内的所有观察者(即页面中的所有 iframe 和主框架)都会收到 关于长动画帧存在的通知。我们公开任务的开始时间、其持续时间 (粒度为 1 ms),以及指向责任框架的指针。这些信息今天已经可以通过 setTimeout 观察到,并且具有更高分辨率。攻击者可以通过清空页面上的所有其他内容, 并添加易受攻击的跨源资源来确保 setTimeout 的延迟由该资源造成。 其他不同页面(标签页或窗口)中的观察者不应接收通知,无论用户代理的 架构如何。

5.2. 考虑过的攻击场景

以下是所考虑的计时攻击:

  1. 传统计时攻击:利用外部资源加载时间揭示 私有数据的大小。例如相册中隐藏图片的数量、用户名是否 有效等。见一个示例

  2. 侧信道计时攻击:利用视频解析、脚本解析、App Cache 读取 或 Cache API(service workers)使用所花的时间来唯一识别用户,或创建 用户年龄、性别、位置和兴趣等画像。例如, 某个实例中, 来自社交网络的状态更新可能仅限于某些人口统计群体(例如 20-30 岁女性), 永久链接页面的文件大小可用于判断用户是否属于目标群体。

这些场景通过 50ms 阈值以及尊重跨源边界来处理,即 不向不可信的跨源观察者显示任务类型或额外归因。

5.3. Long Animation Frames API 暴露的额外信息

由于多个跨源文档可以共享同一个事件循环,它们也可以作为 同一帧序列的一部分进行渲染,并相互影响彼此的渲染时间。这使得这些计时 在跨源情况下已经有些可观察,例如通过请求一个动画帧并观察它是否 被延迟, 不过长动画帧会以更高保真度暴露它们。

为缓解这一点,长动画帧只报告给“参与的本地根”:只有 与对该序列作出贡献的工作任务相关联的文档,或作为该帧一部分被渲染的文档, 才有资格观察该长动画帧,并且该长动画帧只会在 它们最近的祖先中可用,该祖先要么是最顶层的,要么具有跨源父级。

5.4. PerformanceScriptTiming 与不透明脚本

由于 PerformanceScriptTiming 会暴露关于脚本执行的信息,我们需要确保它 不会暴露过多关于 CORS cross-origin 脚本的信息,而这些信息无法通过其他方式轻易推断出来。 为此,我们使用现有的 muted errors 布尔值,并在此类情况下报告一个空的 sourceURL

一致性

文档 约定

一致性要求通过描述性断言 与 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中所述进行解释。 然而,为了可读性, 这些词在本规范中并不全都以大写字母出现。

本规范的所有文本都是规范性的, 除非章节明确标记为非规范性、示例和注释。[RFC2119]

本规范中的示例以 “for example” 一词引入, 或者通过 class="example" 与规范性文本区分开来, 如下所示:

这是一个信息性示例的示例。

信息性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本区分开来, 如下所示:

Note,这是一个信息性注释。

一致性 算法

作为算法一部分以命令式表述的要求 (例如 "strip any leading space characters" 或 "return false and abort these steps") 应以引入该算法时所使用的关键词 ("must"、"should"、"may" 等) 的含义来解释。

以算法或特定步骤表述的一致性要求 可以用任何方式实现, 只要最终结果等价即可。 特别是,本规范中定义的算法 旨在易于理解, 而并非旨在高性能。 鼓励实现者进行优化。

索引

本规范定义的 术语

由引用定义的 术语

参考文献

规范性参考文献

[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021 年 8 月 26 日. WD. URL: https://www.w3.org/TR/cssom-1/
[CSSOM-VIEW-1]
Simon Fraser; Emilio Cobos Álvarez. CSSOM View Module. 2025 年 9 月 16 日. WD. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HR-TIME-2]
Ilya Grigorik. High Resolution Time Level 2. 2019 年 11 月 21 日. REC. URL: https://www.w3.org/TR/hr-time-2/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. 2026 年 3 月 24 日. WD. URL: https://www.w3.org/TR/hr-time-3/
[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/
[LONGTASKS-1]
Noam Rosenthal. Long Tasks API. 2026 年 3 月 19 日. WD. URL: https://www.w3.org/TR/longtasks-1/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing. 2026 年 3 月 24 日. WD. URL: https://www.w3.org/TR/paint-timing/
[PERFORMANCE-TIMELINE]
Nicolas Pena Moreno. Performance Timeline. 2025 年 5 月 21 日. CRD. URL: https://www.w3.org/TR/performance-timeline/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997 年 3 月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[UIEVENTS]
Xiaoqian Wu. UI Events. 2026 年 2 月 21 日. WD. URL: https://www.w3.org/TR/uievents/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL 索引

[Exposed=Window]
interface PerformanceLongAnimationFrameTiming : PerformanceEntry {
    /* Overloading PerformanceEntry */
    readonly attribute DOMHighResTimeStamp startTime;
    readonly attribute DOMHighResTimeStamp duration;
    readonly attribute DOMString name;
    readonly attribute DOMString entryType;

    readonly attribute DOMHighResTimeStamp renderStart;
    readonly attribute DOMHighResTimeStamp styleAndLayoutStart;
    readonly attribute DOMHighResTimeStamp blockingDuration;
    readonly attribute DOMHighResTimeStamp firstUIEventTimestamp;
    [SameObject] readonly attribute FrozenArray<PerformanceScriptTiming> scripts;
    [Default] object toJSON();
};

PerformanceLongAnimationFrameTiming includes PaintTimingMixin;

enum ScriptInvokerType {
    "classic-script",
    "module-script",
    "event-listener",
    "user-callback",
    "resolve-promise",
    "reject-promise"
};

enum ScriptWindowAttribution {
    "self", "descendant", "ancestor", "same-page", "other"
};

[Exposed=Window]
interface PerformanceScriptTiming : PerformanceEntry {
    /* Overloading PerformanceEntry */
    readonly attribute DOMHighResTimeStamp startTime;
    readonly attribute DOMHighResTimeStamp duration;
    readonly attribute DOMString name;
    readonly attribute DOMString entryType;

    readonly attribute ScriptInvokerType invokerType;
    readonly attribute DOMString invoker;
    readonly attribute DOMHighResTimeStamp executionStart;
    readonly attribute DOMString sourceURL;
    readonly attribute DOMString sourceFunctionName;
    readonly attribute long long sourceCharPosition;
    readonly attribute DOMHighResTimeStamp pauseDuration;
    readonly attribute DOMHighResTimeStamp forcedStyleAndLayoutDuration;
    readonly attribute Window? window;
    readonly attribute ScriptWindowAttribution windowAttribution;
    [Default] object toJSON();
};

问题索引

这感觉有点定制化,需要讨论名称生成。
找到一种方式使其可互操作/规范化。也许在 WebIDL 中标记这些函数为 需要同步样式/布局?并且一旦解决,将其移动到 timing info
这相当含糊,因为这很难以规范方式做到。需要看看 是否可以改进,或者 promise 处理程序的源位置是否仍将有点 implementation-defined。