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:
parent
2a659693bc
commit
bed55ac669
Notes:
sideshowbarker
2024-07-17 05:13:53 +09:00
Author: https://github.com/MacDue Commit: https://github.com/SerenityOS/serenity/commit/bed55ac669 Pull-request: https://github.com/SerenityOS/serenity/pull/18157 Reviewed-by: https://github.com/AtkinsSJ
7 changed files with 135 additions and 67 deletions
|
@ -41,6 +41,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/FlexFlowStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
|
||||
|
@ -4270,6 +4271,33 @@ RefPtr<StyleValue> Parser::parse_background_value(Vector<ComponentValue> const&
|
|||
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)
|
||||
{
|
||||
// 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 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 {
|
||||
switch (identifier) {
|
||||
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 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()) {
|
||||
// Check if we're done
|
||||
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();
|
||||
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 (!horizontal.has_value()) {
|
||||
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);
|
||||
if (!maybe_value)
|
||||
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()) {
|
||||
offset_provided = true;
|
||||
tokens.next_token();
|
||||
|
@ -4384,11 +4388,11 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
|
|||
if (is_horizontal(identifier)) {
|
||||
bool offset_provided = false;
|
||||
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)) {
|
||||
bool offset_provided = false;
|
||||
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) {
|
||||
found_center = true;
|
||||
} else {
|
||||
|
@ -4436,8 +4440,63 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
|
|||
|
||||
transaction.commit();
|
||||
return PositionStyleValue::create(
|
||||
horizontal->edge, horizontal->offset,
|
||||
vertical->edge, vertical->offset);
|
||||
EdgeStyleValue::create(horizontal->edge, horizontal->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)
|
||||
|
@ -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); }))
|
||||
return parsed_value.release_nonnull();
|
||||
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:
|
||||
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();
|
||||
|
|
|
@ -294,6 +294,7 @@ private:
|
|||
RefPtr<StyleValue> parse_filter_value_list_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_x_or_y_value(TokenStream<ComponentValue>&, PropertyID);
|
||||
RefPtr<StyleValue> parse_single_background_repeat_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue> parse_single_background_size_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue> parse_border_value(Vector<ComponentValue> const&);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/BorderStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/GridAreaShorthandStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/GridTrackPlacementShorthandStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
|
||||
|
@ -195,7 +196,7 @@ RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(L
|
|||
return BackgroundStyleValue::create(
|
||||
value_or_default(maybe_background_color, InitialStyleValue::the()),
|
||||
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_repeat, BackgroundRepeatStyleValue::create(CSS::Repeat::Repeat, CSS::Repeat::Repeat)),
|
||||
value_or_default(maybe_background_attachment, IdentifierStyleValue::create(CSS::ValueID::Scroll)),
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/NumericStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextDecorationStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
|
||||
|
@ -454,6 +455,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
|||
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 (value.is_value_list()) {
|
||||
auto const& values_list = value.as_value_list();
|
||||
|
|
|
@ -13,21 +13,7 @@ namespace Web::CSS {
|
|||
|
||||
ErrorOr<String> PositionStyleValue::to_string() const
|
||||
{
|
||||
auto to_string = [](PositionEdge edge) {
|
||||
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()));
|
||||
return String::formatted("{} {}", TRY(m_properties.edge_x->to_string()), TRY(m_properties.edge_y->to_string()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,33 +17,29 @@ namespace Web::CSS {
|
|||
|
||||
class PositionStyleValue final : public StyleValueWithDefaultOperators<PositionStyleValue> {
|
||||
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;
|
||||
|
||||
PositionEdge edge_x() const { return m_properties.edge_x; }
|
||||
LengthPercentage const& offset_x() const { return m_properties.offset_x; }
|
||||
PositionEdge edge_y() const { return m_properties.edge_y; }
|
||||
LengthPercentage const& offset_y() const { return m_properties.offset_y; }
|
||||
ValueComparingNonnullRefPtr<StyleValue> edge_x() const { return m_properties.edge_x; }
|
||||
ValueComparingNonnullRefPtr<StyleValue> edge_y() const { return m_properties.edge_y; }
|
||||
|
||||
virtual ErrorOr<String> to_string() const override;
|
||||
|
||||
bool properties_equal(PositionStyleValue const& other) const { return m_properties == other.m_properties; }
|
||||
|
||||
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)
|
||||
, 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 {
|
||||
PositionEdge edge_x;
|
||||
LengthPercentage offset_x;
|
||||
PositionEdge edge_y;
|
||||
LengthPercentage offset_y;
|
||||
ValueComparingNonnullRefPtr<StyleValue> edge_x;
|
||||
ValueComparingNonnullRefPtr<StyleValue> edge_y;
|
||||
bool operator==(Properties const&) const = default;
|
||||
} m_properties;
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/BackgroundRepeatStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.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/DOM/Document.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 images = computed_style.property(CSS::PropertyID::BackgroundImage);
|
||||
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 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(images));
|
||||
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(sizes));
|
||||
|
||||
|
@ -369,14 +371,18 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
|
|||
layer.clip = as_box(clip_value->to_identifier());
|
||||
}
|
||||
|
||||
if (auto position_value = value_for_layer(positions, layer_index); position_value && position_value->is_position()) {
|
||||
auto& position = position_value->as_position();
|
||||
layer.position_edge_x = position.edge_x();
|
||||
layer.position_edge_y = position.edge_y();
|
||||
layer.position_offset_x = position.offset_x();
|
||||
layer.position_offset_y = position.offset_y();
|
||||
if (auto position_value = value_for_layer(x_positions, layer_index); position_value && position_value->is_edge()) {
|
||||
auto& position = position_value->as_edge();
|
||||
layer.position_edge_x = position.edge();
|
||||
layer.position_offset_x = position.offset();
|
||||
}
|
||||
|
||||
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 (size_value->is_background_size()) {
|
||||
auto& size = size_value->as_background_size();
|
||||
|
|
Loading…
Add table
Reference in a new issue