mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Auto merge of #26074 - jdm:transition-fix, r=SimonSapin
Avoid infinitely looping CSS transitions. This change addresses the long-standing issue of CSS transitions not ending appropriately. It does not fundamentally change the way we process transitions/animations. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #20379 - [x] There are tests for these changes
This commit is contained in:
commit
516279e24f
6 changed files with 61 additions and 20 deletions
|
@ -110,6 +110,7 @@ pub fn update_animation_state<E>(
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
debug!("expiring animation for {:?}", running_animation);
|
||||
expired_animations
|
||||
.entry(*key)
|
||||
.or_insert_with(Vec::new)
|
||||
|
|
|
@ -425,6 +425,11 @@ pub fn start_transitions_if_applicable(
|
|||
// above.
|
||||
property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
|
||||
|
||||
debug!(
|
||||
"checking {:?} for matching end value",
|
||||
possibly_expired_animations
|
||||
);
|
||||
|
||||
// Per [1], don't trigger a new transition if the end state for that
|
||||
// transition is the same as that of a transition that's already
|
||||
// running on the same node.
|
||||
|
@ -852,26 +857,18 @@ pub fn complete_expired_transitions(
|
|||
node: OpaqueNode,
|
||||
style: &mut Arc<ComputedValues>,
|
||||
context: &SharedStyleContext,
|
||||
) -> bool {
|
||||
let had_animations_to_expire;
|
||||
{
|
||||
let all_expired_animations = context.expired_animations.read();
|
||||
let animations_to_expire = all_expired_animations.get(&node);
|
||||
had_animations_to_expire = animations_to_expire.is_some();
|
||||
if let Some(ref animations) = animations_to_expire {
|
||||
for animation in *animations {
|
||||
debug!("Updating expired animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, ref frame) = *animation {
|
||||
frame.property_animation.update(Arc::make_mut(style), 1.0);
|
||||
}
|
||||
expired_animations: &mut Vec<crate::animation::PropertyAnimation>,
|
||||
) {
|
||||
let mut all_expired_animations = context.expired_animations.write();
|
||||
if let Some(animations) = all_expired_animations.remove(&node) {
|
||||
debug!("removing expired animations for {:?}", node);
|
||||
for animation in animations {
|
||||
debug!("Updating expired animation {:?}", animation);
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, frame) = animation {
|
||||
frame.property_animation.update(Arc::make_mut(style), 1.0);
|
||||
expired_animations.push(frame.property_animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if had_animations_to_expire {
|
||||
context.expired_animations.write().remove(&node);
|
||||
}
|
||||
|
||||
had_animations_to_expire
|
||||
}
|
||||
|
|
|
@ -607,7 +607,12 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
// Finish any expired transitions.
|
||||
let this_opaque = self.as_node().opaque();
|
||||
animation::complete_expired_transitions(this_opaque, style, context);
|
||||
animation::complete_expired_transitions(
|
||||
this_opaque,
|
||||
style,
|
||||
context,
|
||||
possibly_expired_animations,
|
||||
);
|
||||
|
||||
// Merge any running animations into the current style, and cancel them.
|
||||
let had_running_animations = context
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[transitionend_event.html]
|
||||
expected: TIMEOUT
|
||||
[transitionend_event]
|
||||
expected: TIMEOUT
|
|
@ -13031,6 +13031,13 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"transitionend_event.html": [
|
||||
"71b88117a0280fbffcf3ab77105c0460317c66c8",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"white-space-pre-line-long-line.html": [
|
||||
"bf0d0085fef0f1639637b2e652a7fb857cd51bf6",
|
||||
[
|
||||
|
|
27
tests/wpt/mozilla/tests/css/transitionend_event.html
Normal file
27
tests/wpt/mozilla/tests/css/transitionend_event.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
#test {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: black;
|
||||
transition: 1ms linear transform;
|
||||
}
|
||||
.transform {
|
||||
transform:scale(1.2);
|
||||
}
|
||||
</style>
|
||||
<div id="test" class="transform"></div>
|
||||
<script>
|
||||
async_test(function(t) {
|
||||
let d = document.querySelector('div');
|
||||
// Verify that we only receive a single transitionend event once the transition is complete.
|
||||
d.ontransitionend = t.step_func(() => {
|
||||
d.ontransitionend = t.unreached_func();
|
||||
t.step_timeout(() => t.done(), 100);
|
||||
});
|
||||
t.step_timeout(() => d.className = "", 10);
|
||||
});
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue