mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 12:35:14 +00:00
LibWeb: Implement CounterStyleValue
This is `counter(name, style?)` or `counters(name, link, style?)`. The difference being, `counter()` matches only the nearest level (eg, "1"), and `counters()` combines all the levels in the tree (eg, "3.4.1").
This commit is contained in:
parent
017d6c3314
commit
576a431408
Notes:
github-actions[bot]
2024-07-26 10:05:34 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/576a4314084 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/720 Reviewed-by: https://github.com/tcl3
11 changed files with 274 additions and 3 deletions
|
@ -22,9 +22,28 @@ bool is_animatable_property(JsonObject& properties, StringView property_name);
|
|||
static bool type_name_is_enum(StringView type_name)
|
||||
{
|
||||
return !AK::first_is_one_of(type_name,
|
||||
"angle"sv, "background-position"sv, "basic-shape"sv, "color"sv, "custom-ident"sv, "easing-function"sv, "flex"sv, "frequency"sv, "image"sv,
|
||||
"integer"sv, "length"sv, "number"sv, "paint"sv, "percentage"sv, "position"sv, "ratio"sv, "rect"sv,
|
||||
"resolution"sv, "string"sv, "time"sv, "url"sv);
|
||||
"angle"sv,
|
||||
"background-position"sv,
|
||||
"basic-shape"sv,
|
||||
"color"sv,
|
||||
"counter"sv,
|
||||
"custom-ident"sv,
|
||||
"easing-function"sv,
|
||||
"flex"sv,
|
||||
"frequency"sv,
|
||||
"image"sv,
|
||||
"integer"sv,
|
||||
"length"sv,
|
||||
"number"sv,
|
||||
"paint"sv,
|
||||
"percentage"sv,
|
||||
"position"sv,
|
||||
"ratio"sv,
|
||||
"rect"sv,
|
||||
"resolution"sv,
|
||||
"string"sv,
|
||||
"time"sv,
|
||||
"url"sv);
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
|
@ -183,6 +202,7 @@ enum class ValueType {
|
|||
BackgroundPosition,
|
||||
BasicShape,
|
||||
Color,
|
||||
Counter,
|
||||
CustomIdent,
|
||||
EasingFunction,
|
||||
FilterValueList,
|
||||
|
@ -732,6 +752,8 @@ bool property_accepts_type(PropertyID property_id, ValueType value_type)
|
|||
property_generator.appendln(" case ValueType::BasicShape:");
|
||||
} else if (type_name == "color") {
|
||||
property_generator.appendln(" case ValueType::Color:");
|
||||
} else if (type_name == "counter") {
|
||||
property_generator.appendln(" case ValueType::Counter:");
|
||||
} else if (type_name == "custom-ident") {
|
||||
property_generator.appendln(" case ValueType::CustomIdent:");
|
||||
} else if (type_name == "easing-function") {
|
||||
|
|
|
@ -111,6 +111,7 @@ set(SOURCES
|
|||
CSS/StyleValues/ConicGradientStyleValue.cpp
|
||||
CSS/StyleValues/ContentStyleValue.cpp
|
||||
CSS/StyleValues/CounterDefinitionsStyleValue.cpp
|
||||
CSS/StyleValues/CounterStyleValue.cpp
|
||||
CSS/StyleValues/DisplayStyleValue.cpp
|
||||
CSS/StyleValues/EasingStyleValue.cpp
|
||||
CSS/StyleValues/EdgeStyleValue.cpp
|
||||
|
|
|
@ -36,6 +36,7 @@ Optional<CSSNumericType::BaseType> CSSNumericType::base_type_from_value_type(Val
|
|||
case ValueType::BackgroundPosition:
|
||||
case ValueType::BasicShape:
|
||||
case ValueType::Color:
|
||||
case ValueType::Counter:
|
||||
case ValueType::CustomIdent:
|
||||
case ValueType::EasingFunction:
|
||||
case ValueType::FilterValueList:
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
||||
|
@ -2892,6 +2893,118 @@ RefPtr<StyleValue> Parser::parse_color_value(TokenStream<ComponentValue>& tokens
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-lists-3/#counter-functions
|
||||
RefPtr<StyleValue> Parser::parse_counter_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
auto parse_counter_name = [](TokenStream<ComponentValue>& tokens) -> Optional<FlyString> {
|
||||
// https://drafts.csswg.org/css-lists-3/#typedef-counter-name
|
||||
// Counters are referred to in CSS syntax using the <counter-name> type, which represents
|
||||
// their name as a <custom-ident>. A <counter-name> name cannot match the keyword none;
|
||||
// such an identifier is invalid as a <counter-name>.
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto& token = tokens.next_token();
|
||||
if (!token.is(Token::Type::Ident) || token.token().ident() == "none"sv)
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token())
|
||||
return {};
|
||||
|
||||
transaction.commit();
|
||||
return token.token().ident();
|
||||
};
|
||||
|
||||
auto parse_counter_style = [](TokenStream<ComponentValue>& tokens) -> RefPtr<StyleValue> {
|
||||
// https://drafts.csswg.org/css-counter-styles-3/#typedef-counter-style
|
||||
// <counter-style> = <counter-style-name> | <symbols()>
|
||||
// For now we just support <counter-style-name>, found here:
|
||||
// https://drafts.csswg.org/css-counter-styles-3/#typedef-counter-style-name
|
||||
// <counter-style-name> is a <custom-ident> that is not an ASCII case-insensitive match for none.
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto& token = tokens.next_token();
|
||||
if (!token.is(Token::Type::Ident) || token.token().ident() == "none"sv)
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token())
|
||||
return {};
|
||||
|
||||
transaction.commit();
|
||||
return CustomIdentStyleValue::create(token.token().ident());
|
||||
};
|
||||
|
||||
auto transaction = tokens.begin_transaction();
|
||||
auto token = tokens.next_token();
|
||||
if (token.is_function("counter"sv)) {
|
||||
// counter() = counter( <counter-name>, <counter-style>? )
|
||||
auto& function = token.function();
|
||||
TokenStream function_tokens { function.values() };
|
||||
auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens);
|
||||
if (function_values.is_empty() || function_values.size() > 2)
|
||||
return nullptr;
|
||||
|
||||
TokenStream name_tokens { function_values[0] };
|
||||
auto counter_name = parse_counter_name(name_tokens);
|
||||
if (!counter_name.has_value())
|
||||
return nullptr;
|
||||
|
||||
RefPtr<StyleValue> counter_style;
|
||||
if (function_values.size() > 1) {
|
||||
TokenStream counter_style_tokens { function_values[1] };
|
||||
counter_style = parse_counter_style(counter_style_tokens);
|
||||
if (!counter_style)
|
||||
return nullptr;
|
||||
} else {
|
||||
// In both cases, if the <counter-style> argument is omitted it defaults to `decimal`.
|
||||
counter_style = CustomIdentStyleValue::create("decimal"_fly_string);
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
return CounterStyleValue::create_counter(counter_name.release_value(), counter_style.release_nonnull());
|
||||
}
|
||||
|
||||
if (token.is_function("counters"sv)) {
|
||||
// counters() = counters( <counter-name>, <string>, <counter-style>? )
|
||||
auto& function = token.function();
|
||||
TokenStream function_tokens { function.values() };
|
||||
auto function_values = parse_a_comma_separated_list_of_component_values(function_tokens);
|
||||
if (function_values.is_empty() || function_values.size() > 3)
|
||||
return nullptr;
|
||||
|
||||
TokenStream name_tokens { function_values[0] };
|
||||
auto counter_name = parse_counter_name(name_tokens);
|
||||
if (!counter_name.has_value())
|
||||
return nullptr;
|
||||
|
||||
TokenStream string_tokens { function_values[1] };
|
||||
string_tokens.skip_whitespace();
|
||||
RefPtr<StyleValue> join_string = parse_string_value(string_tokens);
|
||||
string_tokens.skip_whitespace();
|
||||
if (!join_string || string_tokens.has_next_token())
|
||||
return nullptr;
|
||||
|
||||
RefPtr<StyleValue> counter_style;
|
||||
if (function_values.size() > 2) {
|
||||
TokenStream counter_style_tokens { function_values[2] };
|
||||
counter_style = parse_counter_style(counter_style_tokens);
|
||||
if (!counter_style)
|
||||
return nullptr;
|
||||
} else {
|
||||
// In both cases, if the <counter-style> argument is omitted it defaults to `decimal`.
|
||||
counter_style = CustomIdentStyleValue::create("decimal"_fly_string);
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
return CounterStyleValue::create_counters(counter_name.release_value(), join_string->as_string().string_value(), counter_style.release_nonnull());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_counter_definitions_value(TokenStream<ComponentValue>& tokens, AllowReversed allow_reversed, i32 default_value_if_not_reversed)
|
||||
{
|
||||
// If AllowReversed is Yes, parses:
|
||||
|
@ -7141,6 +7254,11 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
|
|||
return PropertyAndValue { *property, maybe_color };
|
||||
}
|
||||
|
||||
if (auto property = any_property_accepts_type(property_ids, ValueType::Counter); property.has_value()) {
|
||||
if (auto maybe_counter = parse_counter_value(tokens))
|
||||
return PropertyAndValue { *property, maybe_counter };
|
||||
}
|
||||
|
||||
if (auto property = any_property_accepts_type(property_ids, ValueType::Image); property.has_value()) {
|
||||
if (auto maybe_image = parse_image_value(tokens))
|
||||
return PropertyAndValue { *property, maybe_image };
|
||||
|
|
|
@ -288,6 +288,7 @@ private:
|
|||
RefPtr<StyleValue> parse_number_or_percentage_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue> parse_identifier_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue> parse_color_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue> parse_counter_value(TokenStream<ComponentValue>&);
|
||||
enum class AllowReversed {
|
||||
No,
|
||||
Yes,
|
||||
|
|
|
@ -966,6 +966,7 @@
|
|||
"initial": "normal",
|
||||
"__comment": "FIXME: This accepts a whole lot of other types and identifiers!",
|
||||
"valid-types": [
|
||||
"counter",
|
||||
"string"
|
||||
],
|
||||
"valid-identifiers": [
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
||||
|
|
|
@ -92,6 +92,7 @@ using StyleValueVector = Vector<ValueComparingNonnullRefPtr<StyleValue const>>;
|
|||
__ENUMERATE_STYLE_VALUE_TYPE(Color, color) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(ConicGradient, conic_gradient) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(Content, content) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(Counter, counter) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(CounterDefinitions, counter_definitions) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(CustomIdent, custom_ident) \
|
||||
__ENUMERATE_STYLE_VALUE_TYPE(Display, display) \
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "CounterStyleValue.h"
|
||||
#include <LibWeb/CSS/Serialize.h>
|
||||
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
CounterStyleValue::CounterStyleValue(CounterFunction function, FlyString counter_name, ValueComparingNonnullRefPtr<StyleValue const> counter_style, FlyString join_string)
|
||||
: StyleValueWithDefaultOperators(Type::Counter)
|
||||
, m_properties {
|
||||
.function = function,
|
||||
.counter_name = move(counter_name),
|
||||
.counter_style = move(counter_style),
|
||||
.join_string = move(join_string)
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
CounterStyleValue::~CounterStyleValue() = default;
|
||||
|
||||
// https://drafts.csswg.org/cssom-1/#ref-for-typedef-counter
|
||||
String CounterStyleValue::to_string() const
|
||||
{
|
||||
// The return value of the following algorithm:
|
||||
// 1. Let s be the empty string.
|
||||
StringBuilder s;
|
||||
|
||||
// 2. If <counter> has three CSS component values append the string "counters(" to s.
|
||||
if (m_properties.function == CounterFunction::Counters)
|
||||
s.append("counters("sv);
|
||||
|
||||
// 3. If <counter> has two CSS component values append the string "counter(" to s.
|
||||
else if (m_properties.function == CounterFunction::Counter)
|
||||
s.append("counter("sv);
|
||||
|
||||
// 4. Let list be a list of CSS component values belonging to <counter>,
|
||||
// omitting the last CSS component value if it is "decimal".
|
||||
Vector<RefPtr<StyleValue const>> list;
|
||||
list.append(CustomIdentStyleValue::create(m_properties.counter_name));
|
||||
if (m_properties.function == CounterFunction::Counters)
|
||||
list.append(StringStyleValue::create(m_properties.join_string.to_string()));
|
||||
if (m_properties.counter_style->to_identifier() != ValueID::Decimal)
|
||||
list.append(m_properties.counter_style);
|
||||
|
||||
// 5. Let each item in list be the result of invoking serialize a CSS component value on that item.
|
||||
// 6. Append the result of invoking serialize a comma-separated list on list to s.
|
||||
serialize_a_comma_separated_list(s, list, [](auto& builder, auto& item) {
|
||||
builder.append(item->to_string());
|
||||
});
|
||||
|
||||
// 7. Append ")" (U+0029) to s.
|
||||
s.append(")"sv);
|
||||
|
||||
// 8. Return s.
|
||||
return MUST(s.to_string());
|
||||
}
|
||||
|
||||
bool CounterStyleValue::properties_equal(CounterStyleValue const& other) const
|
||||
{
|
||||
return m_properties == other.m_properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibWeb/CSS/StyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
// https://drafts.csswg.org/css-lists-3/#counter-functions
|
||||
class CounterStyleValue : public StyleValueWithDefaultOperators<CounterStyleValue> {
|
||||
public:
|
||||
enum class CounterFunction {
|
||||
Counter,
|
||||
Counters,
|
||||
};
|
||||
|
||||
static ValueComparingNonnullRefPtr<CounterStyleValue> create_counter(FlyString counter_name, ValueComparingNonnullRefPtr<StyleValue const> counter_style)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) CounterStyleValue(CounterFunction::Counter, move(counter_name), move(counter_style), {}));
|
||||
}
|
||||
static ValueComparingNonnullRefPtr<CounterStyleValue> create_counters(FlyString counter_name, FlyString join_string, ValueComparingNonnullRefPtr<StyleValue const> counter_style)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) CounterStyleValue(CounterFunction::Counters, move(counter_name), move(counter_style), move(join_string)));
|
||||
}
|
||||
virtual ~CounterStyleValue() override;
|
||||
|
||||
CounterFunction function_type() const { return m_properties.function; }
|
||||
auto counter_name() const { return m_properties.counter_name; }
|
||||
auto counter_style() const { return m_properties.counter_style; }
|
||||
auto join_string() const { return m_properties.join_string; }
|
||||
|
||||
String resolve(DOM::Element&) const;
|
||||
|
||||
virtual String to_string() const override;
|
||||
|
||||
bool properties_equal(CounterStyleValue const& other) const;
|
||||
|
||||
private:
|
||||
explicit CounterStyleValue(CounterFunction, FlyString counter_name, ValueComparingNonnullRefPtr<StyleValue const> counter_style, FlyString join_string);
|
||||
|
||||
struct Properties {
|
||||
CounterFunction function;
|
||||
FlyString counter_name;
|
||||
ValueComparingNonnullRefPtr<StyleValue const> counter_style;
|
||||
FlyString join_string;
|
||||
bool operator==(Properties const&) const = default;
|
||||
} m_properties;
|
||||
};
|
||||
|
||||
}
|
|
@ -120,6 +120,7 @@ class Clip;
|
|||
class ColorStyleValue;
|
||||
class ConicGradientStyleValue;
|
||||
class ContentStyleValue;
|
||||
class CounterStyleValue;
|
||||
class CounterDefinitionsStyleValue;
|
||||
class CustomIdentStyleValue;
|
||||
class Display;
|
||||
|
|
Loading…
Add table
Reference in a new issue