LibWeb: Properly serialize position/edge style values

This commit is contained in:
Gingeh 2024-11-29 22:44:14 +11:00 committed by Sam Atkins
parent 583ca6af89
commit 84150f972f
Notes: github-actions[bot] 2024-12-13 11:36:34 +00:00
21 changed files with 461 additions and 288 deletions

View file

@ -3913,12 +3913,6 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
} }
}; };
auto make_edge_style_value = [](PositionEdge position_edge, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> {
if (position_edge == PositionEdge::Center)
return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 });
return EdgeStyleValue::create(position_edge, Length::make_px(0));
};
// <position> = [ // <position> = [
// [ left | center | right | top | bottom | <length-percentage> ] // [ left | center | right | top | bottom | <length-percentage> ]
// | // |
@ -3945,21 +3939,21 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
// [ left | right ] // [ left | right ]
if (is_horizontal(edge, false)) if (is_horizontal(edge, false))
return PositionStyleValue::create(make_edge_style_value(edge, true), make_edge_style_value(PositionEdge::Center, false)); return PositionStyleValue::create(EdgeStyleValue::create(edge, {}), EdgeStyleValue::create(PositionEdge::Center, {}));
// [ top | bottom ] // [ top | bottom ]
if (is_vertical(edge, false)) if (is_vertical(edge, false))
return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(edge, false)); return PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Center, {}), EdgeStyleValue::create(edge, {}));
// [ center ] // [ center ]
VERIFY(edge == PositionEdge::Center); VERIFY(edge == PositionEdge::Center);
return PositionStyleValue::create(make_edge_style_value(PositionEdge::Center, true), make_edge_style_value(PositionEdge::Center, false)); return PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Center, {}), EdgeStyleValue::create(PositionEdge::Center, {}));
} }
// [ <length-percentage> ] // [ <length-percentage> ]
if (auto maybe_percentage = parse_length_percentage(token); maybe_percentage.has_value()) { if (auto maybe_percentage = parse_length_percentage(token); maybe_percentage.has_value()) {
transaction.commit(); transaction.commit();
return PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Left, *maybe_percentage), make_edge_style_value(PositionEdge::Center, false)); return PositionStyleValue::create(EdgeStyleValue::create({}, *maybe_percentage), EdgeStyleValue::create(PositionEdge::Center, {}));
} }
return nullptr; return nullptr;
@ -3994,7 +3988,7 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
// [ left | center | right ] [ top | bottom | center ] // [ left | center | right ] [ top | bottom | center ]
if (is_horizontal(first_edge, true) && is_vertical(second_edge, true)) { if (is_horizontal(first_edge, true) && is_vertical(second_edge, true)) {
transaction.commit(); transaction.commit();
return PositionStyleValue::create(make_edge_style_value(first_edge, true), make_edge_style_value(second_edge, false)); return PositionStyleValue::create(EdgeStyleValue::create(first_edge, {}), EdgeStyleValue::create(second_edge, {}));
} }
return nullptr; return nullptr;
@ -4014,14 +4008,14 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
bool valid = as_horizontal ? is_horizontal(position, true) : is_vertical(position, true); bool valid = as_horizontal ? is_horizontal(position, true) : is_vertical(position, true);
if (!valid) if (!valid)
return nullptr; return nullptr;
return make_edge_style_value(position, as_horizontal); return EdgeStyleValue::create(position, {});
} }
auto maybe_length = parse_length_percentage(token); auto maybe_length = parse_length_percentage(token);
if (!maybe_length.has_value()) if (!maybe_length.has_value())
return nullptr; return nullptr;
return EdgeStyleValue::create(as_horizontal ? PositionEdge::Left : PositionEdge::Top, maybe_length.release_value()); return EdgeStyleValue::create({}, maybe_length);
}; };
// [ left | center | right | <length-percentage> ] // [ left | center | right | <length-percentage> ]
@ -4157,15 +4151,15 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
if (!is_vertical(group2.position, true)) if (!is_vertical(group2.position, true))
return nullptr; return nullptr;
auto to_style_value = [&](PositionAndMaybeLength const& group, bool is_horizontal) -> NonnullRefPtr<EdgeStyleValue> { auto to_style_value = [&](PositionAndMaybeLength const& group) -> NonnullRefPtr<EdgeStyleValue> {
if (group.position == PositionEdge::Center) if (group.position == PositionEdge::Center)
return EdgeStyleValue::create(is_horizontal ? PositionEdge::Left : PositionEdge::Top, Percentage { 50 }); return EdgeStyleValue::create(PositionEdge::Center, {});
return EdgeStyleValue::create(group.position, group.length.value_or(Length::make_px(0))); return EdgeStyleValue::create(group.position, group.length);
}; };
transaction.commit(); transaction.commit();
return PositionStyleValue::create(to_style_value(group1, true), to_style_value(group2, false)); return PositionStyleValue::create(to_style_value(group1), to_style_value(group2));
}; };
// 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.
@ -4527,16 +4521,7 @@ static Optional<LengthPercentage> style_value_to_length_percentage(auto value)
RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>& tokens, PropertyID property) RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>& tokens, PropertyID property)
{ {
PositionEdge relative_edge {}; Optional<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(); auto transaction = tokens.begin_transaction();
if (!tokens.has_next_token()) if (!tokens.has_next_token())
@ -4550,10 +4535,10 @@ RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(Toke
auto keyword = value->to_keyword(); auto keyword = value->to_keyword();
if (keyword == Keyword::Center) { if (keyword == Keyword::Center) {
transaction.commit(); transaction.commit();
return EdgeStyleValue::create(relative_edge, Percentage { 50 }); return EdgeStyleValue::create(PositionEdge::Center, {});
} }
if (auto edge = keyword_to_position_edge(keyword); edge.has_value()) { if (auto edge = keyword_to_position_edge(keyword); edge.has_value()) {
relative_edge = *edge; relative_edge = edge;
} else { } else {
return nullptr; return nullptr;
} }
@ -4561,7 +4546,7 @@ RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(Toke
value = parse_css_value_for_property(property, tokens); value = parse_css_value_for_property(property, tokens);
if (!value) { if (!value) {
transaction.commit(); transaction.commit();
return EdgeStyleValue::create(relative_edge, Length::make_px(0)); return EdgeStyleValue::create(relative_edge, {});
} }
} }
} }
@ -4572,9 +4557,21 @@ RefPtr<CSSStyleValue> Parser::parse_single_background_position_x_or_y_value(Toke
return EdgeStyleValue::create(relative_edge, *offset); return EdgeStyleValue::create(relative_edge, *offset);
} }
if (!relative_edge.has_value()) {
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();
}
}
// If no offset is provided create this element but with an offset of default value of zero // If no offset is provided create this element but with an offset of default value of zero
transaction.commit(); transaction.commit();
return EdgeStyleValue::create(relative_edge, Length::make_px(0)); return EdgeStyleValue::create(relative_edge, {});
} }
RefPtr<CSSStyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens) RefPtr<CSSStyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens)

