LibWeb/CSS: Start parsing the color() function

This is really bare bone as we only support the `xyz-d50` color space
for the moment.

It makes us pass the following WPT tests:
 - css/css-color/predefined-016.html
 - css/css-color/xyz-d50-001.html
 - css/css-color/xyz-d50-002.html
This commit is contained in:
Lucas CHOLLET 2024-10-27 00:42:13 -04:00 committed by Andreas Kling
parent a6794627b0
commit 48bbebc636
Notes: github-actions[bot] 2024-10-28 22:33:33 +00:00
7 changed files with 166 additions and 0 deletions

View file

@ -7,6 +7,7 @@ source_set("StyleValues") {
"BackgroundSizeStyleValue.cpp",
"BasicShapeStyleValue.cpp",
"BorderRadiusStyleValue.cpp",
"CSSColor.cpp",
"CSSColorValue.cpp",
"CSSHSL.cpp",
"CSSHWB.cpp",

View file

@ -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

View file

@ -44,6 +44,7 @@
#include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.h>
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
#include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h>
#include <LibWeb/CSS/StyleValues/CSSColor.h>
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
#include <LibWeb/CSS/StyleValues/CSSHSL.h>
#include <LibWeb/CSS/StyleValues/CSSHWB.h>
@ -3387,6 +3388,66 @@ RefPtr<CSSStyleValue> Parser::parse_oklch_color_value(TokenStream<ComponentValue
return CSSOKLCH::create(l.release_nonnull(), c.release_nonnull(), h.release_nonnull(), alpha.release_nonnull());
}
// https://www.w3.org/TR/css-color-4/#funcdef-color
RefPtr<CSSStyleValue> Parser::parse_color_function(TokenStream<ComponentValue>& outer_tokens)
{
// color() = color( <colorspace-params> [ / [ <alpha-value> | none ] ]? )
// <colorspace-params> = [ <predefined-rgb-params> | <xyz-params>]
// <predefined-rgb-params> = <predefined-rgb> [ <number> | <percentage> | none ]{3}
// <predefined-rgb> = srgb | srgb-linear | display-p3 | a98-rgb | prophoto-rgb | rec2020
// <xyz-params> = <xyz-space> [ <number> | <percentage> | none ]{3}
// <xyz-space> = 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<CSSStyleValue> 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<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tokens)
{
@ -3401,6 +3462,9 @@ RefPtr<CSSStyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& 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))

View file

@ -254,6 +254,7 @@ private:
RefPtr<CSSStyleValue> parse_lab_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_oklab_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_oklch_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_function(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_color_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_counter_value(TokenStream<ComponentValue>&);
enum class AllowReversed {

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2024, Lucas Chollet <lucas.chollet@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSColor.h"
#include <AK/TypeCasts.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
namespace Web::CSS {
ValueComparingNonnullRefPtr<CSSColor> CSSColor::create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue> c1, ValueComparingNonnullRefPtr<CSSStyleValue> c2, ValueComparingNonnullRefPtr<CSSStyleValue> c3, ValueComparingRefPtr<CSSStyleValue> 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<CSSColor>(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<Layout::NodeWithStyle const&>) 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

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2024, Lucas Chollet <lucas.chollet@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
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<CSSColor> create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue> c1, ValueComparingNonnullRefPtr<CSSStyleValue> c2, ValueComparingNonnullRefPtr<CSSStyleValue> c3, ValueComparingRefPtr<CSSStyleValue> alpha = {});
virtual bool equals(CSSStyleValue const&) const override;
virtual Color to_color(Optional<Layout::NodeWithStyle const&>) const override;
virtual String to_string() const override;
static constexpr Array s_supported_color_space = { "xyz-d50"sv };
private:
CSSColor(ColorType color_type, ValueComparingNonnullRefPtr<CSSStyleValue> c1, ValueComparingNonnullRefPtr<CSSStyleValue> c2, ValueComparingNonnullRefPtr<CSSStyleValue> c3, ValueComparingNonnullRefPtr<CSSStyleValue> alpha)
: CSSColorValue(color_type)
, m_properties { .channels = { move(c1), move(c2), move(c3) }, .alpha = move(alpha) }
{
}
struct Properties {
Array<ValueComparingNonnullRefPtr<CSSStyleValue>, 3> channels;
ValueComparingNonnullRefPtr<CSSStyleValue> alpha;
bool operator==(Properties const&) const = default;
} m_properties;
};
} // Web::CSS

View file

@ -29,6 +29,7 @@ public:
Lab,
OKLab,
OKLCH,
XYZD50,
};
ColorType color_type() const { return m_color_type; }