mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Fix @keyframes resolution for slotted elements
A @keyframes rule scoped to a shadow root was not reliably reached from an animated slotted light-DOM element: the keyframes lookup walked the element's own root first, then fell back to the document, but slotted elements can pick up animation-name from a ::slotted(...) rule that lives in an ancestor shadow root rather than in the element's own tree. Track the shadow-root scope that supplied each winning cascaded declaration, and use that scope to resolve the matching @keyframes when processing animation definitions. A shared constructable stylesheet can be adopted into several scopes at once, so the declaration object alone is too weak as a key; the per-entry shadow-root pointer disambiguates which adoption actually contributed. Also refresh running CSS animations' keyframe sets when style is recomputed. Previously only the first animation creation path set a keyframe set, so an existing animation never picked up newly inserted @keyframes rules.
This commit is contained in:
committed by
Andreas Kling
parent
654e1efacc
commit
11c75a2ffb
Notes:
github-actions[bot]
2026-04-22 19:00:09 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/11c75a2ffbb Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/9042
@@ -0,0 +1 @@
|
||||
opacity: 0.25
|
||||
@@ -0,0 +1 @@
|
||||
opacity: 0.5
|
||||
@@ -0,0 +1 @@
|
||||
opacity: 0.5
|
||||
@@ -0,0 +1 @@
|
||||
opacity: 0.25
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<style>
|
||||
@keyframes fade {
|
||||
from { opacity: 0.5; }
|
||||
to { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
<div id="host">host</div>
|
||||
<script>
|
||||
test(() => {
|
||||
const host = document.getElementById("host");
|
||||
const shadowRoot = host.attachShadow({ mode: "open" });
|
||||
shadowRoot.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
animation-name: fade;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 0.25; }
|
||||
to { opacity: 0.25; }
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
`;
|
||||
|
||||
println(`opacity: ${getComputedStyle(host).opacity}`);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<style>
|
||||
#slotted {
|
||||
animation-name: fade;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 0.5; }
|
||||
to { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
<div id="host"><span id="slotted">slotted</span></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const host = document.getElementById("host");
|
||||
const slotted = document.getElementById("slotted");
|
||||
const shadowRoot = host.attachShadow({ mode: "open" });
|
||||
shadowRoot.innerHTML = `
|
||||
<style>
|
||||
@keyframes fade {
|
||||
from { opacity: 0.25; }
|
||||
to { opacity: 0.25; }
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
`;
|
||||
|
||||
println(`opacity: ${getComputedStyle(slotted).opacity}`);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<style>
|
||||
@keyframes fade {
|
||||
from { opacity: 0.5; }
|
||||
to { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
<div id="host"><span id="slotted">slotted</span></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const host = document.getElementById("host");
|
||||
const slotted = document.getElementById("slotted");
|
||||
const shadowRoot = host.attachShadow({ mode: "open" });
|
||||
|
||||
const sharedSheet = new CSSStyleSheet();
|
||||
sharedSheet.replaceSync(`
|
||||
#slotted {
|
||||
animation-name: fade;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
`);
|
||||
|
||||
const shadowKeyframesSheet = new CSSStyleSheet();
|
||||
shadowKeyframesSheet.replaceSync(`
|
||||
@keyframes fade {
|
||||
from { opacity: 0.25; }
|
||||
to { opacity: 0.25; }
|
||||
}
|
||||
`);
|
||||
|
||||
document.adoptedStyleSheets = [sharedSheet];
|
||||
shadowRoot.innerHTML = "<slot></slot>";
|
||||
shadowRoot.adoptedStyleSheets = [sharedSheet, shadowKeyframesSheet];
|
||||
|
||||
println(`opacity: ${getComputedStyle(slotted).opacity}`);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<style>
|
||||
@keyframes fade {
|
||||
from { opacity: 0.5; }
|
||||
to { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
<div id="host"><span id="slotted" class="item">slotted</span></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const slotted = document.getElementById("slotted");
|
||||
const shadowRoot = document.getElementById("host").attachShadow({ mode: "open" });
|
||||
shadowRoot.innerHTML = `
|
||||
<style>
|
||||
::slotted(.item) {
|
||||
animation-name: fade;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
@keyframes fade {
|
||||
from { opacity: 0.25; }
|
||||
to { opacity: 0.25; }
|
||||
}
|
||||
</style>
|
||||
<slot></slot>
|
||||
`;
|
||||
|
||||
println(`opacity: ${getComputedStyle(slotted).opacity}`);
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user