LibWeb: Add SVG-presentation-attribute-parsing mode to CSS parser

When parsing these, <number> is allowed anywhere that would usually
allow a <length>, <length-percentage>, or <angle>. The spec is not
clear on exactly how this should work
(see https://github.com/w3c/svgwg/issues/792 ) so I'm using some
artistic license until things are clearer:
- If we expected a <length>, treat the <number> as pixels.
- If we expected an <angle>, treat the <number> as degrees.
- Only allow direct <number> tokens, not calc() or other functions.

From what I can tell this is what the spec *intended* but I may be very
wrong. In any case, telling the ParsingContext whether we're parsing
one of these attributes is a cleaner approach and more correct than
temporarily enabling quirks mode, which we did previously.
This commit is contained in:
Sam Atkins 2023-09-25 15:26:33 +01:00 committed by Andreas Kling
parent c46ce53ce3
commit 79a30c209d
3 changed files with 43 additions and 8 deletions

View file

@ -6200,6 +6200,25 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
|| any_property_accepts_type(property_ids, ValueType::Time).has_value(); || any_property_accepts_type(property_ids, ValueType::Time).has_value();
if (property_accepts_dimension) { if (property_accepts_dimension) {
if (peek_token.is(Token::Type::Number) && m_context.is_parsing_svg_presentation_attribute()) {
auto transaction = tokens.begin_transaction();
auto token = tokens.next_token();
// https://svgwg.org/svg2-draft/types.html#presentation-attribute-css-value
// We need to allow <number> in any place that expects a <length> or <angle>.
// FIXME: How should these numbers be interpreted? https://github.com/w3c/svgwg/issues/792
// For now: Convert them to px lengths, or deg angles.
auto angle = Angle::make_degrees(token.token().number_value());
if (auto property = any_property_accepts_type(property_ids, ValueType::Angle); property.has_value() && property_accepts_angle(*property, angle)) {
transaction.commit();
return PropertyAndValue { *property, AngleStyleValue::create(angle) };
}
auto length = Length::make_px(CSSPixels::nearest_value_for(token.token().number_value()));
if (auto property = any_property_accepts_type(property_ids, ValueType::Length); property.has_value() && property_accepts_length(*property, length)) {
transaction.commit();
return PropertyAndValue { *property, LengthStyleValue::create(length) };
}
}
auto transaction = tokens.begin_transaction(); auto transaction = tokens.begin_transaction();
if (auto maybe_dimension = parse_dimension(peek_token); maybe_dimension.has_value()) { if (auto maybe_dimension = parse_dimension(peek_token); maybe_dimension.has_value()) {
(void)tokens.next_token(); (void)tokens.next_token();
@ -6249,6 +6268,9 @@ Optional<Parser::PropertyAndValue> Parser::parse_css_value_for_properties(Readon
(void)tokens.next_token(); (void)tokens.next_token();
auto& calculated = *maybe_calculated; auto& calculated = *maybe_calculated;
// This is a bit sensitive to ordering: `<foo>` and `<percentage>` have to be checked before `<foo-percentage>`. // This is a bit sensitive to ordering: `<foo>` and `<percentage>` have to be checked before `<foo-percentage>`.
// FIXME: When parsing SVG presentation attributes, <number> is permitted wherever <length>, <length-percentage>, or <angle> are.
// The specifics are unclear, so I'm ignoring this for calculated values for now.
// See https://github.com/w3c/svgwg/issues/792
if (calculated.resolves_to_percentage()) { if (calculated.resolves_to_percentage()) {
if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value()) if (auto property = any_property_accepts_type(property_ids, ValueType::Percentage); property.has_value())
return PropertyAndValue { *property, calculated }; return PropertyAndValue { *property, calculated };

View file

@ -13,29 +13,33 @@
namespace Web::CSS::Parser { namespace Web::CSS::Parser {
ParsingContext::ParsingContext(JS::Realm& realm) ParsingContext::ParsingContext(JS::Realm& realm, Mode mode)
: m_realm(realm) : m_realm(realm)
, m_mode(mode)
{ {
} }
ParsingContext::ParsingContext(DOM::Document const& document, AK::URL url) ParsingContext::ParsingContext(DOM::Document const& document, AK::URL url, Mode mode)
: m_realm(const_cast<JS::Realm&>(document.realm())) : m_realm(const_cast<JS::Realm&>(document.realm()))
, m_document(&document) , m_document(&document)
, m_url(move(url)) , m_url(move(url))
, m_mode(mode)
{ {
} }
ParsingContext::ParsingContext(DOM::Document const& document) ParsingContext::ParsingContext(DOM::Document const& document, Mode mode)
: m_realm(const_cast<JS::Realm&>(document.realm())) : m_realm(const_cast<JS::Realm&>(document.realm()))
, m_document(&document) , m_document(&document)
, m_url(document.url()) , m_url(document.url())
, m_mode(mode)
{ {
} }
ParsingContext::ParsingContext(DOM::ParentNode& parent_node) ParsingContext::ParsingContext(DOM::ParentNode& parent_node, Mode mode)
: m_realm(parent_node.realm()) : m_realm(parent_node.realm())
, m_document(&parent_node.document()) , m_document(&parent_node.document())
, m_url(parent_node.document().url()) , m_url(parent_node.document().url())
, m_mode(mode)
{ {
} }

View file

@ -14,10 +14,18 @@ namespace Web::CSS::Parser {
class ParsingContext { class ParsingContext {
public: public:
explicit ParsingContext(JS::Realm&); enum class Mode {
explicit ParsingContext(DOM::Document const&); Normal,
explicit ParsingContext(DOM::Document const&, AK::URL); SVGPresentationAttribute, // See https://svgwg.org/svg2-draft/types.html#presentation-attribute-css-value
explicit ParsingContext(DOM::ParentNode&); };
explicit ParsingContext(JS::Realm&, Mode = Mode::Normal);
explicit ParsingContext(DOM::Document const&, Mode = Mode::Normal);
explicit ParsingContext(DOM::Document const&, AK::URL, Mode = Mode::Normal);
explicit ParsingContext(DOM::ParentNode&, Mode = Mode::Normal);
Mode mode() const { return m_mode; }
bool is_parsing_svg_presentation_attribute() const { return m_mode == Mode::SVGPresentationAttribute; }
bool in_quirks_mode() const; bool in_quirks_mode() const;
DOM::Document const* document() const { return m_document; } DOM::Document const* document() const { return m_document; }
@ -34,6 +42,7 @@ private:
JS::GCPtr<DOM::Document const> m_document; JS::GCPtr<DOM::Document const> m_document;
PropertyID m_current_property_id { PropertyID::Invalid }; PropertyID m_current_property_id { PropertyID::Invalid };
AK::URL m_url; AK::URL m_url;
Mode m_mode { Mode::Normal };
}; };
} }