Skip to content

Commit

Permalink
Don't reset animations/transitions on moveBefore
Browse files Browse the repository at this point in the history
See whatwg/dom#1255

Animations & transitions should attempt to continue
from where they left off, if possible.

This is done in the following way:
- Animations are not cancelled on removal when in a state-preserving
  atomic move.
- We don't reset the computed style when removing the element in
  preparation for an atomic move.
- We don't clear the layout/style flags, so that the layout is
  recomputed and reattached on the next style recalc.

Bug: 40150299
Change-Id: I559e69e75df14df589485cb024da0f0f28b1e1ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5458120
Commit-Queue: Noam Rosenthal <nrosenthal@chromium.org>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1291811}
  • Loading branch information
noamr authored and chromium-wpt-export-bot committed Apr 24, 2024
1 parent d064974 commit 7a80152
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 0 deletions.
51 changes: 51 additions & 0 deletions dom/nodes/moveBefore/tentative/continue-css-animation-left.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<title>Node.moveBefore should preserve CSS animation state (left)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
@keyframes anim {
from {
left: 100px;
}

to {
left: 400px;
}
}

section {
position: absolute;
}

#item {
position: relative;
width: 100px;
height: 100px;
background: green;
animation: 1s linear infinite alternate anim;
animation-delay: 100ms;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
let num_events = 0;
await new Promise(resolve => addEventListener("animationstart", () => {
num_events++;
resolve();
}));

// Reparent item
document.body.querySelector("#new-parent").moveBefore(item, null);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
assert_equals(num_events, 1);
assert_not_equals(getComputedStyle(item).left, "0px");
});
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<title>Node.moveBefore should preserve CSS animation state (transform)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
@keyframes anim {
from {
transform: translateX(100px);
}

to {
transform: translateX(400px);
}
}

#item {
position: relative;
width: 100px;
height: 100px;
background: green;
animation: 1s linear infinite alternate anim;
animation-delay: 100ms;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
let num_events = 0;
await new Promise(resolve => addEventListener("animationstart", () => {
num_events++;
resolve();
}));

// Reparent item
document.body.querySelector("#new-parent").moveBefore(item, null);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
assert_equals(num_events, 1);
assert_not_equals(getComputedStyle(item).transform, "none");
});
</script>
</body>
41 changes: 41 additions & 0 deletions dom/nodes/moveBefore/tentative/continue-css-transition-left.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<title>Node.moveBefore should preserve CSS transition state (left)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
#item {
width: 100px;
height: 100px;
background: green;
transition: left 10s;
position: absolute;
left: 0;
}

section {
position: relative;
}

body {
margin-left: 0;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
assert_equals(item.getBoundingClientRect().x, 0);
item.style.left = "400px";
await new Promise(resolve => item.addEventListener("transitionstart", resolve));
document.querySelector("#new-parent").moveBefore(item, null);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
assert_less_than(item.getBoundingClientRect().x, 399);
});
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<title>Node.moveBefore should preserve CSS transition state (transform)</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
#item {
width: 100px;
height: 100px;
background: green;
transition: transform 60s steps(1, jump-both);
}

body {
margin-left: 0;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
assert_equals(item.getBoundingClientRect().x, 0);
item.style.transform = "translateX(400px)";
await new Promise(resolve => item.addEventListener("transitionstart", resolve));
document.querySelector("#new-parent").moveBefore(item, null);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
assert_equals(item.getBoundingClientRect().x, 200);
assert_equals(item.getAnimations().length, 1);
});
</script>
</body>
45 changes: 45 additions & 0 deletions dom/nodes/moveBefore/tentative/css-animation-commit-styles.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<title>Calling commitStyles after Node.moveBefore should commit mid-transition value</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
@keyframes anim {
from {
transform: translateX(100px);
}

to {
transform: translateX(400px);
}
}

#item {
position: relative;
width: 100px;
height: 100px;
background: green;
animation: 1s linear infinite alternate anim;
animation-delay: 100ms;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
await new Promise(resolve => item.addEventListener("animationstart", resolve));

// Reparent item
document.body.querySelector("#new-parent").moveBefore(item, null);

item.getAnimations()[0].commitStyles();
assert_true("transform" in item.style);
assert_not_equals(item.style.transform, "none");
});
</script>
</body>
43 changes: 43 additions & 0 deletions dom/nodes/moveBefore/tentative/css-transition-trigger.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<title>Node.moveBefore should trigger CSS transition state (left) if needed</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<body>
<section id="old-parent">
<div id="item"></div>
</section>
<section id="new-parent">
</section>
<style>
#item {
width: 100px;
height: 100px;
background: green;
transition: left 10s steps(1, jump-both);
position: absolute;
left: 0;
}

#new-parent #item {
left: 400px;
}

section {
position: relative;
}

body {
margin-left: 0;
}
</style>
<script>
promise_test(async t => {
const item = document.querySelector("#item");
assert_equals(item.getBoundingClientRect().x, 0);
document.querySelector("#new-parent").moveBefore(item, null);
await new Promise(resolve => item.addEventListener("transitionstart", resolve));
assert_equals(item.getBoundingClientRect().x, 200);
});
</script>
</body>

0 comments on commit 7a80152

Please sign in to comment.