mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-17 07:50:04 +00:00
LibWeb: Properly serialize position/edge style values
This commit is contained in:
parent
583ca6af89
commit
84150f972f
Notes:
github-actions[bot]
2024-12-13 11:36:34 +00:00
Author: https://github.com/Gingeh
Commit: 84150f972f
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2651
Reviewed-by: https://github.com/AtkinsSJ ✅
Reviewed-by: https://github.com/awesomekling
21 changed files with 461 additions and 288 deletions
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue