|
@@ -2518,6 +2518,212 @@ RefPtr<StyleValue> Parser::parse_linear_gradient_function(ComponentValue const&
|
|
return LinearGradientStyleValue::create(gradient_direction, move(color_stops), gradient_type, repeating_gradient);
|
|
return LinearGradientStyleValue::create(gradient_direction, move(color_stops), gradient_type, repeating_gradient);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+Optional<PositionValue> Parser::parse_position(TokenStream<ComponentValue>& tokens)
|
|
|
|
+{
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (!tokens.has_next_token())
|
|
|
|
+ return {};
|
|
|
|
+
|
|
|
|
+ auto parse_horizontal_preset = [&](auto ident) -> Optional<PositionValue::HorizontalPreset> {
|
|
|
|
+ if (ident.equals_ignoring_case("left"sv))
|
|
|
|
+ return PositionValue::HorizontalPreset::Left;
|
|
|
|
+ if (ident.equals_ignoring_case("center"sv))
|
|
|
|
+ return PositionValue::HorizontalPreset::Center;
|
|
|
|
+ if (ident.equals_ignoring_case("right"sv))
|
|
|
|
+ return PositionValue::HorizontalPreset::Right;
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ auto parse_vertical_preset = [&](auto ident) -> Optional<PositionValue::VerticalPreset> {
|
|
|
|
+ if (ident.equals_ignoring_case("top"sv))
|
|
|
|
+ return PositionValue::VerticalPreset::Top;
|
|
|
|
+ if (ident.equals_ignoring_case("center"sv))
|
|
|
|
+ return PositionValue::VerticalPreset::Center;
|
|
|
|
+ if (ident.equals_ignoring_case("bottom"sv))
|
|
|
|
+ return PositionValue::VerticalPreset::Bottom;
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ auto parse_horizontal_edge = [&](auto ident) -> Optional<PositionValue::HorizontalEdge> {
|
|
|
|
+ if (ident.equals_ignoring_case("left"sv))
|
|
|
|
+ return PositionValue::HorizontalEdge::Left;
|
|
|
|
+ if (ident.equals_ignoring_case("right"sv))
|
|
|
|
+ return PositionValue::HorizontalEdge::Right;
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ auto parse_vertical_edge = [&](auto ident) -> Optional<PositionValue::VerticalEdge> {
|
|
|
|
+ if (ident.equals_ignoring_case("top"sv))
|
|
|
|
+ return PositionValue::VerticalEdge::Top;
|
|
|
|
+ if (ident.equals_ignoring_case("bottom"sv))
|
|
|
|
+ return PositionValue::VerticalEdge::Bottom;
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // <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 alternation_1 = [&]() -> Optional<PositionValue> {
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ PositionValue position {};
|
|
|
|
+ auto& first_token = tokens.next_token();
|
|
|
|
+ if (!first_token.is(Token::Type::Ident))
|
|
|
|
+ return {};
|
|
|
|
+ auto ident = first_token.token().ident();
|
|
|
|
+ // <horizontal-position> <vertical-position>?
|
|
|
|
+ auto horizontal_position = parse_horizontal_preset(ident);
|
|
|
|
+ if (horizontal_position.has_value()) {
|
|
|
|
+ position.horizontal_position = *horizontal_position;
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (tokens.has_next_token()) {
|
|
|
|
+ auto& second_token = tokens.next_token();
|
|
|
|
+ if (!second_token.is(Token::Type::Ident))
|
|
|
|
+ return {};
|
|
|
|
+ auto vertical_position = parse_vertical_preset(second_token.token().ident());
|
|
|
|
+ if (!vertical_position.has_value())
|
|
|
|
+ return {};
|
|
|
|
+ position.vertical_position = *vertical_position;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // <vertical-position> <horizontal-position>?
|
|
|
|
+ auto vertical_position = parse_vertical_preset(ident);
|
|
|
|
+ if (!vertical_position.has_value())
|
|
|
|
+ return {};
|
|
|
|
+ position.vertical_position = *vertical_position;
|
|
|
|
+ if (tokens.has_next_token()) {
|
|
|
|
+ auto& second_token = tokens.next_token();
|
|
|
|
+ if (!second_token.is(Token::Type::Ident))
|
|
|
|
+ return {};
|
|
|
|
+ auto horizontal_position = parse_horizontal_preset(second_token.token().ident());
|
|
|
|
+ if (!horizontal_position.has_value())
|
|
|
|
+ return {};
|
|
|
|
+ position.horizontal_position = *horizontal_position;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return position;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // [ left | center | right | <length-percentage> ]
|
|
|
|
+ // [ top | center | bottom | <length-percentage> ]?
|
|
|
|
+ auto alternation_2 = [&]() -> Optional<PositionValue> {
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ PositionValue position {};
|
|
|
|
+ auto& first_token = tokens.next_token();
|
|
|
|
+ if (first_token.is(Token::Type::Ident)) {
|
|
|
|
+ auto horizontal_position = parse_horizontal_preset(first_token.token().ident());
|
|
|
|
+ if (!horizontal_position.has_value())
|
|
|
|
+ return {};
|
|
|
|
+ position.horizontal_position = *horizontal_position;
|
|
|
|
+ } else {
|
|
|
|
+ auto dimension = parse_dimension(first_token);
|
|
|
|
+ if (!dimension.has_value() || !dimension->is_length_percentage())
|
|
|
|
+ return {};
|
|
|
|
+ position.horizontal_position = dimension->length_percentage();
|
|
|
|
+ }
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (tokens.has_next_token()) {
|
|
|
|
+ auto& second_token = tokens.next_token();
|
|
|
|
+ if (second_token.is(Token::Type::Ident)) {
|
|
|
|
+ auto vertical_position = parse_vertical_preset(second_token.token().ident());
|
|
|
|
+ if (!vertical_position.has_value())
|
|
|
|
+ return {};
|
|
|
|
+ position.vertical_position = *vertical_position;
|
|
|
|
+ } else {
|
|
|
|
+ auto dimension = parse_dimension(second_token);
|
|
|
|
+ if (!dimension.has_value() || !dimension->is_length_percentage())
|
|
|
|
+ return {};
|
|
|
|
+ position.vertical_position = dimension->length_percentage();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return position;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // [ [ left | right ] <length-percentage> ] &&
|
|
|
|
+ // [ [ top | bottom ] <length-percentage> ]
|
|
|
|
+ auto alternation_3 = [&]() -> Optional<PositionValue> {
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ PositionValue position {};
|
|
|
|
+
|
|
|
|
+ auto parse_horizontal = [&] {
|
|
|
|
+ // [ left | right ] <length-percentage> ]
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (!tokens.has_next_token())
|
|
|
|
+ return false;
|
|
|
|
+ auto& first_token = tokens.next_token();
|
|
|
|
+ if (!first_token.is(Token::Type::Ident))
|
|
|
|
+ return false;
|
|
|
|
+ auto horizontal_egde = parse_horizontal_edge(first_token.token().ident());
|
|
|
|
+ if (!horizontal_egde.has_value())
|
|
|
|
+ return false;
|
|
|
|
+ position.x_relative_to = *horizontal_egde;
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (!tokens.has_next_token())
|
|
|
|
+ return false;
|
|
|
|
+ auto& second_token = tokens.next_token();
|
|
|
|
+ auto dimension = parse_dimension(second_token);
|
|
|
|
+ if (!dimension.has_value() || !dimension->is_length_percentage())
|
|
|
|
+ return false;
|
|
|
|
+ position.horizontal_position = dimension->length_percentage();
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return true;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ auto parse_vertical = [&] {
|
|
|
|
+ // [ top | bottom ] <length-percentage> ]
|
|
|
|
+ auto transaction = tokens.begin_transaction();
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (!tokens.has_next_token())
|
|
|
|
+ return false;
|
|
|
|
+ auto& first_token = tokens.next_token();
|
|
|
|
+ if (!first_token.is(Token::Type::Ident))
|
|
|
|
+ return false;
|
|
|
|
+ auto vertical_edge = parse_vertical_edge(first_token.token().ident());
|
|
|
|
+ if (!vertical_edge.has_value())
|
|
|
|
+ return false;
|
|
|
|
+ position.y_relative_to = *vertical_edge;
|
|
|
|
+ tokens.skip_whitespace();
|
|
|
|
+ if (!tokens.has_next_token())
|
|
|
|
+ return false;
|
|
|
|
+ auto& second_token = tokens.next_token();
|
|
|
|
+ auto dimension = parse_dimension(second_token);
|
|
|
|
+ if (!dimension.has_value() || !dimension->is_length_percentage())
|
|
|
|
+ return false;
|
|
|
|
+ position.vertical_position = dimension->length_percentage();
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return true;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if ((parse_horizontal() && parse_vertical()) || (parse_vertical() && parse_horizontal())) {
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return position;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {};
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // Note: The alternatives must be attempted in this order since `alternation_2' can match a prefix of `alternation_3'
|
|
|
|
+ auto position = alternation_3();
|
|
|
|
+ if (!position.has_value())
|
|
|
|
+ position = alternation_2();
|
|
|
|
+ if (!position.has_value())
|
|
|
|
+ position = alternation_1();
|
|
|
|
+ if (position.has_value())
|
|
|
|
+ transaction.commit();
|
|
|
|
+ return position;
|
|
|
|
+}
|
|
|
|
+
|
|
CSSRule* Parser::convert_to_rule(NonnullRefPtr<Rule> rule)
|
|
CSSRule* Parser::convert_to_rule(NonnullRefPtr<Rule> rule)
|
|
{
|
|
{
|
|
if (rule->is_at_rule()) {
|
|
if (rule->is_at_rule()) {
|