View file

@ -82,20 +82,6 @@ String ResolvedCSSStyleDeclaration::item(size_t index) const
return string_from_property_id(property_id).to_string(); return string_from_property_id(property_id).to_string();
} }
static NonnullRefPtr<CSSStyleValue const> style_value_for_background_property(Layout::NodeWithStyle const& layout_node, Function<NonnullRefPtr<CSSStyleValue const>(BackgroundLayerData const&)> callback, Function<NonnullRefPtr<CSSStyleValue const>()> default_value)
{
auto const& background_layers = layout_node.background_layers();
if (background_layers.is_empty())
return default_value();
if (background_layers.size() == 1)
return callback(background_layers.first());
StyleValueVector values;
values.ensure_capacity(background_layers.size());
for (auto const& layer : background_layers)
values.unchecked_append(callback(layer));
return StyleValueList::create(move(values), StyleValueList::Separator::Comma);
}
static NonnullRefPtr<CSSStyleValue const> style_value_for_length_percentage(LengthPercentage const& length_percentage) static NonnullRefPtr<CSSStyleValue const> style_value_for_length_percentage(LengthPercentage const& length_percentage)
{ {
if (length_percentage.is_auto()) if (length_percentage.is_auto())
@ -453,19 +439,6 @@ RefPtr<CSSStyleValue const> ResolvedCSSStyleDeclaration::style_value_for_propert
// NOTE: This is handled inside the `default` case. // NOTE: This is handled inside the `default` case.
// NOTE: Everything below is a shorthand that requires some manual construction. // NOTE: Everything below is a shorthand that requires some manual construction.
case PropertyID::BackgroundPosition:
return style_value_for_background_property(
layout_node,
[](auto& layer) -> NonnullRefPtr<CSSStyleValue> {
return PositionStyleValue::create(
EdgeStyleValue::create(layer.position_edge_x, layer.position_offset_x),
EdgeStyleValue::create(layer.position_edge_y, layer.position_offset_y));
},
[]() -> NonnullRefPtr<CSSStyleValue> {
return PositionStyleValue::create(
EdgeStyleValue::create(PositionEdge::Left, Percentage(0)),
EdgeStyleValue::create(PositionEdge::Top, Percentage(0)));
});
case PropertyID::Border: { case PropertyID::Border: {
auto width = style_value_for_property(layout_node, PropertyID::BorderWidth); auto width = style_value_for_property(layout_node, PropertyID::BorderWidth);
auto style = style_value_for_property(layout_node, PropertyID::BorderStyle); auto style = style_value_for_property(layout_node, PropertyID::BorderStyle);

View file

@ -1325,12 +1325,12 @@ CSS::ObjectPosition StyleProperties::object_position() const
auto const& edge_y = position.edge_y(); auto const& edge_y = position.edge_y();
if (edge_x->is_edge()) { if (edge_x->is_edge()) {
auto const& edge = edge_x->as_edge(); auto const& edge = edge_x->as_edge();
object_position.edge_x = edge.edge(); object_position.edge_x = edge.edge().value_or(PositionEdge::Left);
object_position.offset_x = edge.offset(); object_position.offset_x = edge.offset();
} }
if (edge_y->is_edge()) { if (edge_y->is_edge()) {
auto const& edge = edge_y->as_edge(); auto const& edge = edge_y->as_edge();
object_position.edge_y = edge.edge(); object_position.edge_y = edge.edge().value_or(PositionEdge::Top);
object_position.offset_y = edge.offset(); object_position.offset_y = edge.offset();
} }
return object_position; return object_position;

View file

@ -34,7 +34,7 @@ Gfx::Path Inset::to_path(CSSPixelRect reference_box, Layout::Node const& node) c
return path_from_resolved_rect(top, right, bottom, left); return path_from_resolved_rect(top, right, bottom, left);
} }
String Inset::to_string() const String Inset::to_string(CSSStyleValue::SerializationMode) const
{ {
return MUST(String::formatted("inset({} {} {} {})", inset_box.top(), inset_box.right(), inset_box.bottom(), inset_box.left())); return MUST(String::formatted("inset({} {} {} {})", inset_box.top(), inset_box.right(), inset_box.bottom(), inset_box.left()));
} }
@ -49,7 +49,7 @@ Gfx::Path Xywh::to_path(CSSPixelRect reference_box, Layout::Node const& node) co
return path_from_resolved_rect(top, right, bottom, left); return path_from_resolved_rect(top, right, bottom, left);
} }
String Xywh::to_string() const String Xywh::to_string(CSSStyleValue::SerializationMode) const
{ {
return MUST(String::formatted("xywh({} {} {} {})", x, y, width, height)); return MUST(String::formatted("xywh({} {} {} {})", x, y, width, height));
} }
@ -68,7 +68,7 @@ Gfx::Path Rect::to_path(CSSPixelRect reference_box, Layout::Node const& node) co
return path_from_resolved_rect(top, max(right, left), max(bottom, top), left); return path_from_resolved_rect(top, max(right, left), max(bottom, top), left);
} }
String Rect::to_string() const String Rect::to_string(CSSStyleValue::SerializationMode) const
{ {
return MUST(String::formatted("rect({} {} {} {})", box.top(), box.right(), box.bottom(), box.left())); return MUST(String::formatted("rect({} {} {} {})", box.top(), box.right(), box.bottom(), box.left()));
} }
@ -123,9 +123,9 @@ Gfx::Path Circle::to_path(CSSPixelRect reference_box, Layout::Node const& node)
return path; return path;
} }
String Circle::to_string() const String Circle::to_string(CSSStyleValue::SerializationMode mode) const
{ {
return MUST(String::formatted("circle({} at {})", radius_to_string(radius), position->to_string(CSSStyleValue::SerializationMode::Normal))); return MUST(String::formatted("circle({} at {})", radius_to_string(radius), position->to_string(mode)));
} }
Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node) const Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
@ -168,9 +168,9 @@ Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node)
return path; return path;
} }
String Ellipse::to_string() const String Ellipse::to_string(CSSStyleValue::SerializationMode mode) const
{ {
return MUST(String::formatted("ellipse({} {} at {})", radius_to_string(radius_x), radius_to_string(radius_y), position->to_string(CSSStyleValue::SerializationMode::Normal))); return MUST(String::formatted("ellipse({} {} at {})", radius_to_string(radius_x), radius_to_string(radius_y), position->to_string(mode)));
} }
Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node) const Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
@ -193,7 +193,7 @@ Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node)
return path; return path;
} }
String Polygon::to_string() const String Polygon::to_string(CSSStyleValue::SerializationMode) const
{ {
StringBuilder builder; StringBuilder builder;
builder.append("polygon("sv); builder.append("polygon("sv);
@ -220,10 +220,10 @@ Gfx::Path BasicShapeStyleValue::to_path(CSSPixelRect reference_box, Layout::Node
}); });
} }
String BasicShapeStyleValue::to_string(SerializationMode) const String BasicShapeStyleValue::to_string(SerializationMode mode) const
{ {
return m_basic_shape.visit([](auto const& shape) { return m_basic_shape.visit([mode](auto const& shape) {
return shape.to_string(); return shape.to_string(mode);
}); });
} }

