[csswg-drafts] [web-animations-2] moving from a progress-based timeline to a monotonic timeline can yield incompatible timeline time and animation current time (#11761)

graouts has just created a new issue for https://github.com/w3c/csswg-drafts:

== [web-animations-2] moving from a progress-based timeline to a monotonic timeline can yield incompatible timeline time and animation current time ==
I'm working on a failure in the WPT subtest "Playback rate affects whether active phase boundary is inclusive" in `scroll-animations/scroll-timelines/scroll-animation-effect-phases.tentative.html` and encountered what appears to be a spec issue. I've simplified the test in this particular reduction:

```html
<style>

.scroller {
    overflow: scroll;
    height: 200px;
    width: 200px;
}

.contents {
    height: 1200px;
    width: 100%;
    background-color: green;
}

</style>

<div class="scroller">
    <div class="contents"></div>
</div>

<script>

(async function () {
    // create an animation associated with a progress-based timeline
    const timeline = new ScrollTimeline({ source: document.querySelector(".scroller") });
    const animation = document.querySelector(".contents").animate({ opacity: [0.1, 1] }, { timeline });

    await Promise.all([animation.ready, new Promise(requestAnimationFrame)]);

    // set the animation to the "after" phase
    const scroller = animation.timeline.source;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    scroller.scrollTop = 1.5 * maxScroll;

    // wait a frame and set the timeline to a monotonic timeline
    await new Promise(requestAnimationFrame);
    animation.timeline = document.timeline;
})();

</script>
```

In WebKit – at the time of writing ([290776@main](https://commits.webkit.org/290776@main)) – this particular test yields a debug assertion because we end up attempting an operation with a time value that is expressed in seconds and one expressed as a percentage. Here's how we get there, and I believe in accordance with the current specifications:

### Step 1

Under the call to `Element.animate()`, the procedure to [set the timeline of an animation](https://drafts.csswg.org/web-animations-2/#setting-the-timeline) takes us inside the "if to finite timeline" branch and we set both the _start time_ and the _hold time_ to be _unresolved_.

### Step 2

As the _update animations and send events_ procedure is triggered and the animation becomes _ready_, we [calculate an auto-aligned start time](https://drafts.csswg.org/web-animations-2/#animation-calculating-an-auto-aligned-start-time) and set the _start time_ to `0%` as the _hold time_ remains _unresolved_.

### Step 3

After the animation timeline's source changes scroll position, and during the next occurence of the _update animations and send events_ procedure, we [update the animation's finished state](https://drafts.csswg.org/web-animations-1/#update-an-animations-finished-state) and match this condition:

> If all three of the following conditions are true,
> 
> - the unconstrained current time is resolved, and
> - animation’s start time is resolved, and
> - animation does not have a pending play task or a pending pause task,
> 
> then update animation’s hold time based on the first matching condition for animation from below, if any:

… and then match this condition:

> If playback rate > 0 and unconstrained current time is greater than or equal to associated effect end

… and finally this sub-condition:

> If did seek is false, let the hold time be the maximum value of previous current time and associated effect end.

As a result, the _hold time_ is set to `100%` and the _start time_ remains set to `0%`.

### Step 4

As we [set the timeline](https://drafts.csswg.org/web-animations-2/#animation-set-the-timeline-of-an-animation) to the document timeline, and as such move from a progress-based timeline to a monotonic timeline, we enter this condition:

> If from finite timeline and previous progress is resolved,
> Run the procedure to set the current time to previous progress * end time.

As such we immediately run the procedure to [silently set the current time](https://drafts.csswg.org/web-animations-2/#silently-set-the-current-time) and enter this condition:

> If any of the following conditions are true:
>
> - animation’s hold time is resolved, or
> - animation’s start time is unresolved, or
> - animation has no associated timeline or the associated timeline is inactive, or
> - animation’s playback rate is 0,
>
> Set animation’s hold time to seek time.

As a result, the _hold time_ becomes `0s` and the _start time_ remains `0%`. Note that these two times are now using different time units.

As the [setting the timeline](https://drafts.csswg.org/web-animations-2/#animation-set-the-timeline-of-an-animation) procedure continues, we additionally run this step:

> If the start time of animation is resolved, make animation’s hold time unresolved.

As a result, the _hold time_ becomes unresolved and the _start time_ remains `0%`.

To finish [setting the timeline](https://drafts.csswg.org/web-animations-2/#animation-set-the-timeline-of-an-animation) we [update the animation's finished state](https://drafts.csswg.org/web-animations-1/#update-an-animations-finished-state) and attempt to compute the unconstrained current time at which point we attempt to compute:

```
current time = (timeline time - start time) * playback rate
```

The _timeline time_ is a resolved time expressed in seconds since the timeline is monotonic while the start time has remained `0%`. This is when WebKit hits a debug assertion.

The WPT in question test immediately sets the current time to `0` but I don't think we should be in the situation described above as we set the timeline.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/11761 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Friday, 21 February 2025 12:26:07 UTC