Skip to content

Commit c2e6308

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

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_property_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_property_result_of_transition);
156176
}
157177

158178
void ComputedProperties::remove_animated_property(PropertyID id)
@@ -172,8 +192,8 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
172192
{
173193
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
174194

175-
// Important properties override animated properties
176-
if (!is_property_important(property_id) && return_animated_value == WithAnimationsApplied::Yes) {
195+
// Important properties override animated but not transitioned properties
196+
if ((!is_property_important(property_id) || is_animated_property_result_of_transition(property_id)) && return_animated_value == WithAnimationsApplied::Yes) {
177197
if (auto animated_value = m_animated_property_values.get(property_id); animated_value.has_value())
178198
return *animated_value.value();
179199
}

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,
@@ -292,6 +299,7 @@ class WEB_API ComputedProperties final : public JS::Cell {
292299
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_important {};
293300
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_inherited {};
294301
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_inherited {};
302+
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_result_of_transition {};
295303

296304
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> m_animated_property_values;
297305

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
}
@@ -1606,15 +1610,20 @@ NonnullRefPtr<StyleValue const> StyleComputer::get_non_animated_inherit_value(Pr
16061610
return parent_element->computed_properties()->property(property_id, ComputedProperties::WithAnimationsApplied::No);
16071611
}
16081612

1609-
Optional<NonnullRefPtr<StyleValue const>> StyleComputer::get_animated_inherit_value(PropertyID property_id, DOM::AbstractElement abstract_element)
1613+
Optional<StyleComputer::AnimatedInheritValue> StyleComputer::get_animated_inherit_value(PropertyID property_id, DOM::AbstractElement abstract_element)
16101614
{
16111615
auto parent_element = abstract_element.element_to_inherit_style_from();
16121616

16131617
if (!parent_element.has_value() || !parent_element->computed_properties())
16141618
return {};
16151619

16161620
if (auto animated_value = parent_element->computed_properties()->animated_property_values().get(property_id); animated_value.has_value())
1617-
return *animated_value.value();
1621+
return AnimatedInheritValue {
1622+
.value = *animated_value.value(),
1623+
.is_result_of_transition = parent_element->computed_properties()->is_animated_property_result_of_transition(property_id)
1624+
? AnimatedPropertyResultOfTransition::Yes
1625+
: AnimatedPropertyResultOfTransition::No
1626+
};
16181627

16191628
return {};
16201629
}
@@ -2523,7 +2532,6 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractEleme
25232532
auto property_id = static_cast<CSS::PropertyID>(i);
25242533
auto value = cascaded_properties.property(property_id);
25252534
auto inherited = ComputedProperties::Inherited::No;
2526-
Optional<NonnullRefPtr<StyleValue const>> animated_value;
25272535

25282536
// NOTE: We've already handled font-size above.
25292537
if (property_id == PropertyID::FontSize && !value && new_font_size)
@@ -2545,17 +2553,17 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractEleme
25452553

25462554
// FIXME: Logical properties should inherit from their parent's equivalent unmapped logical property.
25472555
if (should_inherit) {
2548-
value = get_non_animated_inherit_value(property_id, abstract_element);
2549-
animated_value = get_animated_inherit_value(property_id, abstract_element);
25502556
inherited = ComputedProperties::Inherited::Yes;
2557+
value = get_non_animated_inherit_value(property_id, abstract_element);
2558+
2559+
if (auto animated_value = get_animated_inherit_value(property_id, abstract_element); animated_value.has_value())
2560+
computed_style->set_animated_property(property_id, animated_value->value, animated_value->is_result_of_transition, ComputedProperties::Inherited::Yes);
25512561
}
25522562

25532563
if (!value || value->is_initial() || value->is_unset())
25542564
value = property_initial_value(property_id);
25552565

25562566
computed_style->set_property(property_id, value.release_nonnull(), inherited, cascaded_properties.is_property_important(property_id) ? Important::Yes : Important::No);
2557-
if (animated_value.has_value())
2558-
computed_style->set_animated_property(property_id, animated_value.value(), inherited);
25592567
}
25602568

25612569
// Compute the value of custom properties

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
@@ -860,7 +860,7 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style()
860860

861861
if (computed_properties->is_animated_property_inherited(property_id) || !computed_properties->animated_property_values().contains(property_id)) {
862862
if (auto new_animated_value = CSS::StyleComputer::get_animated_inherit_value(property_id, { *this }); new_animated_value.has_value())
863-
computed_properties->set_animated_property(property_id, new_animated_value.value(), CSS::ComputedProperties::Inherited::Yes);
863+
computed_properties->set_animated_property(property_id, new_animated_value->value, new_animated_value->is_result_of_transition, CSS::ComputedProperties::Inherited::Yes);
864864
else if (computed_properties->animated_property_values().contains(property_id))
865865
computed_properties->remove_animated_property(property_id);
866866
}

Libraries/LibWeb/Forward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ enum class MediaFeatureID : u8;
398398
enum class PropertyID : u16;
399399
enum class PaintOrder : u8;
400400
enum class ValueType : u8;
401+
enum class AnimatedPropertyResultOfTransition : u8;
401402

402403
struct BackgroundLayerData;
403404
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)