LibWeb: Parse and plumb background-position-x/y

This parses the new background-position-x/y longhands and properly
hooks up them up. This requires converting PositionStyleValue to
just contain two EdgeStyleValues so that it can be easily expanded
into the longhands.
This commit is contained in:
MacDue 2023-04-03 00:04:00 +01:00 committed by Andreas Kling
parent 2a659693bc
commit bed55ac669
Notes: sideshowbarker 2024-07-17 05:13:53 +09:00
7 changed files with 135 additions and 67 deletions

View file

@ -41,6 +41,7 @@
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h> #include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h> #include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h> #include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h> #include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexFlowStyleValue.h> #include <LibWeb/CSS/StyleValues/FlexFlowStyleValue.h>
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h> #include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
@ -4270,6 +4271,33 @@ RefPtr<StyleValue> Parser::parse_background_value(Vector<ComponentValue> const&
background_clip.release_nonnull()); background_clip.release_nonnull());
} }
static Optional<PositionEdge> identifier_to_edge(ValueID identifier)
{
switch (identifier) {
case ValueID::Top:
return PositionEdge::Top;
case ValueID::Bottom:
return PositionEdge::Bottom;
case ValueID::Left:
return PositionEdge::Left;
case ValueID::Right:
return PositionEdge::Right;
default:
return {};
}
};
static Optional<LengthPercentage> style_value_to_length_percentage(auto value)
{
if (value->is_percentage())
return LengthPercentage { value->as_percentage().percentage() };
if (value->has_length())
return LengthPercentage { value->to_length() };
if (value->is_calculated())
return LengthPercentage { value->as_calculated() };
return {};
};
RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<ComponentValue>& tokens) RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<ComponentValue>& tokens)
{ {
// NOTE: This *looks* like it parses a <position>, but it doesn't. From the spec: // NOTE: This *looks* like it parses a <position>, but it doesn't. From the spec:
@ -4281,20 +4309,6 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
auto transaction = tokens.begin_transaction(); auto transaction = tokens.begin_transaction();
auto to_edge = [](ValueID identifier) -> Optional<PositionEdge> {
switch (identifier) {
case ValueID::Top:
return PositionEdge::Top;
case ValueID::Bottom:
return PositionEdge::Bottom;
case ValueID::Left:
return PositionEdge::Left;
case ValueID::Right:
return PositionEdge::Right;
default:
return {};
}
};
auto is_horizontal = [](ValueID identifier) -> bool { auto is_horizontal = [](ValueID identifier) -> bool {
switch (identifier) { switch (identifier) {
case ValueID::Left: case ValueID::Left:
@ -4328,16 +4342,6 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
auto const center_offset = Percentage { 50 }; auto const center_offset = Percentage { 50 };
auto const zero_offset = Length::make_px(0); auto const zero_offset = Length::make_px(0);
auto value_to_length_percentage = [&](auto value) -> Optional<LengthPercentage> {
if (value->is_percentage())
return LengthPercentage { value->as_percentage().percentage() };
if (value->has_length())
return LengthPercentage { value->to_length() };
if (value->is_calculated())
return LengthPercentage { value->as_calculated() };
return {};
};
while (tokens.has_next_token()) { while (tokens.has_next_token()) {
// Check if we're done // Check if we're done
auto seen_items = (horizontal.has_value() ? 1 : 0) + (vertical.has_value() ? 1 : 0) + (found_center ? 1 : 0); auto seen_items = (horizontal.has_value() ? 1 : 0) + (vertical.has_value() ? 1 : 0) + (found_center ? 1 : 0);
@ -4351,7 +4355,7 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
tokens.next_token(); tokens.next_token();
auto value = maybe_value.release_nonnull(); auto value = maybe_value.release_nonnull();
auto offset = value_to_length_percentage(value); auto offset = style_value_to_length_percentage(value);
if (offset.has_value()) { if (offset.has_value()) {
if (!horizontal.has_value()) { if (!horizontal.has_value()) {
horizontal = EdgeOffset { PositionEdge::Left, *offset, false, true }; horizontal = EdgeOffset { PositionEdge::Left, *offset, false, true };
@ -4369,7 +4373,7 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
auto maybe_value = parse_css_value(token); auto maybe_value = parse_css_value(token);
if (!maybe_value) if (!maybe_value)
return zero_offset; return zero_offset;
auto offset = value_to_length_percentage(maybe_value.release_nonnull()); auto offset = style_value_to_length_percentage(maybe_value.release_nonnull());
if (offset.has_value()) { if (offset.has_value()) {
offset_provided = true; offset_provided = true;
tokens.next_token(); tokens.next_token();
@ -4384,11 +4388,11 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
if (is_horizontal(identifier)) { if (is_horizontal(identifier)) {
bool offset_provided = false; bool offset_provided = false;
auto offset = try_parse_offset(offset_provided); auto offset = try_parse_offset(offset_provided);
horizontal = EdgeOffset { *to_edge(identifier), offset, true, offset_provided }; horizontal = EdgeOffset { *identifier_to_edge(identifier), offset, true, offset_provided };
} else if (is_vertical(identifier)) { } else if (is_vertical(identifier)) {
bool offset_provided = false; bool offset_provided = false;
auto offset = try_parse_offset(offset_provided); auto offset = try_parse_offset(offset_provided);
vertical = EdgeOffset { *to_edge(identifier), offset, true, offset_provided }; vertical = EdgeOffset { *identifier_to_edge(identifier), offset, true, offset_provided };
} else if (identifier == ValueID::Center) { } else if (identifier == ValueID::Center) {
found_center = true; found_center = true;
} else { } else {
@ -4436,8 +4440,63 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
transaction.commit(); transaction.commit();
return PositionStyleValue::create( return PositionStyleValue::create(
horizontal->edge, horizontal->offset, EdgeStyleValue::create(horizontal->edge, horizontal->offset),
vertical->edge, vertical->offset); EdgeStyleValue::create(vertical->edge, vertical->offset));
}
RefPtr<StyleValue> Parser::parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>& tokens, PropertyID property)
{
PositionEdge relative_edge {};
if (property == PropertyID::BackgroundPositionX) {
// [ center | [ [ left | right | x-start | x-end ]? <length-percentage>? ]! ]#
relative_edge = PositionEdge::Left;
} else if (property == PropertyID::BackgroundPositionY) {
// [ center | [ [ top | bottom | y-start | y-end ]? <length-percentage>? ]! ]#
relative_edge = PositionEdge::Top;
} else {
VERIFY_NOT_REACHED();
}
auto transaction = tokens.begin_transaction();
if (!tokens.has_next_token())
return {};
auto parse_value = [&](auto& token) -> RefPtr<StyleValue> {
auto maybe_value = parse_css_value(token);
if (!maybe_value || !property_accepts_value(property, *maybe_value))
return {};
return maybe_value.release_nonnull();
};
auto value = parse_value(tokens.next_token());
if (value->has_identifier()) {
auto identifier = value->to_identifier();
if (identifier == ValueID::Center) {
transaction.commit();
return EdgeStyleValue::create(relative_edge, Percentage { 50 });
}
if (auto edge = identifier_to_edge(identifier); edge.has_value()) {
relative_edge = *edge;
} else {
return {};
}
if (tokens.has_next_token()) {
value = parse_value(tokens.peek_token());
if (!value) {
transaction.commit();
return EdgeStyleValue::create(relative_edge, Length::make_px(0));
}
tokens.next_token();
}
}
auto offset = style_value_to_length_percentage(value);
if (offset.has_value()) {
transaction.commit();
return EdgeStyleValue::create(relative_edge, *offset);
}
return {};
} }
RefPtr<StyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens) RefPtr<StyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens)
@ -6531,6 +6590,11 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_position_value(tokens); })) if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_position_value(tokens); }))
return parsed_value.release_nonnull(); return parsed_value.release_nonnull();
return ParseError::SyntaxError; return ParseError::SyntaxError;
case PropertyID::BackgroundPositionX:
case PropertyID::BackgroundPositionY:
if (auto parsed_value = parse_comma_separated_value_list(component_values, [this, property_id](auto& tokens) { return parse_single_background_position_x_or_y_value(tokens, property_id); }))
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::BackgroundRepeat: case PropertyID::BackgroundRepeat:
if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_repeat_value(tokens); })) if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_repeat_value(tokens); }))
return parsed_value.release_nonnull(); return parsed_value.release_nonnull();

