Fire transitioncancel event first rather than transitionrun when replacing old transitions
Categories
(Core :: CSS Transitions and Animations, defect, P1)
Tracking
()
People
(Reporter: hiro, Assigned: boris)
References
(Blocks 1 open bug, )
Details
(Keywords: webcompat:platform-bug)
Attachments
(9 files, 1 obsolete file)
|
704 bytes,
text/html
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
782 bytes,
text/html
|
Details | |
|
894 bytes,
text/html
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr128+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr128+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr128+
|
Details | Review |
This bug is the root cause of bug 1916176 (I believe). Setting the same priority and severity as bug 1916176.
Attaching file is exactly same as the one in bug 1916176 comment 19.
| Assignee | ||
Comment 1•1 year ago
|
||
A quick look at the CSSTransition::QueueEvents(). It seems we set the timestamp for the cancel event too late. We call QueueEvents() when ticking the transition, so it is much later than the timing we create this transition.
We probably need to store a timestamp for transitioncancel when canceling the replaced transition to make sure we order the events properly.
| Assignee | ||
Updated•1 year ago
|
| Assignee | ||
Comment 2•1 year ago
|
||
Basically, the cancel events are almost always queued synchronously in some
deterministic manner in CSSTransition::QueueEvents(), so we should also
expect to dispatch them in the same order.
In AnimationEventDispatcher, we use nsTArray::StableSort(), which keeps the
relative order if both elements are equal, so we should let the comparator,
i.e. Animation::HasLowerCompositeOrderThan(), return false if two
transition events have the same scheduled time and one of them is a
cancel event.
Updated•1 year ago
|
Comment 3•1 year ago
|
||
Brian do you happen to know off-hand where this is defined in the spec? (No worries if not)
| Assignee | ||
Comment 4•1 year ago
|
||
| Assignee | ||
Comment 5•1 year ago
•
|
||
Per Hiro's comment in the patch, we should check this testcase as well.
I saw something like this in Chrome/Safari:
transitionrun(opacity) on 770.9
transitionrun(transform) on 770.9
transitioncancel(transform) on 1270.8
transitioncancel(opacity) on 1270.8
transitionrun(transform) on 1270.8
transitionrun(opacity) on 1270.8
but in Gecko:
transitionrun(opacity) on 756.56
transitionrun(transform) on 756.56
transitionrun(transform) on 1289.8
transitioncancel(transform) on 1289.8
transitionrun(opacity) on 1298.14
transitioncancel(opacity) on 1298.14
In this case, we did cancel transform transition and create a new one to replace it first, but Chrome/Safari fired the transitioncancel of transform and opacity together. This is probably implementation-dependent, and maybe we just need to make sure:
transitioncancel({transform|opacity}) is earlier than transitionrun({transform|opacity}), correspondingly.
Comment 6•1 year ago
|
||
(In reply to Emilio Cobos Álvarez (:emilio) from comment #3)
Brian do you happen to know off-hand where this is defined in the spec? (No worries if not)
The events that get queued are defined in CSS Transitions Level 2 here:
https://drafts.csswg.org/css-transitions-2/#transition-events
The sorting of those events is defined in Web Animations Level 1 here:
https://drafts.csswg.org/web-animations-1/#animation-frame-loop
It doesn't strictly define this case but I think Boris' suggestion in comment 1 matches the intent of the spec. Specifically this part:
Each Document maintains a pending animation event queue that stores animation events along with their corresponding event targets and scheduled event time. The scheduled event time is a time value relative to the time origin representing when the event would ideally have been dispatched were animations updated at an infinitely high frequency.
Comment 7•1 year ago
|
||
Wait so why are we even looking at composite order to sort events? Seems we should not sort them, at least events for the same element and same scheduled time?
But the spec seems to contradict that:
Perform a stable sort of the animation events in events to dispatch as follows:
- Sort the events by their scheduled event time such that events that were scheduled to occur earlier sort before events scheduled to occur later, and events whose scheduled event time is unresolved sort before events with a resolved scheduled event time.
- Within events with equal scheduled event times, sort by their composite order.
Note: The purpose of sorting events is to ensure that, as best possible, even on devices with differing capabilities and hence different frame rates, events are dispatched in a consistent order.
Note: The requirement for the sort to be a stable sort is because sometimes multiple events can be queued with the same scheduled event time. For example, a CSS animation with a duration of zero would dispatch both an animationstart and an animationend event with the same scheuled event time, and the order of these events should be preserved.
So what am I missing? We're following the spec to the letter.
WebKit seems to do something a lot more subtle than our AnimationPtrComparator here. Haven't dug into chromium yet.
In particular it seems like they just sort by tree position of the event target here
Comment 8•1 year ago
|
||
I thought our problem was the scheduled event time we set for the cancel event different from the run event?
If the scheduled event times are the same then the algorithm is to sort by composite order. That's going to mean we sort as follows:
- If A and B’s associated animations differ by class, sort by any inter-class composite order as defined for the corresponding classes.
- If A and B are still not sorted, sort by any class-specific composite order defined by the common class of A and B’s associated animations.
- If A and B are still not sorted, sort by the position of their associated animations in the global animation list.
Since the classes are the same ("transition"), we'll sort by the transition-specific composite order:
As I understand it, unfortunately that means we'll hit step 2 there:
- Otherwise, if only one of A or B has an owning element, let the animation with an owning element sort first.
Since the cancelled transition has no owning element, it will sort last but intuitively, it happened first so it should sort first.
I think we're doing what the spec says but I think the spec doesn't do the intuitive thing.
I think the spec should, like Webkit, sort by the event target. The way we cancel transitions ensures that we have an owning element (and hence event target) just long enough to queue the event but not long enough for it to be there when we later sort events. The event target is still there though.
Comment 9•1 year ago
|
||
In spec terms, it's a bit hard to fix this, since we kind of want to use a different composite order behavior when sorting events in this case.
I think we could introduce a term like "context element" and let that be the owning element when compositing animations generally, but for the specific case of sorting transitions events, let it be the event target.
| Assignee | ||
Comment 10•1 year ago
|
||
(In reply to Brian Birtles (:birtles) from comment #9)
In spec terms, it's a bit hard to fix this, since we kind of want to use a different composite order behavior when sorting events in this case.
I think we could introduce a term like "context element" and let that be the owning element when compositing animations generally, but for the specific case of sorting transitions events, let it be the event target.
Thanks for the suggestion. I'm trying to use the event target to sort (i.e. AnimationEventInfo::CssAnimationOrTransitionData::mOwningAnimationTarget).
BTW, we have to updating this for CSS Animations as well because we may set the same schedule time for animationstart and animationcancel when replacing the CSS Animation, and the cancelled animation doesn't have owning element, either.
So I'd like to handle both cases in AnimationEventInfo::operator<() before we go into Animation::HasLowerCompositeOrderThan().
Comment 11•1 year ago
•
|
||
(In reply to Boris Chiou [:boris] from comment #10)
So I'd like to handle both cases in
AnimationEventInfo::operator<()before we go intoAnimation::HasLowerCompositeOrderThan().
I'm not sure but think you might still need to go into HasLowerCompositeOrderThan so that we do the correct inter-class sorting (e.g. sorting transitions before animations) prior to sorting by event target.
Perhaps HasLowerCompositeOrderThan should take a pointer to a context element? Then make asCSSTransitionForSorting check for transition && (transition->IsTiedToMarkup() || aContextElement)? (And similarly update CSSTransition::HasLowerCompositeOrderThan.)
| Assignee | ||
Comment 12•1 year ago
•
|
||
(In reply to Brian Birtles (:birtles) from comment #11)
Perhaps
HasLowerCompositeOrderThanshould take a pointer to a context element? Then makeasCSSTransitionForSortingcheck fortransition && (transition->IsTiedToMarkup() || aContextElement)? (And similarly updateCSSTransition::HasLowerCompositeOrderThan.)
Thanks. This sounds more reasonable. I was thinking that we could add some special handling in AnimationEventInfo::operator<() if both are CSSTransitions or CSSAnimations, but this makes the readability worse. Passing the event target (i.e. dom::Element*) into HasLowerCompositeOrderThan() would be much better.
| Assignee | ||
Comment 13•1 year ago
|
||
BTW, I noticed there is one thing we probably missed. In the spec, if the owning element (or the context element) are the same, we have to use the transition generation to sort the composite order.
However, we change this value when cancelling a transition. That means the cancelled transition is still sorted later in the event queue when replacing this transition.
It seems the spec doesn't mention a lot about how to handle the transition generation when we cancelling this transition, and it seems CSS Animations doesn't use the "animation generation" to sort the events (or perhaps it affects its global animation list).
So either we shouldn't change the transition generation when cancelling the transition, or we should skip this check if we pass a context element. Or maybe we just cache the old transition generation (or animation index) when cancelling a transition, for sorting the events.
Comment 14•1 year ago
|
||
(In reply to Boris Chiou [:boris] from comment #13)
BTW, I noticed there is one thing we probably missed. In the spec, if the owning element (or the context element) are the same, we have to use the transition generation to sort the composite order.
However, we change this value when cancelling a transition. That means the cancelled transition is still sorted later in the event queue when replacing this transition.
Oh, good point. I hadn't thought about that. That makes me wonder if we're approaching this the wrong way. Maybe we simply need to sort cancel events before other events with the same time?
| Assignee | ||
Comment 15•1 year ago
•
|
||
(In reply to Brian Birtles (:birtles) from comment #14)
(In reply to Boris Chiou [:boris] from comment #13)
BTW, I noticed there is one thing we probably missed. In the spec, if the owning element (or the context element) are the same, we have to use the transition generation to sort the composite order.
However, we change this value when cancelling a transition. That means the cancelled transition is still sorted later in the event queue when replacing this transition.
Oh, good point. I hadn't thought about that. That makes me wonder if we're approaching this the wrong way. Maybe we simply need to sort cancel events before other events with the same time?
It seems we always push the cancel event first, so we probably just follow the implementation of WebKit:
- if their event targets are the same => return false (so keep the relative order)
- if not, we compare the order of event targets
And perhaps we put this special case only when comparing two events with the same time (in AnimationEventInfo::operator<(), instead of Animation::HasLowerCompositeOrderThan()) because this is only for two CSSTransition events or two CSSAnimation events.
Updated•1 year ago
|
| Assignee | ||
Comment 16•1 year ago
|
||
Same for the CSSAnimation. We have to keep the relative order if their
event targets are the same.
| Assignee | ||
Comment 17•1 year ago
|
||
See the testcase. If we replace a transition and flush it in the
meantime, after a timeout, we may fail to set a proper mPendingReadyTime
for the newly-created transition, because the refresh driver is not in
refresh when we call Animation::EnsurePaintIsScheduled() in
Animation::PlayNoUpdate(). So we don't have a pending ready time and
so we cannot trigger this transition at the current tick.
Therefore, this newly-created transition would be triggered at the next tick.
The means it doesn't have mStartTime when we enqueued the transition event
i.e. transitionrun, at the current tick. zeroTimeStamp would be null,
and we fail to sort the events.
In this patch, we just assign a proper schedule time for transitionrun
if zeroTimeStamp is null.
Comment 18•1 year ago
|
||
Just following up on my comments on D225117, I think we shouldn't use a separate path for cancel events because it doesn't completely define the ordering in some cases.
For example, suppose we have multiple transitions fire on an element, e.g. transition-property: width, height. Now suppose that both transitions are cancelled and replaced with new transitions. We should get four events: 2 x transitioncancel, 2 x transitionrun.
Since these transitions have the same event target, simply sorting by event target is not going to provide a fully-specified sort order meaning we'll potentially end up with implementation-specific ordering, either cancel, cancel, run, run or cancel, run, cancel, run.
As a result, I'm suggesting we spec something like the following for transition and CSS animation events:
When sorting cancel events, use the owning element and transition generation from when the transition was cancelled.
(And something similar for CSS animation cancel events but somehow storing the position in the animation-name property? I'm not sure, actually. That will need a bit more thought.)
From an implementation point of view, we are already storing the owning element in the event data as the event target. So, in addition to that we'd need to store the transition generation. Then when we do the composite order sort, we'd need to be able to pass in the owning element and generation to use when sorting.
| Assignee | ||
Comment 19•1 year ago
|
||
I filed https://github.com/w3c/csswg-drafts/issues/11064. Hope this makes us easier to update the spec.
| Assignee | ||
Updated•1 year ago
|
| Assignee | ||
Comment 20•1 year ago
|
||
We need this information when comparing the cancelled transitions/animations
in the following patches.
Note that we have to reorder the calling of Animation::Cancel() and
the update of mAnimationIndex to make sure we enqueue the cancel event
with the correct mAnimationIndex.
Comment 21•1 year ago
|
||
Comment 22•1 year ago
|
||
Backed out for causing failures on event-dispatch.tentative.html
| Assignee | ||
Comment 23•1 year ago
|
||
Weird. My previous try on Android looks good. I will send to try again to check what happened. It's unlikely to be reproduced locally.
| Assignee | ||
Comment 24•1 year ago
|
||
It's unfortunate I cannot reproduce this on try today. I will push to try again on the tip of tomorrow to see which patch causes this.
| Reporter | ||
Comment 25•1 year ago
|
||
Boris, it also failed on desktops, this failure for example, so I think you can try it locally.
| Assignee | ||
Comment 26•1 year ago
|
||
Good catch! I will try again locally again. :)
| Assignee | ||
Comment 27•1 year ago
|
||
I still cannot reproduce these failures in try today (https://treeherder.mozilla.org/jobs?repo=try&revision=b34e769b9089f0bac0b2a2aeb8c0d851ebdd4a38) and my local build.
I'd like to try to push part of these patches to find the patch which causes these errors.
| Assignee | ||
Comment 28•1 year ago
•
|
||
I suspect this may be a kind of memory layout issue in AnimationEventDispatcher because I added a new field in Variant<CssAnimationData, CssTransitionData, WebAnimationData>. And I probably saw some issues that we mismatch the data type before, e.g. A CSSTransition object but it uses WebAnimationData.
| Assignee | ||
Updated•1 year ago
|
Comment 29•1 year ago
|
||
Comment 30•1 year ago
|
||
| bugherder | ||
Comment 31•1 year ago
|
||
Comment 33•1 year ago
|
||
| bugherder | ||
Comment 34•1 year ago
|
||
Comment 35•1 year ago
|
||
| bugherder | ||
Comment 37•1 year ago
•
|
||
Hi Boris - this ran across my radar since it's one of our S2 bugs.
Just summing up the status here (as it seems to me at least):
- the patch-stack landed (comment 21) but was backed out for test failures (comment 22)
- Test failures were difficult to repro on-demand, so we re-landed the patches one by one.
- We ended up with 3 of the 4 originally-landed patches as having stuck, and it looks like we haven't attempted to re-land the 4th one yet, specifically https://phabricator.services.mozilla.com/D226212 ("Fallback to compare the order of event targets for CSSAnimation"), so this is still open (and has
leave-openkeyword)
I'm assuming there's still work to be done to get that 4th patch landable-without-breaking-tests, but wanted to check in since some time has passed, just in case the test issues have already been addressed & to be sure this doesn't slip off the radar....
Is that summary accurate & we've still got a bit more to do here to get that final patch landable (and/or to better-understand the test-failures)?
Thanks!
Comment 38•1 year ago
•
|
||
Also: emilio noticed in a quick test that the patches-that-have-already-landed here may have fixed the originally-affected-site here.
Given that, and to keep things simpler for tracking purposes (now that we've just crossed a version-number threshold), maybe it'd be best to close out this bug as fixed-in-134, and spin off a followup to get the remaining patch landed in the 135 nightly cycle?
(You can migrate the existing phab revision to the hypothetical new bug, just by editing the "Bugzilla Bug ID" field in the edit-revision field at https://phabricator.services.mozilla.com/differential/revision/edit/226212/ )
| Assignee | ||
Comment 39•1 year ago
|
||
Yes. Thanks for checking this bug. I landed the first 3 patches because they block the S2 bug (and that's the reason we set this bug S2).
I will do things:
- File a separate bug which hold my animation event patch. In that bug, I think it is OK to give it S3 because it is just a spec update. Therefore, we can close this one.
- I probably will use try server to check my patch again to see any failures. If I still cannot reproduce it, I probably will reland it to see if there are still any failures. This takes much more time, so using a different bug is much better.
Comment 40•1 year ago
|
||
Comment on attachment 9432006 [details]
Bug 1923208 - Fallback to compare the order of event targets for CSSAnimation if they have the same time.
Revision D226212 was moved to bug 1933281. Setting attachment 9432006 [details] to obsolete.
| Assignee | ||
Updated•1 year ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Comment 41•1 year ago
|
||
Did you want to nominate this for ESR128 uplift? It grafts cleanly.
| Assignee | ||
Updated•1 year ago
|
| Assignee | ||
Comment 43•1 year ago
|
||
We need this information when comparing the cancelled transitions/animations
in the following patches.
Note that we have to reorder the calling of Animation::Cancel() and
the update of mAnimationIndex to make sure we enqueue the cancel event
with the correct mAnimationIndex.
Original Revision: https://phabricator.services.mozilla.com/D227110
Updated•1 year ago
|
| Assignee | ||
Comment 44•1 year ago
|
||
Since the cancelled transition has no owning element and its transition
generation is out-of-date, so we have to compare the event targets and
the transition generations in AnimationEventInfo. We still follow the
composite order defined in the spec but use the event target and transition
generation before cancelling this transition, to get the correct event order
if we have the same schedule time.
Original Revision: https://phabricator.services.mozilla.com/D225117
Updated•1 year ago
|
| Assignee | ||
Comment 45•1 year ago
|
||
See the testcase. If we replace a transition and flush it in the
meantime, after a timeout, we may fail to set a proper mPendingReadyTime
for the newly-created transition, because the refresh driver is not in
refresh when we call Animation::EnsurePaintIsScheduled() in
Animation::PlayNoUpdate(). So we don't have a pending ready time and
so we cannot trigger this transition at the current tick.
Therefore, this newly-created transition would be triggered at the next tick.
The means it doesn't have mStartTime when we enqueued the transition event
i.e. transitionrun, at the current tick. zeroTimeStamp would be null,
and we fail to sort the events.
In this patch, we just assign a proper schedule time for transitionrun
if zeroTimeStamp is null.
Original Revision: https://phabricator.services.mozilla.com/D226213
Updated•1 year ago
|
| Comment hidden (obsolete) |
Comment 47•1 year ago
|
||
esr128 Uplift Approval Request
- User impact if declined: The users may see unexpected behaviors on some website which uses CSS transitions
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: Low
- Explanation of risk level: This only changed the sorting way for CSS transition events. The only impact is the performance. However, it is low per our tests in Nightly.
- String changes made/needed: No
- Is Android affected?: yes
Comment 48•1 year ago
|
||
esr128 Uplift Approval Request
- User impact if declined: The users may see unexpected behaviors on some website which uses CSS transitions
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: Very Low
- Explanation of risk level: This just added one extra parameter in some functions.
- String changes made/needed: No
- Is Android affected?: yes
Comment 49•1 year ago
|
||
esr128 Uplift Approval Request
- User impact if declined: The users may see unexpected behaviors on some website which uses CSS transitions
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: Very Low
- Explanation of risk level: This only assigned a proper timestamp for the events in some cases.
- String changes made/needed: No
- Is Android affected?: yes
Updated•1 year ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Updated•1 year ago
|
Comment 50•1 year ago
|
||
| uplift | ||
Updated•1 year ago
|
Description
•