diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index 1e60e84f785..99b69713a55 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -15,6 +15,7 @@ font-language-override: normal font-size: 16px font-style: normal font-variant: normal +font-variation-settings: normal font-weight: 400 font-width: normal image-rendering: auto @@ -118,7 +119,7 @@ grid-row-start: auto grid-template-areas: grid-template-columns: grid-template-rows: -height: 2040px +height: 2057px inline-size: auto inset-block-end: auto inset-block-start: auto diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index a99677fd01a..66c97db5b66 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -488,6 +489,7 @@ public: int font_weight() const { return m_inherited.font_weight; } CSS::FontVariant font_variant() const { return m_inherited.font_variant; } Optional font_language_override() const { return m_inherited.font_language_override; } + Optional> font_variation_settings() const { return m_inherited.font_variation_settings; } CSSPixels line_height() const { return m_inherited.line_height; } CSS::Time transition_delay() const { return m_noninherited.transition_delay; } @@ -520,6 +522,7 @@ protected: int font_weight { InitialValues::font_weight() }; CSS::FontVariant font_variant { InitialValues::font_variant() }; Optional font_language_override; + Optional> font_variation_settings; CSSPixels line_height { InitialValues::line_height() }; CSS::BorderCollapse border_collapse { InitialValues::border_collapse() }; CSS::Length border_spacing_horizontal { InitialValues::border_spacing() }; @@ -678,6 +681,7 @@ public: void set_font_weight(int font_weight) { m_inherited.font_weight = font_weight; } void set_font_variant(CSS::FontVariant font_variant) { m_inherited.font_variant = font_variant; } void set_font_language_override(Optional font_language_override) { m_inherited.font_language_override = font_language_override; } + void set_font_variation_settings(Optional> value) { m_inherited.font_variation_settings = move(value); } void set_line_height(CSSPixels line_height) { m_inherited.line_height = line_height; } void set_border_spacing_horizontal(CSS::Length border_spacing_horizontal) { m_inherited.border_spacing_horizontal = border_spacing_horizontal; } void set_border_spacing_vertical(CSS::Length border_spacing_vertical) { m_inherited.border_spacing_vertical = border_spacing_vertical; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 50ed3e79707..73e3d3e00b7 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -5479,6 +5480,52 @@ RefPtr Parser::parse_font_language_override_value(TokenStream Parser::parse_font_variation_settings_value(TokenStream& tokens) +{ + // https://drafts.csswg.org/css-fonts/#propdef-font-variation-settings + // normal | [ ]# + + // normal + if (auto normal = parse_all_as_single_keyword_value(tokens, Keyword::Normal)) + return normal; + + // [ ]# + auto transaction = tokens.begin_transaction(); + auto tag_values = parse_a_comma_separated_list_of_component_values(tokens); + + // "If the same axis name appears more than once, the value associated with the last appearance supersedes any + // previous value for that axis. This deduplication is observable by accessing the computed value of this property." + // So, we deduplicate them here using a HashSet. + + OrderedHashMap> axis_tags_map; + for (auto const& values : tag_values) { + TokenStream tag_tokens { values }; + tag_tokens.skip_whitespace(); + auto opentype_tag = parse_opentype_tag_value(tag_tokens); + tag_tokens.skip_whitespace(); + auto number = parse_number_value(tag_tokens); + tag_tokens.skip_whitespace(); + + if (!opentype_tag || !number || tag_tokens.has_next_token()) + return nullptr; + + axis_tags_map.set(opentype_tag->string_value(), OpenTypeTaggedStyleValue::create(opentype_tag->string_value(), number.release_nonnull())); + } + + // "The computed value contains the de-duplicated axis names, sorted in ascending order by code unit." + StyleValueVector axis_tags; + axis_tags.ensure_capacity(axis_tags_map.size()); + for (auto const& [key, axis_tag] : axis_tags_map) + axis_tags.append(axis_tag); + + quick_sort(axis_tags, [](auto& a, auto& b) { + return a->as_open_type_tagged().tag() < b->as_open_type_tagged().tag(); + }); + + transaction.commit(); + return StyleValueList::create(move(axis_tags), StyleValueList::Separator::Comma); +} + JS::GCPtr Parser::parse_font_face_rule(TokenStream& tokens) { auto declarations_and_at_rules = parse_a_list_of_declarations(tokens); @@ -7577,6 +7624,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Prope if (auto parsed_value = parse_font_language_override_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::FontVariationSettings: + if (auto parsed_value = parse_font_variation_settings_value(tokens); parsed_value && !tokens.has_next_token()) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::GridArea: if (auto parsed_value = parse_grid_area_shorthand_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 21d3e4505a4..e8b0837ee78 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -307,6 +307,7 @@ private: RefPtr parse_font_value(TokenStream&); RefPtr parse_font_family_value(TokenStream&); RefPtr parse_font_language_override_value(TokenStream&); + RefPtr parse_font_variation_settings_value(TokenStream&); RefPtr parse_list_style_value(TokenStream&); RefPtr parse_math_depth_value(TokenStream&); RefPtr parse_overflow_value(TokenStream&); diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 61bbba8b141..614c5a16fcc 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -1223,6 +1223,18 @@ "font-variant" ] }, + "font-variation-settings": { + "animation-type": "custom", + "inherited": true, + "initial": "normal", + "valid-types": [ + "number", + "opentype-tag" + ], + "valid-identifiers": [ + "normal" + ] + }, "font-weight": { "animation-type": "by-computed-value", "inherited": true, diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 63a5c5da753..94d41d117f9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2024, Andreas Kling - * Copyright (c) 2021-2024, Sam Atkins + * Copyright (c) 2021-2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1027,6 +1028,33 @@ Optional StyleProperties::font_language_override() const return {}; } +Optional> StyleProperties::font_variation_settings() const +{ + auto value = property(CSS::PropertyID::FontVariationSettings); + + if (value->is_keyword()) + return {}; // normal + + if (value->is_value_list()) { + auto const& axis_tags = value->as_value_list().values(); + HashMap result; + result.ensure_capacity(axis_tags.size()); + for (auto const& tag_value : axis_tags) { + auto const& axis_tag = tag_value->as_open_type_tagged(); + + if (axis_tag.value()->is_number()) { + result.set(axis_tag.tag(), axis_tag.value()->as_number().value()); + } else { + VERIFY(axis_tag.value()->is_math()); + result.set(axis_tag.tag(), NumberOrCalculated { axis_tag.value()->as_math() }); + } + } + return result; + } + + return {}; +} + CSS::GridTrackSizeList StyleProperties::grid_auto_columns() const { auto value = property(CSS::PropertyID::GridAutoColumns); diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index f95de6e6ac5..71e4ad8859e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -149,6 +149,7 @@ public: Variant vertical_align() const; Optional font_variant() const; Optional font_language_override() const; + Optional> font_variation_settings() const; CSS::GridTrackSizeList grid_auto_columns() const; CSS::GridTrackSizeList grid_auto_rows() const; CSS::GridTrackSizeList grid_template_columns() const; diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index fa596f8ee21..b590d443a56 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -471,6 +471,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) computed_values.set_font_variant(maybe_font_variant.release_value()); if (auto maybe_font_language_override = computed_style.font_language_override(); maybe_font_language_override.has_value()) computed_values.set_font_language_override(maybe_font_language_override.release_value()); + if (auto maybe_font_variation_settings = computed_style.font_variation_settings(); maybe_font_variation_settings.has_value()) + computed_values.set_font_variation_settings(maybe_font_variation_settings.release_value()); auto border_bottom_left_radius = computed_style.property(CSS::PropertyID::BorderBottomLeftRadius); if (border_bottom_left_radius->is_border_radius()) {