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> = [
// [ left | center | right | top | bottom | <length-percentage> ]
// |
@ -3945,21 +3939,21 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
// [ left | right ]
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 ]
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 ]
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> ]
if (auto maybe_percentage = parse_length_percentage(token); maybe_percentage.has_value()) {
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;
@ -3994,7 +3988,7 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
// [ left | center | right ] [ top | bottom | center ]
if (is_horizontal(first_edge, true) && is_vertical(second_edge, true)) {
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;
@ -4014,14 +4008,14 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
bool valid = as_horizontal ? is_horizontal(position, true) : is_vertical(position, true);
if (!valid)
return nullptr;
return make_edge_style_value(position, as_horizontal);
return EdgeStyleValue::create(position, {});
}
auto maybe_length = parse_length_percentage(token);
if (!maybe_length.has_value())
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> ]
@ -4157,15 +4151,15 @@ RefPtr<PositionStyleValue> Parser::parse_position_value(TokenStream<ComponentVal
if (!is_vertical(group2.position, true))
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)
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();
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.
@ -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)
{
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();
}
Optional<PositionEdge> relative_edge {};
auto transaction = tokens.begin_transaction();
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();
if (keyword == Keyword::Center) {
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()) {
relative_edge = *edge;
relative_edge = edge;
} else {
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);
if (!value) {
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);
}
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
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)

View file

@ -82,20 +82,6 @@ String ResolvedCSSStyleDeclaration::item(size_t index) const
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)
{
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: 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: {
auto width = style_value_for_property(layout_node, PropertyID::BorderWidth);
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();
if (edge_x->is_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();
}
if (edge_y->is_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();
}
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);
}
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()));
}
@ -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);
}
String Xywh::to_string() const
String Xywh::to_string(CSSStyleValue::SerializationMode) const
{
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);
}
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()));
}
@ -123,9 +123,9 @@ Gfx::Path Circle::to_path(CSSPixelRect reference_box, Layout::Node const& node)
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
@ -168,9 +168,9 @@ Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node)
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
@ -193,7 +193,7 @@ Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node)
return path;
}
String Polygon::to_string() const
String Polygon::to_string(CSSStyleValue::SerializationMode) const
{
StringBuilder builder;
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 shape.to_string();
return m_basic_shape.visit([mode](auto const& shape) {
return shape.to_string(mode);
});
}

View file