View file

@ -17,7 +17,7 @@ namespace Web::CSS {
struct Inset { struct Inset {
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Inset const&) const = default; bool operator==(Inset const&) const = default;
@ -26,7 +26,7 @@ struct Inset {
struct Xywh { struct Xywh {
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Xywh const&) const = default; bool operator==(Xywh const&) const = default;
@ -38,7 +38,7 @@ struct Xywh {
struct Rect { struct Rect {
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Rect const&) const = default; bool operator==(Rect const&) const = default;
@ -54,7 +54,7 @@ using ShapeRadius = Variant<LengthPercentage, FitSide>;
struct Circle { struct Circle {
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Circle const&) const = default; bool operator==(Circle const&) const = default;
@ -64,7 +64,7 @@ struct Circle {
struct Ellipse { struct Ellipse {
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Ellipse const&) const = default; bool operator==(Ellipse const&) const = default;
@ -81,7 +81,7 @@ struct Polygon {
}; };
Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const; Gfx::Path to_path(CSSPixelRect reference_box, Layout::Node const&) const;
String to_string() const; String to_string(CSSStyleValue::SerializationMode) const;
bool operator==(Polygon const&) const = default; bool operator==(Polygon const&) const = default;

View file

@ -8,9 +8,40 @@
namespace Web::CSS { namespace Web::CSS {
String EdgeStyleValue::to_string(SerializationMode) const String EdgeStyleValue::to_string(SerializationMode mode) const
{ {
return MUST(String::formatted("{} {}", CSS::to_string(m_properties.edge), m_properties.offset.to_string())); if (mode == CSSStyleValue::SerializationMode::ResolvedValue) {
if (edge() == PositionEdge::Right || edge() == PositionEdge::Bottom) {
if (offset().is_percentage()) {
auto flipped_percentage = 100 - offset().percentage().value();
return Percentage(flipped_percentage).to_string();
}
Vector<NonnullOwnPtr<CalculationNode>> sum_parts;
sum_parts.append(NumericCalculationNode::create(Percentage(100)));
if (offset().is_length()) {
sum_parts.append(NegateCalculationNode::create(NumericCalculationNode::create(offset().length())));
} else {
// FIXME: Flip calculated offsets (convert CSSMathValue to CalculationNode, then negate and append)
return to_string(CSSStyleValue::SerializationMode::Normal);
}
auto flipped_absolute = CSSMathValue::create(SumCalculationNode::create(move(sum_parts)), CSSNumericType(CSSNumericType::BaseType::Length, 1));
return flipped_absolute->to_string(mode);
}
return offset().to_string();
}
StringBuilder builder;
if (m_properties.edge.has_value())
builder.append(CSS::to_string(m_properties.edge.value()));
if (m_properties.edge.has_value() && m_properties.offset.has_value())
builder.append(' ');
if (m_properties.offset.has_value())
builder.append(m_properties.offset->to_string());
return builder.to_string_without_validation();
} }
} }

View file

@ -14,31 +14,45 @@ namespace Web::CSS {
class EdgeStyleValue final : public StyleValueWithDefaultOperators<EdgeStyleValue> { class EdgeStyleValue final : public StyleValueWithDefaultOperators<EdgeStyleValue> {
public: public:
static ValueComparingNonnullRefPtr<EdgeStyleValue> create(PositionEdge edge, LengthPercentage const& offset) static ValueComparingNonnullRefPtr<EdgeStyleValue> create(Optional<PositionEdge> edge, Optional<LengthPercentage> const& offset)
{ {
VERIFY(edge != PositionEdge::Center);
return adopt_ref(*new (nothrow) EdgeStyleValue(edge, offset)); return adopt_ref(*new (nothrow) EdgeStyleValue(edge, offset));
} }
virtual ~EdgeStyleValue() override = default; virtual ~EdgeStyleValue() override = default;
// NOTE: `center` is converted to `left 50%` or `top 50%` in parsing, so is never returned here. Optional<PositionEdge> edge() const
PositionEdge edge() const { return m_properties.edge; } {
LengthPercentage const& offset() const { return m_properties.offset; } if (m_properties.edge == PositionEdge::Center)
return {};
return m_properties.edge;
}
LengthPercentage offset() const
{
if (m_properties.edge == PositionEdge::Center)
return Percentage(50);
if (!m_properties.offset.has_value())
return Percentage(0);
return m_properties.offset.value();
}
virtual String to_string(SerializationMode) const override; virtual String to_string(SerializationMode) const override;
bool properties_equal(EdgeStyleValue const& other) const { return m_properties == other.m_properties; } bool properties_equal(EdgeStyleValue const& other) const { return m_properties == other.m_properties; }
private: private:
EdgeStyleValue(PositionEdge edge, LengthPercentage const& offset) EdgeStyleValue(Optional<PositionEdge> edge, Optional<LengthPercentage> const& offset)
: StyleValueWithDefaultOperators(Type::Edge) : StyleValueWithDefaultOperators(Type::Edge)
, m_properties { .edge = edge, .offset = offset } , m_properties { .edge = edge, .offset = offset }
{ {
} }
struct Properties { struct Properties {
PositionEdge edge; Optional<PositionEdge> edge;
LengthPercentage offset; Optional<LengthPercentage> offset;
bool operator==(Properties const&) const = default; bool operator==(Properties const&) const = default;
} m_properties; } m_properties;
}; };

View file

@ -13,10 +13,8 @@ namespace Web::CSS {
bool PositionStyleValue::is_center() const bool PositionStyleValue::is_center() const
{ {
return (edge_x()->edge() == PositionEdge::Left return (edge_x()->offset().is_percentage() && edge_x()->offset().percentage() == Percentage { 50 })
&& edge_x()->offset().is_percentage() && edge_x()->offset().percentage() == Percentage { 50 }) && (edge_y()->offset().is_percentage() && edge_y()->offset().percentage() == Percentage { 50 });
&& (edge_y()->edge() == PositionEdge::Top
&& edge_y()->offset().is_percentage() && edge_y()->offset().percentage() == Percentage { 50 });
} }
CSSPixelPoint PositionStyleValue::resolved(Layout::Node const& node, CSSPixelRect const& rect) const CSSPixelPoint PositionStyleValue::resolved(Layout::Node const& node, CSSPixelRect const& rect) const

View file

@ -25,8 +25,8 @@ public:
static ValueComparingNonnullRefPtr<PositionStyleValue> create_center() static ValueComparingNonnullRefPtr<PositionStyleValue> create_center()
{ {
return adopt_ref(*new (nothrow) PositionStyleValue( return adopt_ref(*new (nothrow) PositionStyleValue(
EdgeStyleValue::create(PositionEdge::Left, Percentage { 50 }), EdgeStyleValue::create(PositionEdge::Center, {}),
EdgeStyleValue::create(PositionEdge::Top, Percentage { 50 }))); EdgeStyleValue::create(PositionEdge::Center, {})));
} }
virtual ~PositionStyleValue() override = default; virtual ~PositionStyleValue() override = default;

View file

@ -65,6 +65,8 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
auto color = longhand(PropertyID::BackgroundColor); auto color = longhand(PropertyID::BackgroundColor);
auto image = longhand(PropertyID::BackgroundImage); auto image = longhand(PropertyID::BackgroundImage);
auto position = longhand(PropertyID::BackgroundPosition); auto position = longhand(PropertyID::BackgroundPosition);
auto position_x = position->as_shorthand().longhand(PropertyID::BackgroundPositionX);
auto position_y = position->as_shorthand().longhand(PropertyID::BackgroundPositionY);
auto size = longhand(PropertyID::BackgroundSize); auto size = longhand(PropertyID::BackgroundSize);
auto repeat = longhand(PropertyID::BackgroundRepeat); auto repeat = longhand(PropertyID::BackgroundRepeat);
auto attachment = longhand(PropertyID::BackgroundAttachment); auto attachment = longhand(PropertyID::BackgroundAttachment);
@ -75,10 +77,10 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
return style_value->is_value_list() ? style_value->as_value_list().size() : 1; return style_value->is_value_list() ? style_value->as_value_list().size() : 1;
}; };
auto layer_count = max(get_layer_count(image), max(get_layer_count(position), max(get_layer_count(size), max(get_layer_count(repeat), max(get_layer_count(attachment), max(get_layer_count(origin), get_layer_count(clip))))))); auto layer_count = max(get_layer_count(image), max(get_layer_count(position_x), max(get_layer_count(position_y), max(get_layer_count(size), max(get_layer_count(repeat), max(get_layer_count(attachment), max(get_layer_count(origin), get_layer_count(clip))))))));
if (layer_count == 1) { if (layer_count == 1) {
return MUST(String::formatted("{} {} {} {} {} {} {} {}", color->to_string(mode), image->to_string(mode), position->to_string(mode), size->to_string(mode), repeat->to_string(mode), attachment->to_string(mode), origin->to_string(mode), clip->to_string(mode))); return MUST(String::formatted("{} {} {} {} {} {} {} {} {}", color->to_string(mode), image->to_string(mode), position_x->to_string(mode), position_y->to_string(mode), size->to_string(mode), repeat->to_string(mode), attachment->to_string(mode), origin->to_string(mode), clip->to_string(mode)));
} }
auto get_layer_value_string = [mode](ValueComparingRefPtr<CSSStyleValue const> const& style_value, size_t index) { auto get_layer_value_string = [mode](ValueComparingRefPtr<CSSStyleValue const> const& style_value, size_t index) {
@ -93,7 +95,38 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
builder.append(", "sv); builder.append(", "sv);
if (i == layer_count - 1) if (i == layer_count - 1)
builder.appendff("{} ", color->to_string(mode)); builder.appendff("{} ", color->to_string(mode));
builder.appendff("{} {} {} {} {} {} {}", get_layer_value_string(image, i), get_layer_value_string(position, i), get_layer_value_string(size, i), get_layer_value_string(repeat, i), get_layer_value_string(attachment, i), get_layer_value_string(origin, i), get_layer_value_string(clip, i)); builder.appendff("{} {} {} {} {} {} {} {}", get_layer_value_string(image, i), get_layer_value_string(position_x, i), get_layer_value_string(position_y, i), get_layer_value_string(size, i), get_layer_value_string(repeat, i), get_layer_value_string(attachment, i), get_layer_value_string(origin, i), get_layer_value_string(clip, i));
}
return MUST(builder.to_string());
}
case Web::CSS::PropertyID::BackgroundPosition: {
auto x_edges = longhand(PropertyID::BackgroundPositionX);
auto y_edges = longhand(PropertyID::BackgroundPositionY);
auto get_layer_count = [](auto style_value) -> size_t {
return style_value->is_value_list() ? style_value->as_value_list().size() : 1;
};
// FIXME: The spec is unclear about how differing layer counts should be handled
auto layer_count = max(get_layer_count(x_edges), get_layer_count(y_edges));
if (layer_count == 1) {
return MUST(String::formatted("{} {}", x_edges->to_string(mode), y_edges->to_string(mode)));
}
auto get_layer_value_string = [mode](ValueComparingRefPtr<CSSStyleValue const> const& style_value, size_t index) {
if (style_value->is_value_list())
return style_value->as_value_list().value_at(index, true)->to_string(mode);
return style_value->to_string(mode);
};
StringBuilder builder;
for (size_t i = 0; i < layer_count; i++) {
if (i)
builder.append(", "sv);
builder.appendff("{} {}", get_layer_value_string(x_edges, i), get_layer_value_string(y_edges, i));
} }
return MUST(builder.to_string()); return MUST(builder.to_string());

View file

@ -428,13 +428,13 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
if (auto position_value = value_for_layer(x_positions, layer_index); position_value && position_value->is_edge()) { if (auto position_value = value_for_layer(x_positions, layer_index); position_value && position_value->is_edge()) {
auto& position = position_value->as_edge(); auto& position = position_value->as_edge();
layer.position_edge_x = position.edge(); layer.position_edge_x = position.edge().value_or(CSS::PositionEdge::Left);
layer.position_offset_x = position.offset(); layer.position_offset_x = position.offset();
} }
if (auto position_value = value_for_layer(y_positions, layer_index); position_value && position_value->is_edge()) { if (auto position_value = value_for_layer(y_positions, layer_index); position_value && position_value->is_edge()) {
auto& position = position_value->as_edge(); auto& position = position_value->as_edge();
layer.position_edge_y = position.edge(); layer.position_edge_y = position.edge().value_or(CSS::PositionEdge::Top);
layer.position_offset_y = position.offset(); layer.position_offset_y = position.offset();
}; };

View file

@ -1,4 +1,4 @@
style.cssText = background-color: yellow; background-image: none; background-position-x: left 0%; background-position-y: top 0%; background-size: auto auto; background-repeat: repeat; background-attachment: scroll; background-origin: padding-box; background-clip: border-box; style.cssText = background-color: yellow; background-image: none; background-position-x: 0%; background-position-y: 0%; background-size: auto auto; background-repeat: repeat; background-attachment: scroll; background-origin: padding-box; background-clip: border-box;
style.length = 9 style.length = 9
style[] = style[] =
1. background-color 1. background-color

View file

@ -1,2 +1,2 @@
right 0px 100%
bottom 0px 100%

View file

@ -159,7 +159,7 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'aspect-ratio': 'auto' 'aspect-ratio': 'auto'
'backdropFilter': 'none' 'backdropFilter': 'none'
'backdrop-filter': 'none' 'backdrop-filter': 'none'
'background': 'rgba(0, 0, 0, 0) none left 0% top 0% auto auto repeat scroll padding-box border-box' 'background': 'rgba(0, 0, 0, 0) none 0% 0% auto auto repeat scroll padding-box border-box'
'backgroundAttachment': 'scroll' 'backgroundAttachment': 'scroll'
'background-attachment': 'scroll' 'background-attachment': 'scroll'
'backgroundClip': 'border-box' 'backgroundClip': 'border-box'
@ -170,12 +170,12 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'background-image': 'none' 'background-image': 'none'
'backgroundOrigin': 'padding-box' 'backgroundOrigin': 'padding-box'
'background-origin': 'padding-box' 'background-origin': 'padding-box'
'backgroundPosition': 'left 0% top 0%' 'backgroundPosition': '0% 0%'
'background-position': 'left 0% top 0%' 'background-position': '0% 0%'
'backgroundPositionX': 'left 0%' 'backgroundPositionX': '0%'
'background-position-x': 'left 0%' 'background-position-x': '0%'
'backgroundPositionY': 'top 0%' 'backgroundPositionY': '0%'
'background-position-y': 'top 0%' 'background-position-y': '0%'
'backgroundRepeat': 'repeat' 'backgroundRepeat': 'repeat'
'background-repeat': 'repeat' 'background-repeat': 'repeat'
'backgroundSize': 'auto auto' 'backgroundSize': 'auto auto'
@ -430,8 +430,8 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'min-width': 'auto' 'min-width': 'auto'
'objectFit': 'fill' 'objectFit': 'fill'
'object-fit': 'fill' 'object-fit': 'fill'
'objectPosition': 'left 50% top 50%' 'objectPosition': '50% 50%'
'object-position': 'left 50% top 50%' 'object-position': '50% 50%'
'opacity': '1' 'opacity': '1'
'order': '0' 'order': '0'
'outline': 'rgb(0, 0, 0) none medium' 'outline': 'rgb(0, 0, 0) none medium'

View file

@ -8,10 +8,10 @@ backdrop-filter: 'grayscale(calc(2%))' -> 'grayscale(calc(2%))'
backdrop-filter: 'grayscale(calc(2% * var(--n)))' -> 'grayscale(calc(2% * 2))' backdrop-filter: 'grayscale(calc(2% * var(--n)))' -> 'grayscale(calc(2% * 2))'
backdrop-filter: 'grayscale(calc(0.02))' -> 'grayscale(calc(0.02))' backdrop-filter: 'grayscale(calc(0.02))' -> 'grayscale(calc(0.02))'
backdrop-filter: 'grayscale(calc(0.02 * var(--n)))' -> 'grayscale(calc(0.02 * 2))' backdrop-filter: 'grayscale(calc(0.02 * var(--n)))' -> 'grayscale(calc(0.02 * 2))'
background-position-x: 'calc(2px)' -> 'left calc(2px)' background-position-x: 'calc(2px)' -> 'calc(2px)'
background-position-x: 'calc(2px * var(--n))' -> 'left calc(2px * 2)' background-position-x: 'calc(2px * var(--n))' -> 'calc(2px * 2)'
background-position-y: 'calc(2%)' -> 'top calc(2%)' background-position-y: 'calc(2%)' -> 'calc(2%)'
background-position-y: 'calc(2% * var(--n))' -> 'top 4%' background-position-y: 'calc(2% * var(--n))' -> '4%'
background-size: 'calc(2px * var(--n)) calc(2%)' -> 'calc(2px * 2) 2%' background-size: 'calc(2px * var(--n)) calc(2%)' -> 'calc(2px * 2) 2%'
background-size: 'calc(2px * var(--n)) calc(2% * var(--n))' -> 'calc(2px * 2) 4%' background-size: 'calc(2px * var(--n)) calc(2% * var(--n))' -> 'calc(2px * 2) 4%'
border-bottom-left-radius: 'calc(2px)' -> 'calc(2px)' border-bottom-left-radius: 'calc(2px)' -> 'calc(2px)'

View file

@ -71,8 +71,8 @@ background-clip: border-box
background-color: rgba(0, 0, 0, 0) background-color: rgba(0, 0, 0, 0)
background-image: none background-image: none
background-origin: padding-box background-origin: padding-box
background-position-x: left 0% background-position-x: 0%
background-position-y: top 0% background-position-y: 0%
background-repeat: repeat background-repeat: repeat
background-size: auto auto background-size: auto auto
border-bottom-color: rgb(0, 0, 0) border-bottom-color: rgb(0, 0, 0)
@ -154,7 +154,7 @@ min-height: auto
min-inline-size: 0px min-inline-size: 0px
min-width: auto min-width: auto
object-fit: fill object-fit: fill
object-position: left 50% top 50% object-position: 50% 50%
opacity: 1 opacity: 1
order: 0 order: 0
outline-color: rgb(0, 0, 0) outline-color: rgb(0, 0, 0)

View file

@ -0,0 +1,18 @@
inline: center center
computed: 50% 50%
inline: left bottom
computed: 0% 100%
inline: center top
computed: 50% 0%
inline: center top 20%
computed: 50% 20%
inline: left 10px top 20%
computed: 10px 20%
inline: 10px top
computed: 10px 0%
inline: right 10px bottom 20%
computed: calc(100% + (0 - 10px)) 80%
inline: center center, left bottom
computed: 50% 50%, 0% 100%
inline: left 10px bottom 20%, right 10px top 20%
computed: 10px 80%, calc(100% + (0 - 10px)) 20%

View file

@ -0,0 +1,36 @@
Harness status: OK
Found 31 tests
31 Pass
Pass e.style['background-position'] = "1px" should set the property value
Pass e.style['background-position'] = "1px center" should set the property value
Pass e.style['background-position'] = "-2% -3%" should set the property value
Pass e.style['background-position'] = "5% top" should set the property value
Pass e.style['background-position'] = "center" should set the property value
Pass e.style['background-position'] = "center center" should set the property value
Pass e.style['background-position'] = "center 6px" should set the property value
Pass e.style['background-position'] = "center left" should set the property value
Pass e.style['background-position'] = "center right 7%" should set the property value
Pass e.style['background-position'] = "center bottom" should set the property value
Pass e.style['background-position'] = "center top 8px" should set the property value
Pass e.style['background-position'] = "left" should set the property value
Pass e.style['background-position'] = "right 9%" should set the property value
Pass e.style['background-position'] = "left 10px center" should set the property value
Pass e.style['background-position'] = "right 11% bottom" should set the property value
Pass e.style['background-position'] = "left 12px top 13px" should set the property value
Pass e.style['background-position'] = "right center" should set the property value
Pass e.style['background-position'] = "left bottom" should set the property value
Pass e.style['background-position'] = "right top 14%" should set the property value
Pass e.style['background-position'] = "bottom" should set the property value
Pass e.style['background-position'] = "top 15px center" should set the property value
Pass e.style['background-position'] = "bottom 16% left" should set the property value
Pass e.style['background-position'] = "top 17px right 18px" should set the property value
Pass e.style['background-position'] = "bottom center" should set the property value
Pass e.style['background-position'] = "top left" should set the property value
Pass e.style['background-position'] = "bottom right 19%" should set the property value
Pass e.style['background-position'] = "20% 0%" should set the property value
Pass e.style['background-position'] = "0% 0%" should set the property value
Pass e.style['background-position'] = "0%" should set the property value
Pass e.style['background-position'] = "0% center" should set the property value
Pass e.style['background-position'] = "center 0%" should set the property value

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 687 tests Found 687 tests
485 Pass 654 Pass
202 Fail 33 Fail
Pass background-attachment: scroll Pass background-attachment: scroll
Pass background-attachment: fixed Pass background-attachment: fixed
Pass background-attachment: inherit Pass background-attachment: inherit
@ -17,175 +17,175 @@ Pass background-image: url("http://localhost/")
Pass background-image: url(http://localhost/) Pass background-image: url(http://localhost/)
Pass background-image: none Pass background-image: none
Pass background-image: inherit Pass background-image: inherit
Fail background-position: 5% 5% Pass background-position: 5% 5%
Fail background-position: 5% .5% Pass background-position: 5% .5%
Fail background-position: 5% -5% Pass background-position: 5% -5%
Fail background-position: 5% -.5% Pass background-position: 5% -.5%
Fail background-position: 5% 0px Pass background-position: 5% 0px
Fail background-position: 5% 1px Pass background-position: 5% 1px
Fail background-position: 5% .1em Pass background-position: 5% .1em
Fail background-position: 5% -0px Pass background-position: 5% -0px
Fail background-position: 5% -1px Pass background-position: 5% -1px
Fail background-position: 5% -.1em Pass background-position: 5% -.1em
Fail background-position: 5% top Pass background-position: 5% top
Fail background-position: 5% center Pass background-position: 5% center
Fail background-position: 5% bottom Pass background-position: 5% bottom
Fail background-position: .5% 5% Pass background-position: .5% 5%
Fail background-position: .5% .5% Pass background-position: .5% .5%
Fail background-position: .5% -5% Pass background-position: .5% -5%
Fail background-position: .5% -.5% Pass background-position: .5% -.5%
Fail background-position: .5% 0px Pass background-position: .5% 0px
Fail background-position: .5% 1px Pass background-position: .5% 1px
Fail background-position: .5% .1em Pass background-position: .5% .1em
Fail background-position: .5% -0px Pass background-position: .5% -0px
Fail background-position: .5% -1px Pass background-position: .5% -1px
Fail background-position: .5% -.1em Pass background-position: .5% -.1em
Fail background-position: .5% top Pass background-position: .5% top
Fail background-position: .5% center Pass background-position: .5% center
Fail background-position: .5% bottom Pass background-position: .5% bottom
Fail background-position: -5% 5% Pass background-position: -5% 5%
Fail background-position: -5% .5% Pass background-position: -5% .5%
Fail background-position: -5% -5% Pass background-position: -5% -5%
Fail background-position: -5% -.5% Pass background-position: -5% -.5%
Fail background-position: -5% 0px Pass background-position: -5% 0px
Fail background-position: -5% 1px Pass background-position: -5% 1px
Fail background-position: -5% .1em Pass background-position: -5% .1em
Fail background-position: -5% -0px Pass background-position: -5% -0px
Fail background-position: -5% -1px Pass background-position: -5% -1px
Fail background-position: -5% -.1em Pass background-position: -5% -.1em
Fail background-position: -5% top Pass background-position: -5% top
Fail background-position: -5% center Pass background-position: -5% center
Fail background-position: -5% bottom Pass background-position: -5% bottom
Fail background-position: -.5% 5% Pass background-position: -.5% 5%
Fail background-position: -.5% .5% Pass background-position: -.5% .5%
Fail background-position: -.5% -5% Pass background-position: -.5% -5%
Fail background-position: -.5% -.5% Pass background-position: -.5% -.5%
Fail background-position: -.5% 0px Pass background-position: -.5% 0px
Fail background-position: -.5% 1px Pass background-position: -.5% 1px
Fail background-position: -.5% .1em Pass background-position: -.5% .1em
Fail background-position: -.5% -0px Pass background-position: -.5% -0px
Fail background-position: -.5% -1px Pass background-position: -.5% -1px
Fail background-position: -.5% -.1em Pass background-position: -.5% -.1em
Fail background-position: -.5% top Pass background-position: -.5% top
Fail background-position: -.5% center Pass background-position: -.5% center
Fail background-position: -.5% bottom Pass background-position: -.5% bottom
Fail background-position: 0px 5% Pass background-position: 0px 5%
Fail background-position: 0px .5% Pass background-position: 0px .5%
Fail background-position: 0px -5% Pass background-position: 0px -5%
Fail background-position: 0px -.5% Pass background-position: 0px -.5%
Fail background-position: 0px 0px Pass background-position: 0px 0px
Fail background-position: 0px 1px Pass background-position: 0px 1px
Fail background-position: 0px .1em Pass background-position: 0px .1em
Fail background-position: 0px -0px Pass background-position: 0px -0px
Fail background-position: 0px -1px Pass background-position: 0px -1px
Fail background-position: 0px -.1em Pass background-position: 0px -.1em
Fail background-position: 0px top Pass background-position: 0px top
Fail background-position: 0px center Pass background-position: 0px center
Fail background-position: 0px bottom Pass background-position: 0px bottom
Fail background-position: 1px 5% Pass background-position: 1px 5%
Fail background-position: 1px .5% Pass background-position: 1px .5%
Fail background-position: 1px -5% Pass background-position: 1px -5%
Fail background-position: 1px -.5% Pass background-position: 1px -.5%
Fail background-position: 1px 0px Pass background-position: 1px 0px
Fail background-position: 1px 1px Pass background-position: 1px 1px
Fail background-position: 1px .1em Pass background-position: 1px .1em
Fail background-position: 1px -0px Pass background-position: 1px -0px
Fail background-position: 1px -1px Pass background-position: 1px -1px
Fail background-position: 1px -.1em Pass background-position: 1px -.1em
Fail background-position: 1px top Pass background-position: 1px top
Fail background-position: 1px center Pass background-position: 1px center
Fail background-position: 1px bottom Pass background-position: 1px bottom
Fail background-position: .1em 5% Pass background-position: .1em 5%
Fail background-position: .1em .5% Pass background-position: .1em .5%
Fail background-position: .1em -5% Pass background-position: .1em -5%
Fail background-position: .1em -.5% Pass background-position: .1em -.5%
Fail background-position: .1em 0px Pass background-position: .1em 0px
Fail background-position: .1em 1px Pass background-position: .1em 1px
Fail background-position: .1em .1em Pass background-position: .1em .1em
Fail background-position: .1em -0px Pass background-position: .1em -0px
Fail background-position: .1em -1px Pass background-position: .1em -1px
Fail background-position: .1em -.1em Pass background-position: .1em -.1em
Fail background-position: .1em top Pass background-position: .1em top
Fail background-position: .1em center Pass background-position: .1em center
Fail background-position: .1em bottom Pass background-position: .1em bottom
Fail background-position: -0px 5% Pass background-position: -0px 5%
Fail background-position: -0px .5% Pass background-position: -0px .5%
Fail background-position: -0px -5% Pass background-position: -0px -5%
Fail background-position: -0px -.5% Pass background-position: -0px -.5%
Fail background-position: -0px 0px Pass background-position: -0px 0px
Fail background-position: -0px 1px Pass background-position: -0px 1px
Fail background-position: -0px .1em Pass background-position: -0px .1em
Fail background-position: -0px -0px Pass background-position: -0px -0px
Fail background-position: -0px -1px Pass background-position: -0px -1px
Fail background-position: -0px -.1em Pass background-position: -0px -.1em
Fail background-position: -0px top Pass background-position: -0px top
Fail background-position: -0px center Pass background-position: -0px center
Fail background-position: -0px bottom Pass background-position: -0px bottom
Fail background-position: -1px 5% Pass background-position: -1px 5%
Fail background-position: -1px .5% Pass background-position: -1px .5%
Fail background-position: -1px -5% Pass background-position: -1px -5%
Fail background-position: -1px -.5% Pass background-position: -1px -.5%
Fail background-position: -1px 0px Pass background-position: -1px 0px
Fail background-position: -1px 1px Pass background-position: -1px 1px
Fail background-position: -1px .1em Pass background-position: -1px .1em
Fail background-position: -1px -0px Pass background-position: -1px -0px
Fail background-position: -1px -1px Pass background-position: -1px -1px
Fail background-position: -1px -.1em Pass background-position: -1px -.1em
Fail background-position: -1px top Pass background-position: -1px top
Fail background-position: -1px center Pass background-position: -1px center
Fail background-position: -1px bottom Pass background-position: -1px bottom
Fail background-position: -.1em 5% Pass background-position: -.1em 5%
Fail background-position: -.1em .5% Pass background-position: -.1em .5%
Fail background-position: -.1em -5% Pass background-position: -.1em -5%
Fail background-position: -.1em -.5% Pass background-position: -.1em -.5%
Fail background-position: -.1em 0px Pass background-position: -.1em 0px
Fail background-position: -.1em 1px Pass background-position: -.1em 1px
Fail background-position: -.1em .1em Pass background-position: -.1em .1em
Fail background-position: -.1em -0px Pass background-position: -.1em -0px
Fail background-position: -.1em -1px Pass background-position: -.1em -1px
Fail background-position: -.1em -.1em Pass background-position: -.1em -.1em
Fail background-position: -.1em top Pass background-position: -.1em top
Fail background-position: -.1em center Pass background-position: -.1em center
Fail background-position: -.1em bottom Pass background-position: -.1em bottom
Fail background-position: left 5% Pass background-position: left 5%
Fail background-position: left .5% Pass background-position: left .5%
Fail background-position: left -5% Pass background-position: left -5%
Fail background-position: left -.5% Pass background-position: left -.5%
Fail background-position: left 0px Pass background-position: left 0px
Fail background-position: left 1px Pass background-position: left 1px
Fail background-position: left .1em Pass background-position: left .1em
Fail background-position: left -0px Pass background-position: left -0px
Fail background-position: left -1px Pass background-position: left -1px
Fail background-position: left -.1em Pass background-position: left -.1em
Fail background-position: left top Pass background-position: left top
Fail background-position: left center Pass background-position: left center
Fail background-position: left bottom Pass background-position: left bottom
Fail background-position: center 5% Pass background-position: center 5%
Fail background-position: center .5% Pass background-position: center .5%
Fail background-position: center -5% Pass background-position: center -5%
Fail background-position: center -.5% Pass background-position: center -.5%
Fail background-position: center 0px Pass background-position: center 0px
Fail background-position: center 1px Pass background-position: center 1px
Fail background-position: center .1em Pass background-position: center .1em
Fail background-position: center -0px Pass background-position: center -0px
Fail background-position: center -1px Pass background-position: center -1px
Fail background-position: center -.1em Pass background-position: center -.1em
Fail background-position: center top Pass background-position: center top
Fail background-position: center center Pass background-position: center center
Fail background-position: center bottom Pass background-position: center bottom
Fail background-position: right 5% Pass background-position: right 5%
Fail background-position: right .5% Pass background-position: right .5%
Fail background-position: right -5% Pass background-position: right -5%
Fail background-position: right -.5% Pass background-position: right -.5%
Fail background-position: right 0px Pass background-position: right 0px
Fail background-position: right 1px Pass background-position: right 1px
Fail background-position: right .1em Pass background-position: right .1em
Fail background-position: right -0px Pass background-position: right -0px
Fail background-position: right -1px Pass background-position: right -1px
Fail background-position: right -.1em Pass background-position: right -.1em
Fail background-position: right top Pass background-position: right top
Fail background-position: right center Pass background-position: right center
Fail background-position: right bottom Pass background-position: right bottom
Pass background-position: inherit Pass background-position: inherit
Pass background-repeat: repeat Pass background-repeat: repeat
Pass background-repeat: repeat-x Pass background-repeat: repeat-x

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<script src="include.js"></script>
<div id="target"></div>
<script>
test(() => {
const values = [
"center",
"left bottom",
"center top",
"center top 20%",
"left 10px top 20%",
"10px top",
"right 10px bottom 20%",
"center, left bottom",
"left 10px bottom 20%, right 10px top 20%"
];
const target = document.getElementById('target');
for (const value of values) {
target.style.setProperty("background-position", value);
println("inline: " + target.style.getPropertyValue("background-position"));
println("computed: " + getComputedStyle(target).getPropertyValue("background-position"));
}
});
</script>

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Backgrounds and Borders Module Level 3: parsing background-position with valid values</title>
<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#background-position">
<meta name="assert" content="background-position supports the full grammar '<bg-position>'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_valid_value("background-position", "1px", ["1px", "1px center"]);
test_valid_value("background-position", "1px center", ["1px", "1px center"]);
test_valid_value("background-position", "-2% -3%");
test_valid_value("background-position", "5% top");
test_valid_value("background-position", "center", ["center", "center center"]);
test_valid_value("background-position", "center center", ["center", "center center"]);
test_valid_value("background-position", "center 6px");
test_valid_value("background-position", "center left", ["left center", "left"]);
test_valid_value("background-position", "center right 7%", "right 7% center");
test_valid_value("background-position", "center bottom", ["center bottom", "bottom"]);
test_valid_value("background-position", "center top 8px", ["center top 8px", "center 8px"]);
test_valid_value("background-position", "left", ["left center", "left"]);
test_valid_value("background-position", "right 9%");
test_valid_value("background-position", "left 10px center", ["left 10px center", "10px"]);
test_valid_value("background-position", "right 11% bottom", ["right 11% bottom", "right 11% bottom 0%"]); // "right 11% bottom 0%" in Edge
test_valid_value("background-position", "left 12px top 13px", ["left 12px top 13px", "12px 13px"]);
test_valid_value("background-position", "right center", ["right center", "right"]);
test_valid_value("background-position", "left bottom");
test_valid_value("background-position", "right top 14%", ["right top 14%", "right 14%"]);
test_valid_value("background-position", "bottom", ["center bottom", "bottom"]);
test_valid_value("background-position", "top 15px center", ["center top 15px", "center 15px"]);
test_valid_value("background-position", "bottom 16% left", ["left bottom 16%", "left 0% bottom 16%"]); // "left 0% bottom 16%" in Edge
test_valid_value("background-position", "top 17px right 18px", "right 18px top 17px");
test_valid_value("background-position", "bottom center", ["center bottom", "bottom"]);
test_valid_value("background-position", "top left", "left top");
test_valid_value("background-position", "bottom right 19%", ["right 19% bottom", "right 19% bottom 0%"]); // "right 19% bottom 0%" in Edge
test_valid_value("background-position", "20% 0%");
test_valid_value("background-position", "0% 0%");
test_valid_value("background-position", "0%", ["0%", "0% center"]);
test_valid_value("background-position", "0% center", ["0%", "0% center"]);
test_valid_value("background-position", "center 0%");
</script>
</body>
</html>