diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn index 0ec2c8f0151..1ea51c3dcd9 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn @@ -7,6 +7,7 @@ source_set("StyleValues") { "BackgroundSizeStyleValue.cpp", "BasicShapeStyleValue.cpp", "BorderRadiusStyleValue.cpp", + "CSSColor.cpp", "CSSColorValue.cpp", "CSSHSL.cpp", "CSSHWB.cpp", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index cf90c6fde2f..8b0f30de825 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -115,6 +115,7 @@ set(SOURCES CSS/StyleValues/ContentStyleValue.cpp CSS/StyleValues/CounterDefinitionsStyleValue.cpp CSS/StyleValues/CounterStyleValue.cpp + CSS/StyleValues/CSSColor.cpp CSS/StyleValues/CSSColorValue.cpp CSS/StyleValues/CSSHSL.cpp CSS/StyleValues/CSSHWB.cpp diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 0fbb7c3e797..b748ba425d4 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -3387,6 +3388,66 @@ RefPtr Parser::parse_oklch_color_value(TokenStream Parser::parse_color_function(TokenStream& outer_tokens) +{ + // color() = color( [ / [ | none ] ]? ) + // = [ | ] + // = [ | | none ]{3} + // = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020 + // = [ | | none ]{3} + // = xyz | xyz-d50 | xyz-d65 + + auto transaction = outer_tokens.begin_transaction(); + outer_tokens.discard_whitespace(); + + auto const& function_token = outer_tokens.consume_a_token(); + if (!function_token.is_function("color"sv)) + return {}; + + auto inner_tokens = TokenStream { function_token.function().value }; + inner_tokens.discard_whitespace(); + + auto maybe_color_space = inner_tokens.consume_a_token(); + inner_tokens.discard_whitespace(); + if (!any_of(CSSColor::s_supported_color_space, [&](auto supported) { return maybe_color_space.is_ident(supported); })) + return {}; + + auto const& color_space = maybe_color_space.token().ident(); + + auto c1 = parse_number_percentage_value(inner_tokens); + if (!c1) + return {}; + inner_tokens.discard_whitespace(); + + auto c2 = parse_number_percentage_value(inner_tokens); + if (!c2) + return {}; + inner_tokens.discard_whitespace(); + + auto c3 = parse_number_percentage_value(inner_tokens); + if (!c3) + return {}; + inner_tokens.discard_whitespace(); + + RefPtr alpha; + if (inner_tokens.has_next_token()) { + alpha = parse_solidus_and_alpha_value(inner_tokens); + if (!alpha || inner_tokens.has_next_token()) + return {}; + } + + if (!alpha) + alpha = NumberStyleValue::create(1); + + transaction.commit(); + return CSSColor::create(color_space.to_ascii_lowercase(), + c1.release_nonnull(), + c2.release_nonnull(), + c3.release_nonnull(), + alpha.release_nonnull()); +} + // https://www.w3.org/TR/css-color-4/#color-syntax RefPtr Parser::parse_color_value(TokenStream& tokens) { @@ -3401,6 +3462,9 @@ RefPtr Parser::parse_color_value(TokenStream& tok } // Functions + if (auto color = parse_color_function(tokens)) + return color; + if (auto rgb = parse_rgb_color_value(tokens)) return rgb; if (auto hsl = parse_hsl_color_value(tokens)) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 2cb4dd4b4d7..a12d176fc4b 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -254,6 +254,7 @@ private: RefPtr parse_lab_color_value(TokenStream&); RefPtr parse_oklab_color_value(TokenStream&); RefPtr parse_oklch_color_value(TokenStream&); + RefPtr parse_color_function(TokenStream&); RefPtr parse_color_value(TokenStream&); RefPtr parse_counter_value(TokenStream&); enum class AllowReversed { diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.cpp new file mode 100644 index 00000000000..5c147a59c6e --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CSSColor.h" +#include +#include +#include + +namespace Web::CSS { + +ValueComparingNonnullRefPtr CSSColor::create(StringView color_space, ValueComparingNonnullRefPtr c1, ValueComparingNonnullRefPtr c2, ValueComparingNonnullRefPtr c3, ValueComparingRefPtr alpha) +{ + VERIFY(any_of(s_supported_color_space, [=](auto supported) { return color_space == supported; })); + + if (!alpha) + alpha = NumberStyleValue::create(1); + + if (color_space == "xyz-d50") + return adopt_ref(*new (nothrow) CSSColor(ColorType::XYZD50, move(c1), move(c2), move(c3), alpha.release_nonnull())); + + VERIFY_NOT_REACHED(); +} + +bool CSSColor::equals(CSSStyleValue 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_lab_like = verify_cast(other_color); + return m_properties == other_lab_like.m_properties; +} + +// https://www.w3.org/TR/css-color-4/#serializing-color-function-values +String CSSColor::to_string() const +{ + // FIXME: Do this properly, taking unresolved calculated values into account. + return serialize_a_srgb_value(to_color({})); +} + +Color CSSColor::to_color(Optional) const +{ + auto const c1 = resolve_with_reference_value(m_properties.channels[0], 100).value_or(0); + auto const c2 = resolve_with_reference_value(m_properties.channels[1], 100).value_or(0); + auto const c3 = resolve_with_reference_value(m_properties.channels[2], 100).value_or(0); + auto const alpha_val = resolve_alpha(m_properties.alpha).value_or(1); + + if (color_type() == ColorType::XYZD50) + return Color::from_xyz50(c1, c2, c3, alpha_val); + + VERIFY_NOT_REACHED(); +} + +} // Web::CSS diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.h b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.h new file mode 100644 index 00000000000..5a0f402fa44 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColor.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::CSS { + +// https://drafts.css-houdini.org/css-typed-om-1/#csscolor +class CSSColor final : public CSSColorValue { +public: + virtual ~CSSColor() override = default; + + static ValueComparingNonnullRefPtr create(StringView color_space, ValueComparingNonnullRefPtr c1, ValueComparingNonnullRefPtr c2, ValueComparingNonnullRefPtr c3, ValueComparingRefPtr alpha = {}); + + virtual bool equals(CSSStyleValue const&) const override; + virtual Color to_color(Optional) const override; + virtual String to_string() const override; + + static constexpr Array s_supported_color_space = { "xyz-d50"sv }; + +private: + CSSColor(ColorType color_type, ValueComparingNonnullRefPtr c1, ValueComparingNonnullRefPtr c2, ValueComparingNonnullRefPtr c3, ValueComparingNonnullRefPtr alpha) + : CSSColorValue(color_type) + , m_properties { .channels = { move(c1), move(c2), move(c3) }, .alpha = move(alpha) } + { + } + + struct Properties { + Array, 3> channels; + ValueComparingNonnullRefPtr alpha; + bool operator==(Properties const&) const = default; + } m_properties; +}; + +} // Web::CSS diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColorValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColorValue.h index 32ab45a1881..5398fe87a76 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColorValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CSSColorValue.h @@ -29,6 +29,7 @@ public: Lab, OKLab, OKLCH, + XYZD50, }; ColorType color_type() const { return m_color_type; }