View file

@ -294,6 +294,7 @@ private:
RefPtr<StyleValue> parse_filter_value_list_value(Vector<ComponentValue> const&); RefPtr<StyleValue> parse_filter_value_list_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_background_value(Vector<ComponentValue> const&); RefPtr<StyleValue> parse_background_value(Vector<ComponentValue> const&);
RefPtr<StyleValue> parse_single_background_position_value(TokenStream<ComponentValue>&); RefPtr<StyleValue> parse_single_background_position_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>&, PropertyID);
RefPtr<StyleValue> parse_single_background_repeat_value(TokenStream<ComponentValue>&); RefPtr<StyleValue> parse_single_background_repeat_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_single_background_size_value(TokenStream<ComponentValue>&); RefPtr<StyleValue> parse_single_background_size_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_border_value(Vector<ComponentValue> const&); RefPtr<StyleValue> parse_border_value(Vector<ComponentValue> const&);

View file

@ -19,6 +19,7 @@
#include <LibWeb/CSS/StyleValues/BorderStyleValue.h> #include <LibWeb/CSS/StyleValues/BorderStyleValue.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h> #include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h> #include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridAreaShorthandStyleValue.h> #include <LibWeb/CSS/StyleValues/GridAreaShorthandStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTrackPlacementShorthandStyleValue.h> #include <LibWeb/CSS/StyleValues/GridTrackPlacementShorthandStyleValue.h>
#include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h> #include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
@ -195,7 +196,7 @@ RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(L
return BackgroundStyleValue::create( return BackgroundStyleValue::create(
value_or_default(maybe_background_color, InitialStyleValue::the()), value_or_default(maybe_background_color, InitialStyleValue::the()),
value_or_default(maybe_background_image, IdentifierStyleValue::create(CSS::ValueID::None)), value_or_default(maybe_background_image, IdentifierStyleValue::create(CSS::ValueID::None)),
value_or_default(maybe_background_position, PositionStyleValue::create(PositionEdge::Left, Length::make_px(0), PositionEdge::Top, Length::make_px(0))), value_or_default(maybe_background_position, PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Left, Length::make_px(0)), EdgeStyleValue::create(PositionEdge::Top, Length::make_px(0)))),
value_or_default(maybe_background_size, IdentifierStyleValue::create(CSS::ValueID::Auto)), value_or_default(maybe_background_size, IdentifierStyleValue::create(CSS::ValueID::Auto)),
value_or_default(maybe_background_repeat, BackgroundRepeatStyleValue::create(CSS::Repeat::Repeat, CSS::Repeat::Repeat)), value_or_default(maybe_background_repeat, BackgroundRepeatStyleValue::create(CSS::Repeat::Repeat, CSS::Repeat::Repeat)),
value_or_default(maybe_background_attachment, IdentifierStyleValue::create(CSS::ValueID::Scroll)), value_or_default(maybe_background_attachment, IdentifierStyleValue::create(CSS::ValueID::Scroll)),

View file

@ -43,6 +43,7 @@
#include <LibWeb/CSS/StyleValues/NumericStyleValue.h> #include <LibWeb/CSS/StyleValues/NumericStyleValue.h>
#include <LibWeb/CSS/StyleValues/OverflowStyleValue.h> #include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h> #include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h> #include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/TextDecorationStyleValue.h> #include <LibWeb/CSS/StyleValues/TextDecorationStyleValue.h>
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h> #include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
@ -454,6 +455,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
return; return;
} }
if (property_id == CSS::PropertyID::BackgroundPosition) {
if (value.is_position()) {
auto const& position = value.as_position();
style.set_property(CSS::PropertyID::BackgroundPositionX, position.edge_x());
style.set_property(CSS::PropertyID::BackgroundPositionY, position.edge_y());
return;
}
style.set_property(CSS::PropertyID::BackgroundPositionX, value);
style.set_property(CSS::PropertyID::BackgroundPositionY, value);
return;
}
if (property_id == CSS::PropertyID::Margin) { if (property_id == CSS::PropertyID::Margin) {
if (value.is_value_list()) { if (value.is_value_list()) {
auto const& values_list = value.as_value_list(); auto const& values_list = value.as_value_list();

View file

@ -13,21 +13,7 @@ namespace Web::CSS {
ErrorOr<String> PositionStyleValue::to_string() const ErrorOr<String> PositionStyleValue::to_string() const
{ {
auto to_string = [](PositionEdge edge) { return String::formatted("{} {}", TRY(m_properties.edge_x->to_string()), TRY(m_properties.edge_y->to_string()));
switch (edge) {
case PositionEdge::Left:
return "left";
case PositionEdge::Right:
return "right";
case PositionEdge::Top:
return "top";
case PositionEdge::Bottom:
return "bottom";
}
VERIFY_NOT_REACHED();
};
return String::formatted("{} {} {} {}", to_string(m_properties.edge_x), TRY(m_properties.offset_x.to_string()), to_string(m_properties.edge_y), TRY(m_properties.offset_y.to_string()));
} }
} }

View file

@ -17,33 +17,29 @@ namespace Web::CSS {
class PositionStyleValue final : public StyleValueWithDefaultOperators<PositionStyleValue> { class PositionStyleValue final : public StyleValueWithDefaultOperators<PositionStyleValue> {
public: public:
static ValueComparingNonnullRefPtr<PositionStyleValue> create(PositionEdge edge_x, LengthPercentage const& offset_x, PositionEdge edge_y, LengthPercentage const& offset_y) static ValueComparingNonnullRefPtr<PositionStyleValue> create(ValueComparingNonnullRefPtr<StyleValue> egde_x, ValueComparingNonnullRefPtr<StyleValue> edge_y)
{ {
return adopt_ref(*new PositionStyleValue(edge_x, offset_x, edge_y, offset_y)); return adopt_ref(*new PositionStyleValue(move(egde_x), move(edge_y)));
} }
virtual ~PositionStyleValue() override = default; virtual ~PositionStyleValue() override = default;
PositionEdge edge_x() const { return m_properties.edge_x; } ValueComparingNonnullRefPtr<StyleValue> edge_x() const { return m_properties.edge_x; }
LengthPercentage const& offset_x() const { return m_properties.offset_x; } ValueComparingNonnullRefPtr<StyleValue> edge_y() const { return m_properties.edge_y; }
PositionEdge edge_y() const { return m_properties.edge_y; }
LengthPercentage const& offset_y() const { return m_properties.offset_y; }
virtual ErrorOr<String> to_string() const override; virtual ErrorOr<String> to_string() const override;
bool properties_equal(PositionStyleValue const& other) const { return m_properties == other.m_properties; } bool properties_equal(PositionStyleValue const& other) const { return m_properties == other.m_properties; }
private: private:
PositionStyleValue(PositionEdge edge_x, LengthPercentage const& offset_x, PositionEdge edge_y, LengthPercentage const& offset_y) PositionStyleValue(ValueComparingNonnullRefPtr<StyleValue> edge_x, ValueComparingNonnullRefPtr<StyleValue> edge_y)
: StyleValueWithDefaultOperators(Type::Position) : StyleValueWithDefaultOperators(Type::Position)
, m_properties { .edge_x = edge_x, .offset_x = offset_x, .edge_y = edge_y, .offset_y = offset_y } , m_properties { .edge_x = edge_x, .edge_y = edge_y }
{ {
} }
struct Properties { struct Properties {
PositionEdge edge_x; ValueComparingNonnullRefPtr<StyleValue> edge_x;
LengthPercentage offset_x; ValueComparingNonnullRefPtr<StyleValue> edge_y;
PositionEdge edge_y;
LengthPercentage offset_y;
bool operator==(Properties const&) const = default; bool operator==(Properties const&) const = default;
} m_properties; } m_properties;
}; };

View file

@ -9,7 +9,7 @@
#include <LibWeb/CSS/StyleValues/BackgroundRepeatStyleValue.h> #include <LibWeb/CSS/StyleValues/BackgroundRepeatStyleValue.h>
#include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.h> #include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.h>
#include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h> #include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h>
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h> #include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h> #include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/Dump.h> #include <LibWeb/Dump.h>
@ -293,7 +293,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
auto clips = computed_style.property(CSS::PropertyID::BackgroundClip); auto clips = computed_style.property(CSS::PropertyID::BackgroundClip);
auto images = computed_style.property(CSS::PropertyID::BackgroundImage); auto images = computed_style.property(CSS::PropertyID::BackgroundImage);
auto origins = computed_style.property(CSS::PropertyID::BackgroundOrigin); auto origins = computed_style.property(CSS::PropertyID::BackgroundOrigin);
auto positions = computed_style.property(CSS::PropertyID::BackgroundPosition); auto x_positions = computed_style.property(CSS::PropertyID::BackgroundPositionX);
auto y_positions = computed_style.property(CSS::PropertyID::BackgroundPositionY);
auto repeats = computed_style.property(CSS::PropertyID::BackgroundRepeat); auto repeats = computed_style.property(CSS::PropertyID::BackgroundRepeat);
auto sizes = computed_style.property(CSS::PropertyID::BackgroundSize); auto sizes = computed_style.property(CSS::PropertyID::BackgroundSize);
@ -315,7 +316,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
layer_count = max(layer_count, count_layers(clips)); layer_count = max(layer_count, count_layers(clips));
layer_count = max(layer_count, count_layers(images)); layer_count = max(layer_count, count_layers(images));
layer_count = max(layer_count, count_layers(origins)); layer_count = max(layer_count, count_layers(origins));
layer_count = max(layer_count, count_layers(positions)); layer_count = max(layer_count, count_layers(x_positions));
layer_count = max(layer_count, count_layers(y_positions));
layer_count = max(layer_count, count_layers(repeats)); layer_count = max(layer_count, count_layers(repeats));
layer_count = max(layer_count, count_layers(sizes)); layer_count = max(layer_count, count_layers(sizes));
@ -369,14 +371,18 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
layer.clip = as_box(clip_value->to_identifier()); layer.clip = as_box(clip_value->to_identifier());
} }
if (auto position_value = value_for_layer(positions, layer_index); position_value && position_value->is_position()) { if (auto position_value = value_for_layer(x_positions, layer_index); position_value && position_value->is_edge()) {
auto& position = position_value->as_position(); auto& position = position_value->as_edge();
layer.position_edge_x = position.edge_x(); layer.position_edge_x = position.edge();
layer.position_edge_y = position.edge_y(); layer.position_offset_x = position.offset();
layer.position_offset_x = position.offset_x();
layer.position_offset_y = position.offset_y();
} }
if (auto position_value = value_for_layer(y_positions, layer_index); position_value && position_value->is_edge()) {
auto& position = position_value->as_edge();
layer.position_edge_y = position.edge();
layer.position_offset_y = position.offset();
};
if (auto size_value = value_for_layer(sizes, layer_index); size_value) { if (auto size_value = value_for_layer(sizes, layer_index); size_value) {
if (size_value->is_background_size()) { if (size_value->is_background_size()) {
auto& size = size_value->as_background_size(); auto& size = size_value->as_background_size();