LibWeb: Favour !important property values over animated values

This commit is contained in:
Callum Law
2025-10-27 02:06:01 +13:00
committed by Sam Atkins
parent c8f345356e
commit 76dadd45d6
Notes: github-actions[bot] 2025-10-27 09:52:58 +00:00
3 changed files with 159 additions and 1 deletions

View File

@@ -169,7 +169,8 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
if (return_animated_value == WithAnimationsApplied::Yes) {
// Important properties override animated properties
if (!is_property_important(property_id) && return_animated_value == WithAnimationsApplied::Yes) {
if (auto animated_value = m_animated_property_values.get(property_id); animated_value.has_value())
return *animated_value.value();
}

View File

@@ -0,0 +1,13 @@
Harness status: OK
Found 7 tests
5 Pass
2 Fail
Pass Interpolated value is observable
Pass Important rules override animations
Pass Non-overriden interpolations are observable
Pass Important rules override animations (::before)
Pass Important rules do not override animations on :visited as seen from JS
Fail Standard property animations appearing via setKeyframes do not override important declarations
Fail Custom property animations appearing via setKeyframes do not override important declarations

View File

@@ -0,0 +1,144 @@
<!DOCTYPE html>
<title>Test !important declarations vs. animation effects</title>
<link rel="help" href="https://drafts.csswg.org/css-cascade/#cascade-origin">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
CSS.registerProperty({
name: "--length-1",
syntax: "<length>",
initialValue: "0px",
inherits: false
});
CSS.registerProperty({
name: "--length-2",
syntax: "<length>",
initialValue: "0px",
inherits: false
});
</script>
<style>
@keyframes bgcolor_animation {
from { background-color: rgb(10, 10, 10); }
to { background-color: rgb(20, 20, 20); }
}
@keyframes color_and_bg_animation {
from { background-color: rgb(10, 10, 10); color: rgb(10, 10, 10); }
to { background-color: rgb(20, 20, 20); color: rgb(20, 20, 20); }
}
a, div, ::before{
animation-duration: 1000s;
animation-delay: -500s;
animation-timing-function: steps(2, end);
}
#target1 {
animation-name: bgcolor_animation;
}
#target2 {
background-color: rgb(0, 128, 0) !important;
animation-name: bgcolor_animation;
}
#target3 {
color: rgb(0, 128, 0) !important;
animation-name: color_and_bg_animation;
}
#target4::before {
color: rgb(0, 128, 0) !important;
animation-name: color_and_bg_animation;
content: " ";
}
#target5 {
animation-name: color_and_bg_animation;
}
#target5:visited {
color: rgb(0, 128, 0) !important;
}
#target6 {
background-color: white;
color: white !important;
}
#target7 {
--length-1: 10px;
--length-2: 10px !important;
}
</style>
<div id="target1"></div>
<div id="target2"></div>
<div id="target3"></div>
<div id="target4"></div>
<a href="" id="target5"></a>
<span id="target6"></span>
<span id="target7"></span>
<script>
test(() => {
assert_equals(getComputedStyle(target1).backgroundColor, 'rgb(15, 15, 15)');
}, 'Interpolated value is observable');
test(() => {
assert_equals(getComputedStyle(target2).backgroundColor, 'rgb(0, 128, 0)');
}, 'Important rules override animations');
test(() => {
assert_equals(getComputedStyle(target3).color, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(target3).backgroundColor, 'rgb(15, 15, 15)');
}, 'Non-overriden interpolations are observable');
test(() => {
assert_equals(getComputedStyle(target4, '::before').color, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(target4, '::before').backgroundColor, 'rgb(15, 15, 15)');
}, 'Important rules override animations (::before)');
test(() => {
assert_equals(getComputedStyle(target5).color, 'rgb(15, 15, 15)');
assert_equals(getComputedStyle(target5).backgroundColor, 'rgb(15, 15, 15)');
}, 'Important rules do not override animations on :visited as seen from JS');
test(() => {
getComputedStyle(target6).backgroundColor;
let animation = target6.animate([
{ backgroundColor: 'rgb(10, 10, 10)' },
{ backgroundColor: 'rgb(20, 20, 20)' },
], {
duration: 1000000,
delay: -500000,
easing: 'steps(2, end)'
});
assert_equals(getComputedStyle(target6).backgroundColor, 'rgb(15, 15, 15)');
assert_equals(getComputedStyle(target6).color, 'rgb(255, 255, 255)');
animation.effect.setKeyframes([
{ color: 'rgb(10, 10, 10)' },
{ color: 'rgb(20, 20, 20)' },
]);
assert_equals(getComputedStyle(target6).backgroundColor, 'rgb(255, 255, 255)');
assert_equals(getComputedStyle(target6).color, 'rgb(255, 255, 255)');
}, 'Standard property animations appearing via setKeyframes do not override important declarations');
test(() => {
getComputedStyle(target7).getPropertyValue('--length-1');
let animation = target7.animate([
{ '--length-1': '100px' },
{ '--length-1': '200px' },
], {
duration: 1000000,
delay: -500000,
easing: 'steps(2, end)'
});
assert_equals(getComputedStyle(target7).getPropertyValue('--length-1'), '150px');
assert_equals(getComputedStyle(target7).getPropertyValue('--length-2'), '10px');
animation.effect.setKeyframes([
{ '--length-2': '100px' },
{ '--length-2': '200px' },
]);
assert_equals(getComputedStyle(target7).getPropertyValue('--length-1'), '10px');
assert_equals(getComputedStyle(target7).getPropertyValue('--length-2'), '10px');
}, 'Custom property animations appearing via setKeyframes do not override important declarations');
</script>