LibWeb: Route hsl()/hsla() through unified ColorFunctionStyleValue

This commit is contained in:
Tim Ledbetter 2026-04-17 11:42:07 +01:00 committed by Sam Atkins
parent 9ea880dcf0
commit ebe12a8766
6 changed files with 7 additions and 181 deletions

View file

@ -265,7 +265,6 @@ set(SOURCES
CSS/StyleValues/GridTemplateAreaStyleValue.cpp
CSS/StyleValues/GridTrackPlacementStyleValue.cpp
CSS/StyleValues/GridTrackSizeListStyleValue.cpp
CSS/StyleValues/HSLColorStyleValue.cpp
CSS/StyleValues/HWBColorStyleValue.cpp
CSS/StyleValues/ImageStyleValue.cpp
CSS/StyleValues/IntegerStyleValue.cpp

View file

@ -9,7 +9,6 @@
#include <LibWeb/CSS/ColorInterpolation.h>
#include <LibWeb/CSS/Interpolation.h>
#include <LibWeb/CSS/StyleValues/ColorFunctionStyleValue.h>
#include <LibWeb/CSS/StyleValues/HSLColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/HWBColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/KeywordStyleValue.h>
#include <LibWeb/CSS/StyleValues/LCHLikeColorStyleValue.h>
@ -125,8 +124,8 @@ static MissingComponents extract_missing_components(StyleValue const& style_valu
return {};
switch (*color_type) {
case ColorStyleValue::ColorType::HSL: {
auto const& hsl = as<HSLColorStyleValue>(color);
return { is_component_none(hsl.h()), is_component_none(hsl.s()), is_component_none(hsl.l()), is_component_none(hsl.alpha()) };
auto const& hsl = as<ColorFunctionStyleValue>(color);
return { is_component_none(hsl.channel(0)), is_component_none(hsl.channel(1)), is_component_none(hsl.channel(2)), is_component_none(hsl.alpha()) };
}
case ColorStyleValue::ColorType::HWB: {
auto const& hwb = as<HWBColorStyleValue>(color);
@ -399,10 +398,10 @@ static Optional<Gfx::ColorComponents> style_value_to_color_components(StyleValue
switch (*color_type) {
case ColorStyleValue::ColorType::HSL: {
auto const& hsl = as<HSLColorStyleValue>(color);
auto h = ColorStyleValue::resolve_hue(hsl.h(), context);
auto s = ColorStyleValue::resolve_with_reference_value(hsl.s(), 100.0f, context);
auto l = ColorStyleValue::resolve_with_reference_value(hsl.l(), 100.0f, context);
auto const& hsl = as<ColorFunctionStyleValue>(color);
auto h = ColorStyleValue::resolve_hue(hsl.channel(0), context);
auto s = ColorStyleValue::resolve_with_reference_value(hsl.channel(1), 100.0f, context);
auto l = ColorStyleValue::resolve_with_reference_value(hsl.channel(2), 100.0f, context);
auto a = resolve_alpha(hsl.alpha());
if (!h.has_value() || !s.has_value() || !l.has_value() || !a.has_value())
return {};

View file

@ -53,7 +53,6 @@
#include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h>
#include <LibWeb/CSS/StyleValues/GuaranteedInvalidStyleValue.h>
#include <LibWeb/CSS/StyleValues/HSLColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/HWBColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
@ -1683,7 +1682,7 @@ RefPtr<StyleValue const> Parser::parse_hsl_color_value(TokenStream<ComponentValu
alpha = NumberStyleValue::create(1);
transaction.commit();
return HSLColorStyleValue::create(h.release_nonnull(), s.release_nonnull(), l.release_nonnull(), alpha.release_nonnull(), legacy_syntax ? ColorSyntax::Legacy : ColorSyntax::Modern);
return ColorFunctionStyleValue::create(ColorStyleValue::ColorType::HSL, h.release_nonnull(), s.release_nonnull(), l.release_nonnull(), alpha.release_nonnull(), legacy_syntax ? ColorSyntax::Legacy : ColorSyntax::Modern);
}
// https://www.w3.org/TR/css-color-4/#funcdef-hwb

View file

@ -1,107 +0,0 @@
/*
* Copyright (c) 2024-2026, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "HSLColorStyleValue.h"
#include <AK/TypeCasts.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/ColorFunctionStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
namespace Web::CSS {
Optional<Color> HSLColorStyleValue::to_color(ColorResolutionContext color_resolution_context) const
{
auto h_val = resolve_hue(m_properties.h, color_resolution_context.calculation_resolution_context);
auto s_val = resolve_with_reference_value(m_properties.s, 100.0, color_resolution_context.calculation_resolution_context);
auto l_val = resolve_with_reference_value(m_properties.l, 100.0, color_resolution_context.calculation_resolution_context);
auto alpha_val = resolve_alpha(m_properties.alpha, color_resolution_context.calculation_resolution_context);
if (!h_val.has_value() || !s_val.has_value() || !l_val.has_value() || !alpha_val.has_value())
return {};
return Color::from_hsla(h_val.value(), s_val.value() / 100.0f, l_val.value() / 100.0f, alpha_val.value());
}
ValueComparingNonnullRefPtr<StyleValue const> HSLColorStyleValue::absolutized(ComputationContext const& context) const
{
auto absolutized_h = m_properties.h->absolutized(context);
auto absolutized_s = m_properties.s->absolutized(context);
auto absolutized_l = m_properties.l->absolutized(context);
auto absolutized_alpha = m_properties.alpha->absolutized(context);
// hsl() computes to rgb()
// https://drafts.csswg.org/css-color-4/#resolving-sRGB-values
auto resolved_h = resolve_hue(absolutized_h, {});
auto resolved_s = resolve_with_reference_value(absolutized_s, 100.0, {});
auto resolved_l = resolve_with_reference_value(absolutized_l, 100.0, {});
auto resolved_alpha = resolve_alpha(absolutized_alpha, {});
// These should all be computable at this point.
if (!resolved_h.has_value() || !resolved_s.has_value() || !resolved_l.has_value() || !resolved_alpha.has_value())
VERIFY_NOT_REACHED();
// https://drafts.csswg.org/css-color-4/#hsl-to-rgb
auto hue = fmod(resolved_h.value(), 360.0);
if (hue < 0.0)
hue += 360.0;
auto saturation = clamp(resolved_s.value() / 100.0, 0.0, 1.0);
auto lightness = clamp(resolved_l.value() / 100.0, 0.0, 1.0);
auto to_rgb = [](auto h, auto s, auto l, auto offset) {
auto k = fmod(offset + h / 30.0, 12.0);
auto a = s * min(l, 1.0 - l);
return l - a * max(-1.0, min(min(k - 3.0, 9.0 - k), 1.0));
};
auto r = to_rgb(hue, saturation, lightness, 0.0);
auto g = to_rgb(hue, saturation, lightness, 8.0);
auto b = to_rgb(hue, saturation, lightness, 4.0);
return ColorFunctionStyleValue::create(
ColorType::RGB,
NumberStyleValue::create(clamp(r * 255.0, 0, 255)),
NumberStyleValue::create(clamp(g * 255.0, 0, 255)),
NumberStyleValue::create(clamp(b * 255.0, 0, 255)),
NumberStyleValue::create(clamp(resolved_alpha.value(), 0.0, 1.0)),
ColorSyntax::Legacy);
}
bool HSLColorStyleValue::equals(StyleValue const& other) const
{
if (type() != other.type())
return false;
auto const& other_color = other.as_color();
if (color_type() != other_color.color_type())
return false;
auto const& other_hsl = as<HSLColorStyleValue>(other_color);
return m_properties == other_hsl.m_properties;
}
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
void HSLColorStyleValue::serialize(StringBuilder& builder, SerializationMode mode) const
{
if (auto color = to_color({}); color.has_value()) {
builder.append(color->serialize_a_srgb_value());
return;
}
builder.append("hsl("sv);
serialize_hue_component(builder, mode, m_properties.h);
builder.append(" "sv);
serialize_color_component(builder, mode, m_properties.s, 100, 0);
builder.append(" "sv);
serialize_color_component(builder, mode, m_properties.l, 100, 0);
if ((!m_properties.alpha->is_number() || m_properties.alpha->as_number().number() < 1) && (!m_properties.alpha->is_percentage() || m_properties.alpha->as_percentage().percentage().as_fraction() < 1)) {
builder.append(" / "sv);
serialize_alpha_component(builder, mode, m_properties.alpha);
}
builder.append(")"sv);
}
}

View file

@ -1,63 +0,0 @@
/*
* Copyright (c) 2024-2026, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/ComputationContext.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
namespace Web::CSS {
class HSLColorStyleValue final : public ColorStyleValue {
public:
static ValueComparingNonnullRefPtr<HSLColorStyleValue const> create(ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingNonnullRefPtr<StyleValue const> s, ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingRefPtr<StyleValue const> alpha, ColorSyntax color_syntax)
{
// alpha defaults to 1
if (!alpha)
return adopt_ref(*new (nothrow) HSLColorStyleValue(move(h), move(s), move(l), NumberStyleValue::create(1), color_syntax));
return adopt_ref(*new (nothrow) HSLColorStyleValue(move(h), move(s), move(l), alpha.release_nonnull(), color_syntax));
}
virtual ~HSLColorStyleValue() override = default;
StyleValue const& h() const { return *m_properties.h; }
StyleValue const& s() const { return *m_properties.s; }
StyleValue const& l() const { return *m_properties.l; }
StyleValue const& alpha() const { return *m_properties.alpha; }
virtual Optional<Color> to_color(ColorResolutionContext color_resolution_context) const override;
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
virtual void serialize(StringBuilder&, SerializationMode) const override;
virtual bool equals(StyleValue const& other) const override;
virtual bool is_computationally_independent() const override
{
return m_properties.h->is_computationally_independent()
&& m_properties.s->is_computationally_independent()
&& m_properties.l->is_computationally_independent()
&& m_properties.alpha->is_computationally_independent();
}
private:
HSLColorStyleValue(ValueComparingNonnullRefPtr<StyleValue const> h, ValueComparingNonnullRefPtr<StyleValue const> s, ValueComparingNonnullRefPtr<StyleValue const> l, ValueComparingNonnullRefPtr<StyleValue const> alpha, ColorSyntax color_syntax)
: ColorStyleValue(ColorType::HSL, color_syntax)
, m_properties { .h = move(h), .s = move(s), .l = move(l), .alpha = move(alpha) }
{
}
struct Properties {
ValueComparingNonnullRefPtr<StyleValue const> h;
ValueComparingNonnullRefPtr<StyleValue const> s;
ValueComparingNonnullRefPtr<StyleValue const> l;
ValueComparingNonnullRefPtr<StyleValue const> alpha;
bool operator==(Properties const&) const = default;
} m_properties;
};
}

View file

@ -343,7 +343,6 @@ class GridTrackPlacementStyleValue;
class GridTrackSizeList;
class GridTrackSizeListStyleValue;
class GuaranteedInvalidStyleValue;
class HSLColorStyleValue;
class HWBColorStyleValue;
class ImageStyleValue;
class IntegerStyleValue;