@ -17,7 +17,7 @@ namespace Web::CSS {
struct Inset {
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;
@ -26,7 +26,7 @@ struct Inset {
struct Xywh {
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;
@ -38,7 +38,7 @@ struct Xywh {
struct Rect {
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;
@ -54,7 +54,7 @@ using ShapeRadius = Variant<LengthPercentage, FitSide>;
struct Circle {
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;
@ -64,7 +64,7 @@ struct Circle {
struct Ellipse {
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;
@ -81,7 +81,7 @@ struct Polygon {
};
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;

View file

@ -8,9 +8,40 @@
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> {
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));
}
virtual ~EdgeStyleValue() override = default;
// NOTE: `center` is converted to `left 50%` or `top 50%` in parsing, so is never returned here.
PositionEdge edge() const { return m_properties.edge; }
LengthPercentage const& offset() const { return m_properties.offset; }
Optional<PositionEdge> edge() const
{
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;
bool properties_equal(EdgeStyleValue const& other) const { return m_properties == other.m_properties; }
private:
EdgeStyleValue(PositionEdge edge, LengthPercentage const& offset)
EdgeStyleValue(Optional<PositionEdge> edge, Optional<LengthPercentage> const& offset)
: StyleValueWithDefaultOperators(Type::Edge)
, m_properties { .edge = edge, .offset = offset }
{
}
struct Properties {
PositionEdge edge;
LengthPercentage offset;
Optional<PositionEdge> edge;
Optional<LengthPercentage> offset;
bool operator==(Properties const&) const = default;
} m_properties;
};

View file

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

View file

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

View file

@ -65,6 +65,8 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
auto color = longhand(PropertyID::BackgroundColor);
auto image = longhand(PropertyID::BackgroundImage);
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 repeat = longhand(PropertyID::BackgroundRepeat);
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;
};
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) {
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) {
@ -93,7 +95,38 @@ String ShorthandStyleValue::to_string(SerializationMode mode) const
builder.append(", "sv);
if (i == layer_count - 1)
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());

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()) {
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();
}
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_edge_y = position.edge().value_or(CSS::PositionEdge::Top);
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[] =
1. background-color

View file

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

View file

@ -159,7 +159,7 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'aspect-ratio': 'auto'
'backdropFilter': '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'
'background-attachment': 'scroll'
'backgroundClip': 'border-box'
@ -170,12 +170,12 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'background-image': 'none'
'backgroundOrigin': 'padding-box'
'background-origin': 'padding-box'
'backgroundPosition': 'left 0% top 0%'
'background-position': 'left 0% top 0%'
'backgroundPositionX': 'left 0%'
'background-position-x': 'left 0%'
'backgroundPositionY': 'top 0%'
'background-position-y': 'top 0%'
'backgroundPosition': '0% 0%'
'background-position': '0% 0%'
'backgroundPositionX': '0%'
'background-position-x': '0%'
'backgroundPositionY': '0%'
'background-position-y': '0%'
'backgroundRepeat': 'repeat'
'background-repeat': 'repeat'
'backgroundSize': 'auto auto'
@ -430,8 +430,8 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'min-width': 'auto'
'objectFit': 'fill'
'object-fit': 'fill'
'objectPosition': 'left 50% top 50%'
'object-position': 'left 50% top 50%'
'objectPosition': '50% 50%'
'object-position': '50% 50%'
'opacity': '1'
'order': '0'
'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(0.02))' -> 'grayscale(calc(0.02))'
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 * var(--n))' -> 'left calc(2px * 2)'
background-position-y: 'calc(2%)' -> 'top calc(2%)'
background-position-y: 'calc(2% * var(--n))' -> 'top 4%'
background-position-x: 'calc(2px)' -> 'calc(2px)'
background-position-x: 'calc(2px * var(--n))' -> 'calc(2px * 2)'
background-position-y: 'calc(2%)' -> 'calc(2%)'
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% * var(--n))' -> 'calc(2px * 2) 4%'
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-image: none
background-origin: padding-box
background-position-x: left 0%
background-position-y: top 0%
background-position-x: 0%
background-position-y: 0%
background-repeat: repeat
background-size: auto auto
border-bottom-color: rgb(0, 0, 0)
@ -154,7 +154,7 @@ min-height: auto
min-inline-size: 0px
min-width: auto
object-fit: fill
object-position: left 50% top 50%
object-position: 50% 50%
opacity: 1
order: 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
485 Pass
202 Fail
654 Pass
33 Fail
Pass background-attachment: scroll
Pass background-attachment: fixed
Pass background-attachment: inherit
@ -17,175 +17,175 @@ Pass background-image: url("http://localhost/")
Pass background-image: url(http://localhost/)
Pass background-image: none
Pass background-image: inherit
Fail background-position: 5% 5%
Fail background-position: 5% .5%
Fail background-position: 5% -5%
Fail background-position: 5% -.5%
Fail background-position: 5% 0px
Fail background-position: 5% 1px
Fail background-position: 5% .1em
Fail background-position: 5% -0px
Fail background-position: 5% -1px
Fail background-position: 5% -.1em
Fail background-position: 5% top
Fail background-position: 5% center
Fail background-position: 5% bottom
Fail background-position: .5% 5%
Fail background-position: .5% .5%
Fail background-position: .5% -5%
Fail background-position: .5% -.5%
Fail background-position: .5% 0px
Fail background-position: .5% 1px
Fail background-position: .5% .1em
Fail background-position: .5% -0px
Fail background-position: .5% -1px
Fail background-position: .5% -.1em
Fail background-position: .5% top
Fail background-position: .5% center
Fail background-position: .5% bottom
Fail background-position: -5% 5%
Fail background-position: -5% .5%
Fail background-position: -5% -5%
Fail background-position: -5% -.5%
Fail background-position: -5% 0px
Fail background-position: -5% 1px
Fail background-position: -5% .1em
Fail background-position: -5% -0px
Fail background-position: -5% -1px
Fail background-position: -5% -.1em
Fail background-position: -5% top
Fail background-position: -5% center
Fail background-position: -5% bottom
Fail background-position: -.5% 5%
Fail background-position: -.5% .5%
Fail background-position: -.5% -5%
Fail background-position: -.5% -.5%
Fail background-position: -.5% 0px
Fail background-position: -.5% 1px
Fail background-position: -.5% .1em
Fail background-position: -.5% -0px
Fail background-position: -.5% -1px
Fail background-position: -.5% -.1em
Fail background-position: -.5% top
Fail background-position: -.5% center
Fail background-position: -.5% bottom
Fail background-position: 0px 5%
Fail background-position: 0px .5%
Fail background-position: 0px -5%
Fail background-position: 0px -.5%
Fail background-position: 0px 0px
Fail background-position: 0px 1px
Fail background-position: 0px .1em
Fail background-position: 0px -0px
Fail background-position: 0px -1px
Fail background-position: 0px -.1em
Fail background-position: 0px top
Fail background-position: 0px center
Fail background-position: 0px bottom
Fail background-position: 1px 5%
Fail background-position: 1px .5%
Fail background-position: 1px -5%
Fail background-position: 1px -.5%
Fail background-position: 1px 0px
Fail background-position: 1px 1px
Fail background-position: 1px .1em
Fail background-position: 1px -0px
Fail background-position: 1px -1px
Fail background-position: 1px -.1em
Fail background-position: 1px top
Fail background-position: 1px center
Fail background-position: 1px bottom
Fail background-position: .1em 5%
Fail background-position: .1em .5%
Fail background-position: .1em -5%
Fail background-position: .1em -.5%
Fail background-position: .1em 0px
Fail background-position: .1em 1px
Fail background-position: .1em .1em
Fail background-position: .1em -0px
Fail background-position: .1em -1px
Fail background-position: .1em -.1em
Fail background-position: .1em top
Fail background-position: .1em center
Fail background-position: .1em bottom
Fail background-position: -0px 5%
Fail background-position: -0px .5%
Fail background-position: -0px -5%
Fail background-position: -0px -.5%
Fail background-position: -0px 0px
Fail background-position: -0px 1px
Fail background-position: -0px .1em
Fail background-position: -0px -0px
Fail background-position: -0px -1px
Fail background-position: -0px -.1em
Fail background-position: -0px top
Fail background-position: -0px center
Fail background-position: -0px bottom
Fail background-position: -1px 5%
Fail background-position: -1px .5%
Fail background-position: -1px -5%
Fail background-position: -1px -.5%
Fail background-position: -1px 0px
Fail background-position: -1px 1px
Fail background-position: -1px .1em
Fail background-position: -1px -0px
Fail background-position: -1px -1px
Fail background-position: -1px -.1em
Fail background-position: -1px top
Fail background-position: -1px center
Fail background-position: -1px bottom
Fail background-position: -.1em 5%
Fail background-position: -.1em .5%
Fail background-position: -.1em -5%
Fail background-position: -.1em -.5%
Fail background-position: -.1em 0px
Fail background-position: -.1em 1px
Fail background-position: -.1em .1em
Fail background-position: -.1em -0px
Fail background-position: -.1em -1px
Fail background-position: -.1em -.1em
Fail background-position: -.1em top
Fail background-position: -.1em center
Fail background-position: -.1em bottom
Fail background-position: left 5%
Fail background-position: left .5%
Fail background-position: left -5%
Fail background-position: left -.5%
Fail background-position: left 0px
Fail background-position: left 1px
Fail background-position: left .1em
Fail background-position: left -0px
Fail background-position: left -1px
Fail background-position: left -.1em
Fail background-position: left top
Fail background-position: left center
Fail background-position: left bottom
Fail background-position: center 5%
Fail background-position: center .5%
Fail background-position: center -5%
Fail background-position: center -.5%
Fail background-position: center 0px
Fail background-position: center 1px
Fail background-position: center .1em
Fail background-position: center -0px
Fail background-position: center -1px
Fail background-position: center -.1em
Fail background-position: center top
Fail background-position: center center
Fail background-position: center bottom
Fail background-position: right 5%
Fail background-position: right .5%
Fail background-position: right -5%
Fail background-position: right -.5%
Fail background-position: right 0px
Fail background-position: right 1px
Fail background-position: right .1em
Fail background-position: right -0px
Fail background-position: right -1px
Fail background-position: right -.1em
Fail background-position: right top
Fail background-position: right center
Fail background-position: right bottom
Pass background-position: 5% 5%
Pass background-position: 5% .5%
Pass background-position: 5% -5%
Pass background-position: 5% -.5%
Pass background-position: 5% 0px
Pass background-position: 5% 1px
Pass background-position: 5% .1em
Pass background-position: 5% -0px
Pass background-position: 5% -1px
Pass background-position: 5% -.1em
Pass background-position: 5% top
Pass background-position: 5% center
Pass background-position: 5% bottom
Pass background-position: .5% 5%
Pass background-position: .5% .5%
Pass background-position: .5% -5%
Pass background-position: .5% -.5%
Pass background-position: .5% 0px
Pass background-position: .5% 1px
Pass background-position: .5% .1em
Pass background-position: .5% -0px
Pass background-position: .5% -1px
Pass background-position: .5% -.1em
Pass background-position: .5% top
Pass background-position: .5% center
Pass background-position: .5% bottom
Pass background-position: -5% 5%
Pass background-position: -5% .5%
Pass background-position: -5% -5%
Pass background-position: -5% -.5%
Pass background-position: -5% 0px
Pass background-position: -5% 1px
Pass background-position: -5% .1em
Pass background-position: -5% -0px
Pass background-position: -5% -1px
Pass background-position: -5% -.1em
Pass background-position: -5% top
Pass background-position: -5% center
Pass background-position: -5% bottom
Pass background-position: -.5% 5%
Pass background-position: -.5% .5%
Pass background-position: -.5% -5%
Pass background-position: -.5% -.5%
Pass background-position: -.5% 0px
Pass background-position: -.5% 1px
Pass background-position: -.5% .1em
Pass background-position: -.5% -0px
Pass background-position: -.5% -1px
Pass background-position: -.5% -.1em
Pass background-position: -.5% top
Pass background-position: -.5% center
Pass background-position: -.5% bottom
Pass background-position: 0px 5%
Pass background-position: 0px .5%
Pass background-position: 0px -5%
Pass background-position: 0px -.5%
Pass background-position: 0px 0px
Pass background-position: 0px 1px
Pass background-position: 0px .1em
Pass background-position: 0px -0px
Pass background-position: 0px -1px
Pass background-position: 0px -.1em
Pass background-position: 0px top
Pass background-position: 0px center
Pass background-position: 0px bottom
Pass background-position: 1px 5%
Pass background-position: 1px .5%
Pass background-position: 1px -5%
Pass background-position: 1px -.5%
Pass background-position: 1px 0px
Pass background-position: 1px 1px
Pass background-position: 1px .1em
Pass background-position: 1px -0px
Pass background-position: 1px -1px
Pass background-position: 1px -.1em
Pass background-position: 1px top
Pass background-position: 1px center
Pass background-position: 1px bottom
Pass background-position: .1em 5%
Pass background-position: .1em .5%
Pass background-position: .1em -5%
Pass background-position: .1em -.5%
Pass background-position: .1em 0px
Pass background-position: .1em 1px
Pass background-position: .1em .1em
Pass background-position: .1em -0px
Pass background-position: .1em -1px
Pass background-position: .1em -.1em
Pass background-position: .1em top
Pass background-position: .1em center
Pass background-position: .1em bottom
Pass background-position: -0px 5%
Pass background-position: -0px .5%
Pass background-position: -0px -5%
Pass background-position: -0px -.5%
Pass background-position: -0px 0px
Pass background-position: -0px 1px
Pass background-position: -0px .1em
Pass background-position: -0px -0px
Pass background-position: -0px -1px
Pass background-position: -0px -.1em
Pass background-position: -0px top
Pass background-position: -0px center
Pass background-position: -0px bottom
Pass background-position: -1px 5%
Pass background-position: -1px .5%
Pass background-position: -1px -5%
Pass background-position: -1px -.5%
Pass background-position: -1px 0px
Pass background-position: -1px 1px
Pass background-position: -1px .1em
Pass background-position: -1px -0px
Pass background-position: -1px -1px
Pass background-position: -1px -.1em
Pass background-position: -1px top
Pass background-position: -1px center
Pass background-position: -1px bottom
Pass background-position: -.1em 5%
Pass background-position: -.1em .5%
Pass background-position: -.1em -5%
Pass background-position: -.1em -.5%
Pass background-position: -.1em 0px
Pass background-position: -.1em 1px
Pass background-position: -.1em .1em
Pass background-position: -.1em -0px
Pass background-position: -.1em -1px
Pass background-position: -.1em -.1em
Pass background-position: -.1em top
Pass background-position: -.1em center
Pass background-position: -.1em bottom
Pass background-position: left 5%
Pass background-position: left .5%
Pass background-position: left -5%
Pass background-position: left -.5%
Pass background-position: left 0px
Pass background-position: left 1px
Pass background-position: left .1em
Pass background-position: left -0px
Pass background-position: left -1px
Pass background-position: left -.1em
Pass background-position: left top
Pass background-position: left center
Pass background-position: left bottom
Pass background-position: center 5%
Pass background-position: center .5%
Pass background-position: center -5%
Pass background-position: center -.5%
Pass background-position: center 0px
Pass background-position: center 1px
Pass background-position: center .1em
Pass background-position: center -0px
Pass background-position: center -1px
Pass background-position: center -.1em
Pass background-position: center top
Pass background-position: center center
Pass background-position: center bottom
Pass background-position: right 5%
Pass background-position: right .5%
Pass background-position: right -5%
Pass background-position: right -.5%
Pass background-position: right 0px
Pass background-position: right 1px
Pass background-position: right .1em
Pass background-position: right -0px
Pass background-position: right -1px
Pass background-position: right -.1em
Pass background-position: right top
Pass background-position: right center
Pass background-position: right bottom
Pass background-position: inherit
Pass background-repeat: repeat
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>