Skip to content

Commit 345c742

Browse files
committed
LibWeb: Prefer transitioned property values over important
CSS transitions have a higher precedence in the cascade than important properties
1 parent 63d2f57 commit 345c742

File tree

8 files changed

+89
-18
lines changed

8 files changed

+89
-18
lines changed

Libraries/LibWeb/CSS/ComputedProperties.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ bool ComputedProperties::is_animated_property_inherited(PropertyID property_id)
9292
return m_animated_property_inherited[n / 8] & (1 << (n % 8));
9393
}
9494

95+
bool ComputedProperties::is_animated_property_result_of_transition(PropertyID property_id) const
96+
{
97+
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
98+
99+
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
100+
return m_animated_property_result_of_transition[n / 8] & (1 << (n % 8));
101+
}
102+
95103
void ComputedProperties::set_property_inherited(PropertyID property_id, Inherited inherited)
96104
{
97105
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
@@ -114,6 +122,17 @@ void ComputedProperties::set_animated_property_inherited(PropertyID property_id,
114122
m_animated_property_inherited[n / 8] &= ~(1 << (n % 8));
115123
}
116124

125+
void ComputedProperties::set_animated_property_result_of_transition(PropertyID property_id, AnimatedPropertyResultOfTransition animated_value_result_of_transition)
126+
{
127+
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
128+
129+
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
130+
if (animated_value_result_of_transition == AnimatedPropertyResultOfTransition::Yes)
131+
m_animated_property_result_of_transition[n / 8] |= (1 << (n % 8));
132+
else
133+
m_animated_property_result_of_transition[n / 8] &= ~(1 << (n % 8));
134+
}
135+
117136
void ComputedProperties::set_property(PropertyID id, NonnullRefPtr<StyleValue const> value, Inherited inherited, Important important)
118137
{
119138
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
@@ -149,10 +168,11 @@ void ComputedProperties::set_display_before_box_type_transformation(Display valu
149168
m_display_before_box_type_transformation = value;
150169
}
151170

152-
void ComputedProperties::set_animated_property(PropertyID id, NonnullRefPtr<StyleValue const> value, Inherited inherited)
171+
void ComputedProperties::set_animated_property(PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_value_result_of_transition, Inherited inherited)
153172
{
154173
m_animated_property_values.set(id, move(value));
155174
set_animated_property_inherited(id, inherited);
175+
set_animated_property_result_of_transition(id, animated_value_result_of_transition);
156176
}
157177

158178
void ComputedProperties::remove_animated_property(PropertyID id)
@@ -169,8 +189,8 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
169189
{
170190
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
171191

172-
// Important properties override animated properties
173-
if (!is_property_important(property_id) && return_animated_value == WithAnimationsApplied::Yes) {
192+
// Important properties override animated but not transitioned properties
193+
if ((!is_property_important(property_id) || is_animated_property_result_of_transition(property_id)) && return_animated_value == WithAnimationsApplied::Yes) {
174194
if (auto animated_value = m_animated_property_values.get(property_id); animated_value.has_value())
175195
return *animated_value.value();
176196
}

Libraries/LibWeb/CSS/ComputedProperties.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626

2727
namespace Web::CSS {
2828

29+
enum class AnimatedPropertyResultOfTransition : u8 {
30+
No,
31+
Yes
32+
};
33+
2934
class WEB_API ComputedProperties final : public JS::Cell {
3035
GC_CELL(ComputedProperties, JS::Cell);
3136
GC_DECLARE_ALLOCATOR(ComputedProperties);
@@ -55,13 +60,15 @@ class WEB_API ComputedProperties final : public JS::Cell {
5560
bool is_property_important(PropertyID property_id) const;
5661
bool is_property_inherited(PropertyID property_id) const;
5762
bool is_animated_property_inherited(PropertyID property_id) const;
63+
bool is_animated_property_result_of_transition(PropertyID property_id) const;
5864
void set_property_important(PropertyID, Important);
5965
void set_property_inherited(PropertyID, Inherited);
6066
void set_animated_property_inherited(PropertyID, Inherited);
67+
void set_animated_property_result_of_transition(PropertyID, AnimatedPropertyResultOfTransition);
6168

6269
void set_property(PropertyID, NonnullRefPtr<StyleValue const> value, Inherited = Inherited::No, Important = Important::No);
6370
void set_property_without_modifying_flags(PropertyID, NonnullRefPtr<StyleValue const> value);
64-
void set_animated_property(PropertyID, NonnullRefPtr<StyleValue const> value, Inherited = Inherited::No);
71+
void set_animated_property(PropertyID, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition, Inherited = Inherited::No);
6572
void remove_animated_property(PropertyID);
6673
enum class WithAnimationsApplied {
6774
No,
@@ -294,6 +301,7 @@ class WEB_API ComputedProperties final : public JS::Cell {
294301
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_important {};
295302
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_inherited {};
296303
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_inherited {};
304+
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_result_of_transition {};
297305

298306
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> m_animated_property_values;
299307

Libraries/LibWeb/CSS/StyleComputer.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
950950
auto const& specified_value_with_css_wide_keywords_applied = [&]() -> StyleValue const& {
951951
if (longhand_value.is_inherit() || (longhand_value.is_unset() && is_inherited_property(longhand_id))) {
952952
if (auto inherited_animated_value = get_animated_inherit_value(longhand_id, abstract_element); inherited_animated_value.has_value())
953-
return inherited_animated_value.value();
953+
return inherited_animated_value->value;
954954

955955
return get_non_animated_inherit_value(longhand_id, abstract_element);
956956
}
@@ -1070,6 +1070,8 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
10701070
VERIFY_NOT_REACHED();
10711071
};
10721072

1073+
auto is_result_of_transition = animation->is_css_transition() ? AnimatedPropertyResultOfTransition::Yes : AnimatedPropertyResultOfTransition::No;
1074+
10731075
auto start_composite_operation = to_composite_operation(keyframe_values.composite);
10741076
auto end_composite_operation = to_composite_operation(keyframe_end_values.composite);
10751077

@@ -1079,7 +1081,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
10791081

10801082
if (!resolved_end_property) {
10811083
if (resolved_start_property) {
1082-
computed_properties.set_animated_property(it.key, *resolved_start_property);
1084+
computed_properties.set_animated_property(it.key, *resolved_start_property, is_result_of_transition);
10831085
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "No end property for property {}, using {}", string_from_property_id(it.key), resolved_start_property->to_string(SerializationMode::Normal));
10841086
}
10851087
continue;
@@ -1094,7 +1096,9 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
10941096
auto start = resolved_start_property.release_nonnull();
10951097
auto end = resolved_end_property.release_nonnull();
10961098

1097-
if (computed_properties.is_property_important(it.key)) {
1099+
// OPTIMIZATION: Values resulting from animations other than CSS transitions are overriden by important
1100+
// properties so there's no need to calculate them
1101+
if (!animation->is_css_transition() && computed_properties.is_property_important(it.key)) {
10981102
continue;
10991103
}
11001104

@@ -1107,11 +1111,11 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
11071111

11081112
if (auto next_value = interpolate_property(*effect->target(), it.key, *start, *end, progress_in_keyframe, AllowDiscrete::Yes)) {
11091113
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} = {}", string_from_property_id(it.key), progress_in_keyframe, start->to_string(SerializationMode::Normal), end->to_string(SerializationMode::Normal), next_value->to_string(SerializationMode::Normal));
1110-
computed_properties.set_animated_property(it.key, *next_value);
1114+
computed_properties.set_animated_property(it.key, *next_value, is_result_of_transition);
11111115
} else {
11121116
// If interpolate_property() fails, the element should not be rendered
11131117
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} is invalid", string_from_property_id(it.key), progress_in_keyframe, start->to_string(SerializationMode::Normal), end->to_string(SerializationMode::Normal));
1114-
computed_properties.set_animated_property(PropertyID::Visibility, KeywordStyleValue::create(Keyword::Hidden));
1118+
computed_properties.set_animated_property(PropertyID::Visibility, KeywordStyleValue::create(Keyword::Hidden), is_result_of_transition);
11151119
}
11161120
}
11171121
}
@@ -1591,15 +1595,20 @@ NonnullRefPtr<StyleValue const> StyleComputer::get_non_animated_inherit_value(Pr
15911595
return parent_element->computed_properties()->property(property_id, ComputedProperties::WithAnimationsApplied::No);
15921596
}
15931597

1594-
Optional<NonnullRefPtr<StyleValue const>> StyleComputer::get_animated_inherit_value(PropertyID property_id, DOM::AbstractElement abstract_element)
1598+
Optional<StyleComputer::AnimatedInheritValue> StyleComputer::get_animated_inherit_value(PropertyID property_id, DOM::AbstractElement abstract_element)
15951599
{
15961600
auto parent_element = abstract_element.element_to_inherit_style_from();
15971601

15981602
if (!parent_element.has_value() || !parent_element->computed_properties())
15991603
return {};
16001604

16011605
if (auto animated_value = parent_element->computed_properties()->animated_property_values().get(property_id); animated_value.has_value())
1602-
return *animated_value.value();
1606+
return AnimatedInheritValue {
1607+
.value = *animated_value.value(),
1608+
.is_result_of_transition = parent_element->computed_properties()->is_animated_property_result_of_transition(property_id)
1609+
? AnimatedPropertyResultOfTransition::Yes
1610+
: AnimatedPropertyResultOfTransition::No
1611+
};
16031612

16041613
return {};
16051614
}
@@ -2508,7 +2517,6 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractEleme
25082517
auto property_id = static_cast<CSS::PropertyID>(i);
25092518
auto value = cascaded_properties.property(property_id);
25102519
auto inherited = ComputedProperties::Inherited::No;
2511-
Optional<NonnullRefPtr<StyleValue const>> animated_value;
25122520

25132521
// NOTE: We've already handled font-size above.
25142522
if (property_id == PropertyID::FontSize && !value && new_font_size)
@@ -2530,17 +2538,17 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractEleme
25302538

25312539
// FIXME: Logical properties should inherit from their parent's equivalent unmapped logical property.
25322540
if (should_inherit) {
2533-
value = get_non_animated_inherit_value(property_id, abstract_element);
2534-
animated_value = get_animated_inherit_value(property_id, abstract_element);
25352541
inherited = ComputedProperties::Inherited::Yes;
2542+
value = get_non_animated_inherit_value(property_id, abstract_element);
2543+
2544+
if (auto animated_value = get_animated_inherit_value(property_id, abstract_element); animated_value.has_value())
2545+
computed_style->set_animated_property(property_id, animated_value->value, animated_value->is_result_of_transition, ComputedProperties::Inherited::Yes);
25362546
}
25372547

25382548
if (!value || value->is_initial() || value->is_unset())
25392549
value = property_initial_value(property_id);
25402550

25412551
computed_style->set_property(property_id, value.release_nonnull(), inherited, cascaded_properties.is_property_important(property_id) ? Important::Yes : Important::No);
2542-
if (animated_value.has_value())
2543-
computed_style->set_animated_property(property_id, animated_value.value(), inherited);
25442552

25452553
if (property_id == PropertyID::TransitionProperty) {
25462554
computed_style->set_transition_property_source(cascaded_properties.property_source(property_id));

Libraries/LibWeb/CSS/StyleComputer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ class WEB_API StyleComputer final : public GC::Cell {
110110
public:
111111
static void for_each_property_expanding_shorthands(PropertyID, StyleValue const&, Function<void(PropertyID, StyleValue const&)> const& set_longhand_property);
112112
static NonnullRefPtr<StyleValue const> get_non_animated_inherit_value(PropertyID, DOM::AbstractElement);
113-
static Optional<NonnullRefPtr<StyleValue const>> get_animated_inherit_value(PropertyID, DOM::AbstractElement);
113+
struct AnimatedInheritValue {
114+
NonnullRefPtr<StyleValue const> value;
115+
AnimatedPropertyResultOfTransition is_result_of_transition;
116+
};
117+
static Optional<AnimatedInheritValue> get_animated_inherit_value(PropertyID, DOM::AbstractElement);
114118

115119
static Optional<String> user_agent_style_sheet_source(StringView name);
116120

Libraries/LibWeb/DOM/Element.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style()
851851

852852
if (computed_properties->is_animated_property_inherited(property_id) || !computed_properties->animated_property_values().contains(property_id)) {
853853
if (auto new_animated_value = CSS::StyleComputer::get_animated_inherit_value(property_id, { *this }); new_animated_value.has_value())
854-
computed_properties->set_animated_property(property_id, new_animated_value.value(), CSS::ComputedProperties::Inherited::Yes);
854+
computed_properties->set_animated_property(property_id, new_animated_value->value, new_animated_value->is_result_of_transition, CSS::ComputedProperties::Inherited::Yes);
855855
else if (computed_properties->animated_property_values().contains(property_id))
856856
computed_properties->remove_animated_property(property_id);
857857
}

Libraries/LibWeb/Forward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ enum class MediaFeatureID : u8;
397397
enum class PropertyID : u16;
398398
enum class PaintOrder : u8;
399399
enum class ValueType : u8;
400+
enum class AnimatedPropertyResultOfTransition : u8;
400401

401402
struct BackgroundLayerData;
402403
struct CalculationContext;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Harness status: OK
2+
3+
Found 1 tests
4+
5+
1 Pass
6+
Pass !important should not override transition
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<title>CSS Transitions Test: !important property</title>
3+
<link rel="help" href="https://drafts.csswg.org/css-cascade-5/#cascade-sort">
4+
<script src="../../resources/testharness.js" type="text/javascript"></script>
5+
<script src="../../resources/testharnessreport.js" type="text/javascript"></script>
6+
<style>
7+
#target {
8+
width: 200px;
9+
height: 200px;
10+
background-color: green;
11+
transition: background-color 100s;
12+
}
13+
.red {
14+
background-color: red !important;
15+
}
16+
</style>
17+
<div id="target"></div>
18+
<script>
19+
test(t => {
20+
assert_equals(getComputedStyle(target).backgroundColor, "rgb(0, 128, 0)");
21+
target.className = "red";
22+
assert_equals(getComputedStyle(target).backgroundColor, "rgb(0, 128, 0)");
23+
}, "!important should not override transition");
24+
</script>

0 commit comments

Comments
 (0)