mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:47:35 +00:00
LibWeb: Allow 'center' in 3-value background-positions
The grammar which we implemented did not match the spec, resulting in us rejecting background positions which contained 'center' position keywords. Fixes: #22401
This commit is contained in:
parent
f7fc2df8ac
commit
d95461ebe1
1 changed files with 59 additions and 64 deletions
|
@ -4,6 +4,7 @@
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
|
||||||
|
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -2818,84 +2819,78 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
|
||||||
};
|
};
|
||||||
|
|
||||||
// The extra 3-value syntax that's allowed for background-position:
|
// The extra 3-value syntax that's allowed for background-position:
|
||||||
// [
|
// [ center | [ left | right ] <length-percentage>? ] &&
|
||||||
// [ [ left | right ] <length-percentage> ] &&
|
// [ center | [ top | bottom ] <length-percentage>? ]
|
||||||
// [ top | bottom ]
|
|
||||||
// |
|
|
||||||
// [ left | right ] &&
|
|
||||||
// [ [ top | bottom ] <length-percentage> ]
|
|
||||||
// ]
|
|
||||||
auto alternative_5_for_background_position = [&]() -> RefPtr<PositionStyleValue> {
|
auto alternative_5_for_background_position = [&]() -> RefPtr<PositionStyleValue> {
|
||||||
auto transaction = tokens.begin_transaction();
|
auto transaction = tokens.begin_transaction();
|
||||||
tokens.skip_whitespace();
|
|
||||||
Optional<PositionEdge> horizontal_edge;
|
|
||||||
Optional<LengthPercentage> horizontal_offset;
|
|
||||||
Optional<PositionEdge> vertical_edge;
|
|
||||||
Optional<LengthPercentage> vertical_offset;
|
|
||||||
|
|
||||||
auto matches = [&]() {
|
struct PositionAndMaybeLength {
|
||||||
return horizontal_edge.has_value() && vertical_edge.has_value()
|
PositionEdge position;
|
||||||
&& horizontal_offset.has_value() != vertical_offset.has_value();
|
Optional<LengthPercentage> length;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parse_horizontal = [&] {
|
// [ <position> <length-percentage>? ]
|
||||||
// [ left | right ] <length-percentage>?
|
auto parse_position_and_maybe_length = [&]() -> Optional<PositionAndMaybeLength> {
|
||||||
auto transaction = tokens.begin_transaction();
|
|
||||||
tokens.skip_whitespace();
|
tokens.skip_whitespace();
|
||||||
auto edge = parse_position_edge(tokens.next_token());
|
|
||||||
if (!edge.has_value() || !is_horizontal(*edge, false))
|
auto maybe_position = parse_position_edge(tokens.next_token());
|
||||||
return false;
|
if (!maybe_position.has_value())
|
||||||
horizontal_edge = move(edge);
|
return {};
|
||||||
|
|
||||||
tokens.skip_whitespace();
|
tokens.skip_whitespace();
|
||||||
auto length_percentage = parse_length_percentage(tokens.peek_token());
|
|
||||||
if (length_percentage.has_value()) {
|
auto maybe_length = parse_length_percentage(tokens.peek_token());
|
||||||
(void)tokens.next_token(); // offset
|
if (maybe_length.has_value()) {
|
||||||
horizontal_offset = move(length_percentage);
|
// 'center' cannot be followed by a <length-percentage>
|
||||||
|
if (maybe_position.value() == PositionEdge::Center && maybe_length.has_value())
|
||||||
|
return {};
|
||||||
|
tokens.next_token();
|
||||||
}
|
}
|
||||||
transaction.commit();
|
|
||||||
return true;
|
return PositionAndMaybeLength {
|
||||||
|
.position = maybe_position.release_value(),
|
||||||
|
.length = move(maybe_length),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parse_vertical = [&] {
|
auto maybe_group1 = parse_position_and_maybe_length();
|
||||||
// [ top | bottom ] <length-percentage>?
|
if (!maybe_group1.has_value())
|
||||||
auto transaction = tokens.begin_transaction();
|
return nullptr;
|
||||||
tokens.skip_whitespace();
|
|
||||||
auto edge = parse_position_edge(tokens.next_token());
|
|
||||||
if (!edge.has_value() || !is_vertical(*edge, false))
|
|
||||||
return false;
|
|
||||||
vertical_edge = move(edge);
|
|
||||||
|
|
||||||
tokens.skip_whitespace();
|
auto maybe_group2 = parse_position_and_maybe_length();
|
||||||
auto length_percentage = parse_length_percentage(tokens.peek_token());
|
if (!maybe_group2.has_value())
|
||||||
if (length_percentage.has_value()) {
|
return nullptr;
|
||||||
(void)tokens.next_token(); // offset
|
|
||||||
vertical_offset = move(length_percentage);
|
auto group1 = maybe_group1.release_value();
|
||||||
}
|
auto group2 = maybe_group2.release_value();
|
||||||
transaction.commit();
|
|
||||||
return true;
|
// 2-value or 4-value if both <length-percentage>s are present or missing.
|
||||||
|
if (group1.length.has_value() == group2.length.has_value())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If 'left' or 'right' is given, that position is X and the other is Y.
|
||||||
|
// Conversely -
|
||||||
|
// If 'top' or 'bottom' is given, that position is Y and the other is X.
|
||||||
|
if (is_vertical(group1.position, false) || is_horizontal(group2.position, false))
|
||||||
|
swap(group1, group2);
|
||||||
|
|
||||||
|
// [ center | [ left | right ] ]
|
||||||
|
if (!is_horizontal(group1.position, true))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// [ center | [ top | bottom ] ]
|
||||||
|
if (!is_vertical(group2.position, true))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto to_style_value = [&](PositionAndMaybeLength const& group, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> {
|
||||||
|
if (group.position == PositionEdge::Center)
|
||||||
|
return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 });
|
||||||
|
|
||||||
|
return EdgeStyleValue::create(group.position, group.length.value_or(Length::make_px(0)));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (parse_horizontal() && parse_vertical() && matches()) {
|
transaction.commit();
|
||||||
transaction.commit();
|
return PositionStyleValue::create(to_style_value(group1, true), to_style_value(group2, false));
|
||||||
return PositionStyleValue::create(
|
|
||||||
EdgeStyleValue::create(*horizontal_edge, horizontal_offset.value_or(Length::make_px(0))),
|
|
||||||
EdgeStyleValue::create(*vertical_edge, vertical_offset.value_or(Length::make_px(0))));
|
|
||||||
}
|
|
||||||
|
|
||||||
horizontal_edge.clear();
|
|
||||||
horizontal_offset.clear();
|
|
||||||
vertical_edge.clear();
|
|
||||||
vertical_offset.clear();
|
|
||||||
|
|
||||||
if (parse_vertical() && parse_horizontal() && matches()) {
|
|
||||||
transaction.commit();
|
|
||||||
return PositionStyleValue::create(
|
|
||||||
EdgeStyleValue::create(*horizontal_edge, horizontal_offset.value_or(Length::make_px(0))),
|
|
||||||
EdgeStyleValue::create(*vertical_edge, vertical_offset.value_or(Length::make_px(0))));
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: The alternatives must be attempted in this order since shorter alternatives can match a prefix of longer ones.
|
// Note: The alternatives must be attempted in this order since shorter alternatives can match a prefix of longer ones.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue