From fc5cdd69a065b4a52e55e8bada9a41b41dc13d51 Mon Sep 17 00:00:00 2001 From: Callum Law Date: Sat, 18 Oct 2025 18:03:58 +1300 Subject: [PATCH] LibWeb: Extract transition coordination logic to reusable method This will be useful for other coordinating list property groups (e.g. background and animation) --- Libraries/LibWeb/CSS/ComputedProperties.cpp | 37 ++++++++++++++ Libraries/LibWeb/CSS/ComputedProperties.h | 1 + Libraries/LibWeb/CSS/StyleComputer.cpp | 53 +++++---------------- Libraries/LibWeb/Layout/Node.cpp | 1 + 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/Libraries/LibWeb/CSS/ComputedProperties.cpp b/Libraries/LibWeb/CSS/ComputedProperties.cpp index 23211265919..d57576aeec1 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.cpp +++ b/Libraries/LibWeb/CSS/ComputedProperties.cpp @@ -312,6 +312,43 @@ Color ComputedProperties::color_or_fallback(PropertyID id, ColorResolutionContex return value.to_color(color_resolution_context).value(); } +// https://drafts.csswg.org/css-values-4/#linked-properties +HashMap ComputedProperties::assemble_coordinated_value_list(PropertyID base_property_id, Vector const& property_ids) const +{ + // A coordinating list property group creates a coordinated value list, which has, for each entry, a value from each + // property in the group; these are used together to define a single effect, such as a background image layer or an + // animation. The coordinated value list is assembled as follows: + // - The length of the coordinated value list is determined by the number of items specified in one particular + // coordinating list property, the coordinating list base property. (In the case of backgrounds, this is the + // background-image property.) + // - The Nth value of the coordinated value list is constructed by collecting the Nth use value of each coordinating + // list property + // - If a coordinating list property has too many values specified, excess values at the end of its list are not + // used. + // - If a coordinating list property has too few values specified, its value list is repeated to add more used + // values. + // - The computed values of the coordinating list properties are not affected by such truncation or repetition. + + // FIXME: This is only required until we update parse_comma_separated_list to always return a StyleValueList + auto const get_property_value_as_list = [&](PropertyID property_id) { + auto const& value = property(property_id); + + return value.is_value_list() ? value.as_value_list().values() : StyleValueVector { value }; + }; + + HashMap coordinated_value_list; + + for (size_t i = 0; i < get_property_value_as_list(base_property_id).size(); i++) { + for (auto property_id : property_ids) { + auto const& list = get_property_value_as_list(property_id); + + coordinated_value_list.ensure(property_id).append(list[i % list.size()]); + } + } + + return coordinated_value_list; +} + ColorInterpolation ComputedProperties::color_interpolation() const { auto const& value = property(PropertyID::ColorInterpolation); diff --git a/Libraries/LibWeb/CSS/ComputedProperties.h b/Libraries/LibWeb/CSS/ComputedProperties.h index 01c56273a93..edaffcce706 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.h +++ b/Libraries/LibWeb/CSS/ComputedProperties.h @@ -84,6 +84,7 @@ public: Optional length_percentage(PropertyID, Layout::NodeWithStyle const&, ClampNegativeLengths) const; LengthBox length_box(PropertyID left_id, PropertyID top_id, PropertyID right_id, PropertyID bottom_id, Layout::NodeWithStyle const&, ClampNegativeLengths, LengthPercentageOrAuto const& default_value) const; Color color_or_fallback(PropertyID, ColorResolutionContext, Color fallback) const; + HashMap assemble_coordinated_value_list(PropertyID base_property_id, Vector const& property_ids) const; ColorInterpolation color_interpolation() const; PreferredColorScheme color_scheme(PreferredColorScheme, Optional const&> document_supported_schemes) const; TextAnchor text_anchor() const; diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 2cd001205b5..4186ead7311 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -1351,11 +1351,11 @@ static void compute_transitioned_properties(ComputedProperties const& style, DOM element.clear_transitions(pseudo_element); element.set_cached_transition_property_source(pseudo_element, *source_declaration); - auto const& transition_properties_value = style.property(PropertyID::TransitionProperty); - auto transition_properties = transition_properties_value.is_value_list() - ? transition_properties_value.as_value_list().values() - : StyleValueVector { transition_properties_value }; + auto coordinated_transition_list = style.assemble_coordinated_value_list( + PropertyID::TransitionProperty, + { PropertyID::TransitionProperty, PropertyID::TransitionDuration, PropertyID::TransitionTimingFunction, PropertyID::TransitionDelay, PropertyID::TransitionBehavior }); + auto transition_properties = coordinated_transition_list.get(PropertyID::TransitionProperty).value(); Vector> properties; for (size_t i = 0; i < transition_properties.size(); i++) { @@ -1396,44 +1396,13 @@ static void compute_transitioned_properties(ComputedProperties const& style, DOM properties.append(move(properties_for_this_transition)); } - auto normalize_transition_length_list = [&properties, &style](PropertyID property, auto make_default_value) { - auto const& style_value = style.property(property); - StyleValueVector list; - - if (!style_value.is_value_list()) { - for (size_t i = 0; i < properties.size(); i++) - list.append(style_value); - return list; - } - - if (style_value.as_value_list().size() == 0) { - auto default_value = make_default_value(); - for (size_t i = 0; i < properties.size(); i++) - list.append(default_value); - return list; - } - - auto const& value_list = style_value.as_value_list(); - for (size_t i = 0; i < properties.size(); i++) - list.append(value_list.value_at(i, true)); - - return list; - }; - - auto delays = normalize_transition_length_list( - PropertyID::TransitionDelay, - [] { return TimeStyleValue::create(Time::make_seconds(0.0)); }); - auto durations = normalize_transition_length_list( - PropertyID::TransitionDuration, - [] { return TimeStyleValue::create(Time::make_seconds(0.0)); }); - auto timing_functions = normalize_transition_length_list( - PropertyID::TransitionTimingFunction, - [] { return KeywordStyleValue::create(Keyword::Ease); }); - auto transition_behaviors = normalize_transition_length_list( - PropertyID::TransitionBehavior, - [] { return KeywordStyleValue::create(Keyword::None); }); - - element.add_transitioned_properties(pseudo_element, move(properties), move(delays), move(durations), move(timing_functions), move(transition_behaviors)); + element.add_transitioned_properties( + pseudo_element, + move(properties), + move(coordinated_transition_list.get(PropertyID::TransitionDelay).value()), + move(coordinated_transition_list.get(PropertyID::TransitionDuration).value()), + move(coordinated_transition_list.get(PropertyID::TransitionTimingFunction).value()), + move(coordinated_transition_list.get(PropertyID::TransitionBehavior).value())); } // https://drafts.csswg.org/css-transitions/#starting diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 3d0ed0219c5..676f6444600 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -392,6 +392,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) computed_values.set_vertical_align(computed_style.vertical_align()); { + // FIXME: Use `ComputedProperties::assemble_coordinated_value_list()` for this auto const& attachments = computed_style.property(CSS::PropertyID::BackgroundAttachment); auto const& clips = computed_style.property(CSS::PropertyID::BackgroundClip); auto const& images = computed_style.property(CSS::PropertyID::BackgroundImage);