mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
LibWeb: Adapt parse_position()
into parse_position_value()
Having two ways that `<position>` is represented is awkward and unnecessary. So, let's combine the two paths together. This first step copies and modifies the `parse_position()` code to produce a `PositionStyleValue`. Apart from returning a StyleValue, this also makes use of automatic enum parsing instead of manually comparing identifier strings.
This commit is contained in:
parent
4ad58f0204
commit
8917378aa7
Notes:
sideshowbarker
2024-07-17 02:59:43 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/8917378aa7 Pull-request: https://github.com/SerenityOS/serenity/pull/21829
@ -334,6 +334,7 @@
|
||||
"sticky"
|
||||
],
|
||||
"position-edge": [
|
||||
"center",
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
|
@ -2558,6 +2558,208 @@ RefPtr<StyleValue> Parser::parse_paint_value(TokenStream<ComponentValue>& tokens
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-values-4/#position
|
||||
RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
auto parse_position_edge = [](ComponentValue const& token) -> Optional<PositionEdge> {
|
||||
if (!token.is(Token::Type::Ident))
|
||||
return {};
|
||||
auto ident = value_id_from_string(token.token().ident());
|
||||
if (!ident.has_value())
|
||||
return {};
|
||||
return value_id_to_position_edge(*ident);
|
||||
};
|
||||
|
||||
auto parse_length_percentage = [&](ComponentValue const& token) -> Optional<LengthPercentage> {
|
||||
if (token.is(Token::Type::EndOfFile))
|
||||
return {};
|
||||
// FIXME: calc()!
|
||||
auto dimension = parse_dimension(token);
|
||||
if (!dimension.has_value() || !dimension->is_length_percentage())
|
||||
return {};
|
||||
return dimension->length_percentage();
|
||||
};
|
||||
|
||||
auto is_horizontal = [](PositionEdge edge, bool accept_center) -> bool {
|
||||
switch (edge) {
|
||||
case PositionEdge::Left:
|
||||
case PositionEdge::Right:
|
||||
return true;
|
||||
case PositionEdge::Center:
|
||||
return accept_center;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto is_vertical = [](PositionEdge edge, bool accept_center) -> bool {
|
||||
switch (edge) {
|
||||
case PositionEdge::Top:
|
||||
case PositionEdge::Bottom:
|
||||
return true;
|
||||
case PositionEdge::Center:
|
||||
return accept_center;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto make_edge_style_value = [](PositionEdge position_edge, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> {
|
||||
if (position_edge == PositionEdge::Center)
|
||||
return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 });
|
||||
return EdgeStyleValue::create(position_edge, Length::make_px(0));
|
||||
};
|
||||
|
||||
// <position> = [
|
||||
// [ left | center | right ] || [ top | center | bottom ]
|
||||
// |
|
||||
// [ left | center | right | <length-percentage> ]
|
||||
// [ top | center | bottom | <length-percentage> ]?
|
||||
// |
|
||||
// [ [ left | right ] <length-percentage> ] &&
|
||||
// [ [ top | bottom ] <length-percentage> ]
|
||||
// ]
|
||||
|
||||
// [ left | center | right ] || [ top | center | bottom ]
|
||||
auto alternative_1 = [&]() -> RefPtr<PositionStyleValue> {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
auto maybe_first_edge = parse_position_edge(tokens.next_token());
|
||||
if (!maybe_first_edge.has_value())
|
||||
return nullptr;
|
||||
auto first_edge = maybe_first_edge.release_value();
|
||||
|
||||
// Try and parse the two-value variant
|
||||
tokens.skip_whitespace();
|
||||
auto maybe_second_edge = parse_position_edge(tokens.peek_token());
|
||||
if (maybe_second_edge.has_value()) {
|
||||
auto second_edge = maybe_second_edge.release_value();
|
||||
|
||||
if (is_horizontal(first_edge, true) && is_vertical(second_edge, true)) {
|
||||
(void)tokens.next_token(); // second_edge
|
||||
transaction.commit();
|
||||
return PositionStyleValue::create(make_edge_style_value(first_edge, true), make_edge_style_value(second_edge, false));
|
||||
} else if (is_vertical(first_edge, true) && is_horizontal(second_edge, true)) {
|
||||
(void)tokens.next_token(); // second_edge
|
||||
transaction.commit();
|
||||
return PositionStyleValue::create(make_edge_style_value(second_edge, true), make_edge_style_value(first_edge, false));
|
||||
}
|
||||
|
||||
// Otherwise, second value isn't valid as part of this position, so ignore it and fall back to single-edge parsing.
|
||||
}
|
||||
|
||||
// Single-value variant
|
||||
transaction.commit();
|
||||
if (is_horizontal(first_edge, false))
|
||||
return PositionStyleValue::create(make_edge_style_value(first_edge, true), make_edge_style_value(PositionEdge::Center, false));
|
||||
if (is_vertical(first_edge, false))
|
||||
return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(first_edge, false));
|
||||
VERIFY(first_edge == PositionEdge::Center);
|
||||
return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(PositionEdge::Center, false));
|
||||
};
|
||||
|
||||
// [ left | center | right | <length-percentage> ]
|
||||
// [ top | center | bottom | <length-percentage> ]?
|
||||
auto alternative_2 = [&]() -> RefPtr<PositionStyleValue> {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
RefPtr<EdgeStyleValue> horizontal_edge;
|
||||
RefPtr<EdgeStyleValue> vertical_edge;
|
||||
|
||||
auto& first_token = tokens.next_token();
|
||||
if (auto edge = parse_position_edge(first_token); edge.has_value() && is_horizontal(*edge, true)) {
|
||||
horizontal_edge = make_edge_style_value(*edge, true);
|
||||
} else {
|
||||
auto length_percentage = parse_length_percentage(first_token);
|
||||
if (!length_percentage.has_value())
|
||||
return nullptr;
|
||||
horizontal_edge = EdgeStyleValue::create(PositionEdge::Left, *length_percentage);
|
||||
}
|
||||
|
||||
auto transaction_optional_parse = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token()) {
|
||||
auto& second_token = tokens.next_token();
|
||||
if (auto edge = parse_position_edge(second_token); edge.has_value() && is_vertical(*edge, true)) {
|
||||
transaction_optional_parse.commit();
|
||||
vertical_edge = make_edge_style_value(*edge, false);
|
||||
} else {
|
||||
auto length_percentage = parse_length_percentage(second_token);
|
||||
if (length_percentage.has_value()) {
|
||||
transaction_optional_parse.commit();
|
||||
vertical_edge = EdgeStyleValue::create(PositionEdge::Top, *length_percentage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
if (!vertical_edge)
|
||||
vertical_edge = make_edge_style_value(PositionEdge::Center, false);
|
||||
return PositionStyleValue::create(horizontal_edge.release_nonnull(), vertical_edge.release_nonnull());
|
||||
};
|
||||
|
||||
// [ [ left | right ] <length-percentage> ] &&
|
||||
// [ [ top | bottom ] <length-percentage> ]
|
||||
auto alternative_3 = [&]() -> RefPtr<PositionStyleValue> {
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
RefPtr<EdgeStyleValue> horizontal_edge;
|
||||
RefPtr<EdgeStyleValue> vertical_edge;
|
||||
|
||||
auto parse_horizontal = [&] {
|
||||
// [ left | right ] <length-percentage> ]
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
auto edge = parse_position_edge(tokens.next_token());
|
||||
if (!edge.has_value() || !is_horizontal(*edge, false))
|
||||
return false;
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto length_percentage = parse_length_percentage(tokens.next_token());
|
||||
if (!length_percentage.has_value())
|
||||
return false;
|
||||
|
||||
horizontal_edge = EdgeStyleValue::create(*edge, *length_percentage);
|
||||
transaction.commit();
|
||||
return true;
|
||||
};
|
||||
|
||||
auto parse_vertical = [&] {
|
||||
// [ top | bottom ] <length-percentage> ]
|
||||
auto transaction = tokens.begin_transaction();
|
||||
tokens.skip_whitespace();
|
||||
auto edge = parse_position_edge(tokens.next_token());
|
||||
if (!edge.has_value() || !is_vertical(*edge, false))
|
||||
return false;
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto length_percentage = parse_length_percentage(tokens.next_token());
|
||||
if (!length_percentage.has_value())
|
||||
return false;
|
||||
|
||||
vertical_edge = EdgeStyleValue::create(*edge, *length_percentage);
|
||||
transaction.commit();
|
||||
return true;
|
||||
};
|
||||
|
||||
if ((parse_horizontal() && parse_vertical()) || (parse_vertical() && parse_horizontal())) {
|
||||
transaction.commit();
|
||||
return PositionStyleValue::create(horizontal_edge.release_nonnull(), vertical_edge.release_nonnull());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// Note: The alternatives must be attempted in this order since `alternative_2' can match a prefix of `alternative_3'
|
||||
if (auto position = alternative_3())
|
||||
return position.release_nonnull();
|
||||
if (auto position = alternative_2())
|
||||
return position;
|
||||
if (auto position = alternative_1())
|
||||
return position;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename ParseFunction>
|
||||
RefPtr<StyleValue> Parser::parse_comma_separated_value_list(Vector<ComponentValue> const& component_values, ParseFunction parse_one_value)
|
||||
{
|
||||
|
@ -217,6 +217,7 @@ private:
|
||||
RefPtr<StyleValue> parse_string_value(ComponentValue const&);
|
||||
RefPtr<StyleValue> parse_image_value(ComponentValue const&);
|
||||
RefPtr<StyleValue> parse_paint_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<PositionStyleValue> parse_position_value(TokenStream<ComponentValue>&);
|
||||
template<typename ParseFunction>
|
||||
RefPtr<StyleValue> parse_comma_separated_value_list(Vector<ComponentValue> const&, ParseFunction);
|
||||
RefPtr<StyleValue> parse_simple_comma_separated_value_list(PropertyID, Vector<ComponentValue> const&);
|
||||
|
@ -16,10 +16,12 @@ class EdgeStyleValue final : public StyleValueWithDefaultOperators<EdgeStyleValu
|
||||
public:
|
||||
static ValueComparingNonnullRefPtr<EdgeStyleValue> create(PositionEdge edge, LengthPercentage const& offset)
|
||||
{
|
||||
VERIFY(edge != PositionEdge::Center);
|
||||
return adopt_ref(*new (nothrow) EdgeStyleValue(edge, offset));
|
||||
}
|
||||
virtual ~EdgeStyleValue() override = default;
|
||||
|
||||
// NOTE: `center` is converted to `left 50%` or `top 50%` in parsing, so is never returned here.
|
||||
PositionEdge edge() const { return m_properties.edge; }
|
||||
LengthPercentage const& offset() const { return m_properties.offset; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user