LibWeb: Parse the position-area property

This commit is contained in:
Tim Ledbetter 2025-09-28 23:10:51 +01:00 committed by Jelle Raaijmakers
commit eb571a1a46
Notes: github-actions[bot] 2025-09-29 07:54:33 +00:00
12 changed files with 2733 additions and 0 deletions

View file

@ -734,6 +734,11 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
if (auto parsed_value = parse_place_self_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::PositionArea:
if (auto parsed_value = parse_position_area_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::Quotes:
if (auto parsed_value = parse_quotes_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
@ -4157,6 +4162,255 @@ RefPtr<StyleValue const> Parser::parse_place_self_value(TokenStream<ComponentVal
{ *maybe_align_self_value, *maybe_justify_self_value });
}
// https://drafts.csswg.org/css-anchor-position/#position-area
RefPtr<StyleValue const> Parser::parse_position_area_value(TokenStream<ComponentValue>& tokens)
{
// none | <position-area>
// none
if (auto none_keyword_value = parse_all_as_single_keyword_value(tokens, Keyword::None))
return none_keyword_value;
// <position-area>
// https://drafts.csswg.org/css-anchor-position/#position-area-syntax
// <position-area> = [
// [ left | center | right | span-left | span-right
// | x-start | x-end | span-x-start | span-x-end
// | x-self-start | x-self-end | span-x-self-start | span-x-self-end
// | span-all ]
// ||
// [ top | center | bottom | span-top | span-bottom
// | y-start | y-end | span-y-start | span-y-end
// | y-self-start | y-self-end | span-y-self-start | span-y-self-end
// | span-all ]
// |
// [ block-start | center | block-end | span-block-start | span-block-end | span-all ]
// ||
// [ inline-start | center | inline-end | span-inline-start | span-inline-end
// | span-all ]
// |
// [ self-block-start | center | self-block-end | span-self-block-start
// | span-self-block-end | span-all ]
// ||
// [ self-inline-start | center | self-inline-end | span-self-inline-start
// | span-self-inline-end | span-all ]
// |
// [ start | center | end | span-start | span-end | span-all ]{1,2}
// |
// [ self-start | center | self-end | span-self-start | span-self-end | span-all ]{1,2}
// ]
auto is_x_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::Left:
case Keyword::Center:
case Keyword::Right:
case Keyword::SpanLeft:
case Keyword::SpanRight:
case Keyword::XStart:
case Keyword::XEnd:
case Keyword::SpanXStart:
case Keyword::SpanXEnd:
case Keyword::XSelfStart:
case Keyword::XSelfEnd:
case Keyword::SpanXSelfStart:
case Keyword::SpanXSelfEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_y_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::Top:
case Keyword::Center:
case Keyword::Bottom:
case Keyword::SpanTop:
case Keyword::SpanBottom:
case Keyword::YStart:
case Keyword::YEnd:
case Keyword::SpanYStart:
case Keyword::SpanYEnd:
case Keyword::YSelfStart:
case Keyword::YSelfEnd:
case Keyword::SpanYSelfStart:
case Keyword::SpanYSelfEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_block_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::BlockStart:
case Keyword::Center:
case Keyword::BlockEnd:
case Keyword::SpanBlockStart:
case Keyword::SpanBlockEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_inline_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::InlineStart:
case Keyword::Center:
case Keyword::InlineEnd:
case Keyword::SpanInlineStart:
case Keyword::SpanInlineEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_self_block_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::SelfBlockStart:
case Keyword::Center:
case Keyword::SelfBlockEnd:
case Keyword::SpanSelfBlockStart:
case Keyword::SpanSelfBlockEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_self_inline_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::SelfInlineStart:
case Keyword::Center:
case Keyword::SelfInlineEnd:
case Keyword::SpanSelfInlineStart:
case Keyword::SpanSelfInlineEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_start_end_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::Start:
case Keyword::Center:
case Keyword::End:
case Keyword::SpanStart:
case Keyword::SpanEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_self_start_end_keyword = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::SelfStart:
case Keyword::Center:
case Keyword::SelfEnd:
case Keyword::SpanSelfStart:
case Keyword::SpanSelfEnd:
case Keyword::SpanAll:
return true;
default:
break;
}
return false;
};
auto is_axis_ambiguous = [](Keyword keyword) -> bool {
switch (keyword) {
case Keyword::Center:
case Keyword::SpanAll:
case Keyword::Start:
case Keyword::End:
case Keyword::SelfStart:
case Keyword::SelfEnd:
case Keyword::SpanStart:
case Keyword::SpanEnd:
case Keyword::SpanSelfStart:
case Keyword::SpanSelfEnd:
return true;
default:
break;
}
return false;
};
auto transaction = tokens.begin_transaction();
auto create_keyword_list = [&](StyleValue const& first, StyleValue const& second) -> NonnullRefPtr<StyleValue const> {
transaction.commit();
// If only a single keyword is given, it behaves as if the second keyword is span-all if the given keyword is
// unambigous about its axis.
// These conditions ensure the span-all keyword is omitted when it would be implied.
if (!is_axis_ambiguous(first.as_keyword().keyword()) && second.as_keyword().keyword() == Keyword::SpanAll)
return first;
if (!is_axis_ambiguous(second.as_keyword().keyword()) && first.as_keyword().keyword() == Keyword::SpanAll)
return second;
return StyleValueList::create({ first, second }, StyleValueList::Separator::Space);
};
auto first_keyword_value = parse_keyword_value(tokens);
if (!first_keyword_value)
return nullptr;
tokens.discard_whitespace();
if (!tokens.has_next_token()) {
auto first_position_area_keyword = keyword_to_position_area(first_keyword_value->as_keyword().keyword());
if (!first_position_area_keyword.has_value())
return nullptr;
transaction.commit();
return first_keyword_value;
}
auto second_keyword_value = parse_keyword_value(tokens);
if (!second_keyword_value)
return nullptr;
auto first_keyword = first_keyword_value->as_keyword().keyword();
auto second_keyword = second_keyword_value->as_keyword().keyword();
if (is_x_keyword(first_keyword) && is_y_keyword(second_keyword))
return create_keyword_list(*first_keyword_value, *second_keyword_value);
if (is_y_keyword(first_keyword) && is_x_keyword(second_keyword))
return create_keyword_list(*second_keyword_value, *first_keyword_value);
if (is_block_keyword(first_keyword) && is_inline_keyword(second_keyword))
return create_keyword_list(*first_keyword_value, *second_keyword_value);
if (is_inline_keyword(first_keyword) && is_block_keyword(second_keyword))
return create_keyword_list(*second_keyword_value, *first_keyword_value);
if (is_self_block_keyword(first_keyword) && is_self_inline_keyword(second_keyword))
return create_keyword_list(*first_keyword_value, *second_keyword_value);
if (is_self_inline_keyword(first_keyword) && is_self_block_keyword(second_keyword))
return create_keyword_list(*second_keyword_value, *first_keyword_value);
if (is_start_end_keyword(first_keyword) && is_start_end_keyword(second_keyword))
return create_keyword_list(*first_keyword_value, *second_keyword_value);
if (is_self_start_end_keyword(first_keyword) && is_self_start_end_keyword(second_keyword))
return create_keyword_list(*first_keyword_value, *second_keyword_value);
return nullptr;
}
RefPtr<StyleValue const> Parser::parse_quotes_value(TokenStream<ComponentValue>& tokens)
{
// https://www.w3.org/TR/css-content-3/#quotes-property