LibWeb: Parse rect style value

Add ability to parse a rect when it is used as the value of a style
property.
This commit is contained in:
Tom 2022-07-31 18:46:35 +02:00 committed by Andreas Kling
parent 4d1ceaaa43
commit b4dd477644
Notes: sideshowbarker 2024-07-17 08:23:06 +09:00
7 changed files with 117 additions and 0 deletions

View file

@ -501,6 +501,11 @@ bool property_accepts_value(PropertyID property_id, StyleValue& style_value)
output_numeric_value_check(property_generator, "has_number"sv, "to_number()"sv, Array { "Integer"sv, "Number"sv }, min_value, max_value);
} else if (type_name == "percentage") {
output_numeric_value_check(property_generator, "is_percentage"sv, "as_percentage().percentage().value()"sv, Array { "Percentage"sv }, min_value, max_value);
} else if (type_name == "rect") {
property_generator.append(R"~~~(
if (style_value.has_rect())
return true;
)~~~");
} else if (type_name == "resolution") {
output_numeric_value_check(property_generator, "is_resolution"sv, "as_resolution().resolution().to_dots_per_pixel()"sv, Array<StringView, 0> {}, min_value, max_value);
} else if (type_name == "string") {

View file

@ -27,6 +27,7 @@
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/Parser/Rule.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWeb/CSS/StyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Dump.h>
@ -3350,6 +3351,46 @@ Optional<Color> Parser::parse_rgb_or_hsl_color(StringView function_name, Vector<
return {};
}
// https://www.w3.org/TR/CSS2/visufx.html#value-def-shape
RefPtr<StyleValue> Parser::parse_rect_value(ComponentValue const& component_value)
{
if (!component_value.is_function())
return {};
auto& function = component_value.function();
if (!function.name().equals_ignoring_case("rect"sv))
return {};
Vector<Length, 4> params;
auto tokens = TokenStream { function.values() };
// In CSS 2.1, the only valid <shape> value is: rect(<top>, <right>, <bottom>, <left>) where
// <top> and <bottom> specify offsets from the top border edge of the box, and <right>, and
// <left> specify offsets from the left border edge of the box.
for (size_t idx = 0; idx < 4; idx++) {
tokens.skip_whitespace();
// <top>, <right>, <bottom>, and <left> may either have a <length> value or 'auto'.
// Negative lengths are permitted.
auto current_token = tokens.next_token().token();
if (current_token.to_string() == "auto") {
params.append(Length::make_auto());
} else {
auto maybe_length = parse_length(current_token);
if (!maybe_length.has_value())
return {};
params.append(maybe_length.value());
}
tokens.skip_whitespace();
// Authors should separate offset values with commas. User agents must support separation
// with commas, but may also support separation without commas (but not a combination),
// because a previous revision of this specification was ambiguous in this respect.
if (tokens.peek_token().is(Token::Type::Comma))
tokens.next_token();
}
return RectStyleValue::create(EdgeRect { params[0], params[1], params[2], params[3] });
}
Optional<Color> Parser::parse_color(ComponentValue const& component_value)
{
// https://www.w3.org/TR/css-color-4/
@ -5416,6 +5457,9 @@ RefPtr<StyleValue> Parser::parse_css_value(ComponentValue const& component_value
if (auto image = parse_image_value(component_value))
return image;
if (auto rect = parse_rect_value(component_value))
return rect;
return {};
}

View file

@ -328,6 +328,7 @@ private:
RefPtr<StyleValue> parse_numeric_value(ComponentValue const&);
RefPtr<StyleValue> parse_identifier_value(ComponentValue const&);
RefPtr<StyleValue> parse_color_value(ComponentValue const&);
RefPtr<StyleValue> parse_rect_value(ComponentValue const&);
RefPtr<StyleValue> parse_string_value(ComponentValue const&);
RefPtr<StyleValue> parse_image_value(ComponentValue const&);
template<typename ParseFunction>

View file

@ -475,6 +475,9 @@
"valid-identifiers": [
"auto"
],
"valid-types": [
"rect"
],
"quirks": [
"unitless-length"
]

View file

@ -179,6 +179,12 @@ PositionStyleValue const& StyleValue::as_position() const
return static_cast<PositionStyleValue const&>(*this);
}
RectStyleValue const& StyleValue::as_rect() const
{
VERIFY(is_rect());
return static_cast<RectStyleValue const&>(*this);
}
ResolutionStyleValue const& StyleValue::as_resolution() const
{
VERIFY(is_resolution());
@ -1485,6 +1491,11 @@ static bool operator==(ColorStopListElement a, ColorStopListElement b)
return a.transition_hint == b.transition_hint && a.color_stop == b.color_stop;
}
static bool operator==(EdgeRect a, EdgeRect b)
{
return a.top_edge == b.top_edge && a.right_edge == b.right_edge && a.bottom_edge == b.bottom_edge && a.left_edge == b.left_edge;
}
bool LinearGradientStyleValue::equals(StyleValue const& other_) const
{
if (type() != other_.type())
@ -1650,6 +1661,19 @@ bool PositionStyleValue::equals(StyleValue const& other) const
&& m_offset_y == typed_other.m_offset_y;
}
String RectStyleValue::to_string() const
{
return String::formatted("top_edge: {}, right_edge: {}, bottom_edge: {}, left_edge: {}", m_rect.top_edge, m_rect.right_edge, m_rect.bottom_edge, m_rect.left_edge);
}
bool RectStyleValue::equals(StyleValue const& other) const
{
if (type() != other.type())
return false;
auto const& typed_other = other.as_rect();
return m_rect == typed_other.rect();
}
bool ResolutionStyleValue::equals(StyleValue const& other) const
{
if (type() != other.type())
@ -1810,6 +1834,11 @@ NonnullRefPtr<ColorStyleValue> ColorStyleValue::create(Color color)
return adopt_ref(*new ColorStyleValue(color));
}
NonnullRefPtr<RectStyleValue> RectStyleValue::create(EdgeRect rect)
{
return adopt_ref(*new RectStyleValue(rect));
}
NonnullRefPtr<LengthStyleValue> LengthStyleValue::create(Length const& length)
{
if (length.is_auto()) {

View file

@ -84,6 +84,13 @@ struct ColorStopListElement {
GradientColorStop color_stop;
};
struct EdgeRect {
Length top_edge;
Length right_edge;
Length bottom_edge;
Length left_edge;
};
// FIXME: Find a better place for this helper.
inline Gfx::Painter::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_value)
{
@ -131,6 +138,7 @@ public:
Overflow,
Percentage,
Position,
Rect,
Resolution,
Shadow,
String,
@ -169,6 +177,7 @@ public:
bool is_overflow() const { return type() == Type::Overflow; }
bool is_percentage() const { return type() == Type::Percentage; }
bool is_position() const { return type() == Type::Position; }
bool is_rect() const { return type() == Type::Rect; }
bool is_resolution() const { return type() == Type::Resolution; }
bool is_shadow() const { return type() == Type::Shadow; }
bool is_string() const { return type() == Type::String; }
@ -206,6 +215,7 @@ public:
OverflowStyleValue const& as_overflow() const;
PercentageStyleValue const& as_percentage() const;
PositionStyleValue const& as_position() const;
RectStyleValue const& as_rect() const;
ResolutionStyleValue const& as_resolution() const;
ShadowStyleValue const& as_shadow() const;
StringStyleValue const& as_string() const;
@ -241,6 +251,7 @@ public:
OverflowStyleValue& as_overflow() { return const_cast<OverflowStyleValue&>(const_cast<StyleValue const&>(*this).as_overflow()); }
PercentageStyleValue& as_percentage() { return const_cast<PercentageStyleValue&>(const_cast<StyleValue const&>(*this).as_percentage()); }
PositionStyleValue& as_position() { return const_cast<PositionStyleValue&>(const_cast<StyleValue const&>(*this).as_position()); }
RectStyleValue& as_rect() { return const_cast<RectStyleValue&>(const_cast<StyleValue const&>(*this).as_rect()); }
ResolutionStyleValue& as_resolution() { return const_cast<ResolutionStyleValue&>(const_cast<StyleValue const&>(*this).as_resolution()); }
ShadowStyleValue& as_shadow() { return const_cast<ShadowStyleValue&>(const_cast<StyleValue const&>(*this).as_shadow()); }
StringStyleValue& as_string() { return const_cast<StringStyleValue&>(const_cast<StyleValue const&>(*this).as_string()); }
@ -255,12 +266,14 @@ public:
virtual bool has_color() const { return false; }
virtual bool has_identifier() const { return false; }
virtual bool has_length() const { return false; }
virtual bool has_rect() const { return false; }
virtual bool has_number() const { return false; }
virtual bool has_integer() const { return false; }
virtual NonnullRefPtr<StyleValue> absolutized(Gfx::IntRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, float font_size, float root_font_size) const;
virtual Color to_color(Layout::NodeWithStyle const&) const { return {}; }
virtual EdgeRect to_rect() const { VERIFY_NOT_REACHED(); }
virtual CSS::ValueID to_identifier() const { return ValueID::Invalid; }
virtual Length to_length() const { VERIFY_NOT_REACHED(); }
virtual float to_number() const { return 0; }
@ -1440,6 +1453,27 @@ private:
NonnullRefPtrVector<StyleValue> m_values;
};
class RectStyleValue : public StyleValue {
public:
static NonnullRefPtr<RectStyleValue> create(EdgeRect rect);
virtual ~RectStyleValue() override = default;
EdgeRect rect() const { return m_rect; }
virtual String to_string() const override;
virtual bool has_rect() const override { return true; }
virtual EdgeRect to_rect() const override { return m_rect; }
virtual bool equals(StyleValue const& other) const override;
private:
explicit RectStyleValue(EdgeRect rect)
: StyleValue(Type::Rect)
, m_rect(rect)
{
}
EdgeRect m_rect;
};
}
template<>

View file

@ -75,6 +75,7 @@ class Percentage;
class PercentageStyleValue;
class PositionStyleValue;
class PropertyOwningCSSStyleDeclaration;
class RectStyleValue;
class Resolution;
class ResolutionStyleValue;
class Screen;