diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index 739620738e9..9ee0a456b09 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -38,6 +38,15 @@ struct GridAutoFlow { bool dense { false }; }; +struct QuotesData { + enum class Type { + None, + Auto, + Specified, + } type; + Vector> strings {}; +}; + class InitialValues { public: static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; } @@ -120,6 +129,7 @@ public: static CSS::OutlineStyle outline_style() { return CSS::OutlineStyle::None; } static CSS::Length outline_width() { return CSS::Length::make_px(3); } static CSS::TableLayout table_layout() { return CSS::TableLayout::Auto; } + static QuotesData quotes() { return QuotesData { .type = QuotesData::Type::Auto }; } static CSS::MathShift math_shift() { return CSS::MathShift::Normal; } static CSS::MathStyle math_style() { return CSS::MathStyle::Normal; } @@ -350,6 +360,8 @@ public: CSS::TableLayout table_layout() const { return m_noninherited.table_layout; } + CSS::QuotesData quotes() const { return m_inherited.quotes; } + CSS::MathShift math_shift() const { return m_inherited.math_shift; } CSS::MathStyle math_style() const { return m_inherited.math_style; } int math_depth() const { return m_inherited.math_depth; } @@ -383,6 +395,7 @@ protected: CSS::ListStyleType list_style_type { InitialValues::list_style_type() }; CSS::ListStylePosition list_style_position { InitialValues::list_style_position() }; CSS::Visibility visibility { InitialValues::visibility() }; + CSS::QuotesData quotes { InitialValues::quotes() }; Optional fill; CSS::FillRule fill_rule { InitialValues::fill_rule() }; @@ -577,6 +590,7 @@ public: void set_grid_auto_flow(CSS::GridAutoFlow grid_auto_flow) { m_noninherited.grid_auto_flow = grid_auto_flow; } void set_transition_delay(CSS::Time const& transition_delay) { m_noninherited.transition_delay = transition_delay; } void set_table_layout(CSS::TableLayout value) { m_noninherited.table_layout = value; } + void set_quotes(CSS::QuotesData value) { m_inherited.quotes = value; } void set_fill(SVGPaint value) { m_inherited.fill = value; } void set_stroke(SVGPaint value) { m_inherited.stroke = value; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 4fbba0574f8..028e9f92645 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -4586,6 +4586,35 @@ RefPtr Parser::parse_place_self_value(Vector const& return PlaceItemsStyleValue::create(*maybe_align_self_value, *maybe_justify_self_value); } +RefPtr Parser::parse_quotes_value(Vector const& component_values) +{ + // https://www.w3.org/TR/css-content-3/#quotes-property + // auto | none | [ ]+ + + if (component_values.size() == 1) { + auto identifier = parse_identifier_value(component_values.first()); + if (identifier && property_accepts_identifier(PropertyID::Quotes, identifier->to_identifier())) + return identifier; + return nullptr; + } + + // Parse an even number of values. + if (component_values.size() % 2 != 0) + return nullptr; + + auto tokens = TokenStream { component_values }; + StyleValueVector string_values; + while (tokens.has_next_token()) { + auto maybe_string = parse_string_value(tokens.next_token()); + if (!maybe_string) + return nullptr; + + string_values.append(maybe_string.release_nonnull()); + } + + return StyleValueList::create(move(string_values), StyleValueList::Separator::Space); +} + RefPtr Parser::parse_text_decoration_value(Vector const& component_values) { RefPtr decoration_line; @@ -5869,6 +5898,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property if (auto parsed_value = parse_place_self_value(component_values)) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::Quotes: + if (auto parsed_value = parse_quotes_value(component_values)) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::TextDecoration: if (auto parsed_value = parse_text_decoration_value(component_values)) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index d886939f5af..2c6c866d4ca 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -243,6 +243,7 @@ private: RefPtr parse_place_content_value(Vector const&); RefPtr parse_place_items_value(Vector const&); RefPtr parse_place_self_value(Vector const&); + RefPtr parse_quotes_value(Vector const&); enum class AllowInsetKeyword { No, Yes, diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index a5380e9d6d1..b1e42685292 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -1836,6 +1836,17 @@ "position" ] }, + "quotes": { + "inherited": true, + "initial": "auto", + "valid-types": [ + "string" + ], + "valid-identifiers": [ + "auto", + "none" + ] + }, "right": { "inherited": false, "initial": "auto", diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 5d6a9ae4890..67f7f10924a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -990,4 +990,32 @@ void StyleProperties::set_math_depth(int math_depth) set_property(PropertyID::MathDepth, MathDepthStyleValue::create_integer(IntegerStyleValue::create(math_depth))); } +QuotesData StyleProperties::quotes() const +{ + auto value = property(CSS::PropertyID::Quotes); + if (value->is_identifier()) { + switch (value->to_identifier()) { + case ValueID::Auto: + return QuotesData { .type = QuotesData::Type::Auto }; + case ValueID::None: + return QuotesData { .type = QuotesData::Type::None }; + default: + break; + } + } + if (value->is_value_list()) { + auto& value_list = value->as_value_list(); + QuotesData quotes_data { .type = QuotesData::Type::Specified }; + VERIFY(value_list.size() % 2 == 0); + for (auto i = 0u; i < value_list.size(); i += 2) { + quotes_data.strings.empend( + value_list.value_at(i, false)->as_string().string_value(), + value_list.value_at(i + 1, false)->as_string().string_value()); + } + return quotes_data; + } + + return InitialValues::quotes(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index 0662ee750f9..682be1b7f92 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -146,6 +146,8 @@ public: void set_math_depth(int math_depth); int math_depth() const { return m_math_depth; } + QuotesData quotes() const; + static NonnullRefPtr font_fallback(bool monospace, bool bold); private: diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index f00708ebd22..19e16ecf4b8 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -806,6 +806,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) computed_values.set_math_style(math_style.value()); computed_values.set_math_depth(computed_style.math_depth()); + computed_values.set_quotes(computed_style.quotes()); // Update any anonymous children that inherit from this node. // FIXME: This is pretty hackish. It would be nicer if they shared the inherited style