From f97ac33cb3de3eff5adde8d677f7da543835fc26 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 25 Feb 2025 16:37:10 +0000 Subject: [PATCH] LibWeb/CSS: Use NumericCalculationNode for constants Having multiple kinds of node that hold numeric values made things more complicated than they needed to be, and we were already converting ConstantCalculationNodes to NumericCalculationNodes in the first simplification pass that happens at parse-time, so they didn't exist after that. As noted, the spec allows for other contexts to introduce their own numeric keywords, which might be resolved later than parse-time. We'll need a different mechanism to support those, but ConstantCalculationNode could not have done so anyway. --- Libraries/LibWeb/CSS/Keywords.json | 5 + Libraries/LibWeb/CSS/Parser/ValueParsing.cpp | 6 +- .../CSS/StyleValues/CalculatedStyleValue.cpp | 125 ++++-------------- .../CSS/StyleValues/CalculatedStyleValue.h | 34 +---- .../LibWeb/GenerateCSSKeyword.cpp | 13 +- .../LibWeb/GenerateCSSMathFunctions.cpp | 2 +- 6 files changed, 45 insertions(+), 140 deletions(-) diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index eff596994d6..5c6267ed0b9 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -1,4 +1,5 @@ [ + "-infinity", "-libweb-center", "-libweb-left", "-libweb-link", @@ -151,6 +152,7 @@ "dotted", "double", "down", + "e", "e-resize", "ease", "ease-in", @@ -207,6 +209,7 @@ "inactivecaption", "inactivecaptiontext", "infinite", + "infinity", "infobackground", "infotext", "inherit", @@ -285,6 +288,7 @@ "move", "multiply", "n-resize", + "nan", "ne-resize", "nearest", "nesw-resize", @@ -325,6 +329,7 @@ "paint", "paused", "petite-caps", + "pi", "pixelated", "plaintext", "plus-darker", diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 576aa24e8e2..0867caa0afe 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -3340,10 +3340,10 @@ RefPtr Parser::convert_to_calculation_node(CalcParsing::Node co // AD-HOC: We also need to convert tokens into their numeric types. if (component_value->is(Token::Type::Ident)) { - auto maybe_constant = CalculationNode::constant_type_from_string(component_value->token().ident()); - if (!maybe_constant.has_value()) + auto maybe_keyword = keyword_from_string(component_value->token().ident()); + if (!maybe_keyword.has_value()) return nullptr; - return ConstantCalculationNode::create(*maybe_constant); + return NumericCalculationNode::from_keyword(*maybe_keyword, context); } if (component_value->is(Token::Type::Number)) diff --git a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp index f9884a30328..dab84206db0 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp @@ -129,26 +129,6 @@ static NonnullRefPtr simplify_2_children(T const& original, Non return original; } -Optional CalculationNode::constant_type_from_string(StringView string) -{ - if (string.equals_ignoring_ascii_case("e"sv)) - return CalculationNode::ConstantType::E; - - if (string.equals_ignoring_ascii_case("pi"sv)) - return CalculationNode::ConstantType::Pi; - - if (string.equals_ignoring_ascii_case("infinity"sv)) - return CalculationNode::ConstantType::Infinity; - - if (string.equals_ignoring_ascii_case("-infinity"sv)) - return CalculationNode::ConstantType::MinusInfinity; - - if (string.equals_ignoring_ascii_case("NaN"sv)) - return CalculationNode::ConstantType::NaN; - - return {}; -} - CalculationNode::CalculationNode(Type type, Optional numeric_type) : m_type(type) , m_numeric_type(move(numeric_type)) @@ -228,6 +208,29 @@ NonnullRefPtr NumericCalculationNode::create(NumericValu return adopt_ref(*new (nothrow) NumericCalculationNode(move(value), numeric_type)); } +RefPtr NumericCalculationNode::from_keyword(Keyword keyword, CalculationContext const& context) +{ + switch (keyword) { + case Keyword::E: + // https://drafts.csswg.org/css-values-4/#valdef-calc-e + return create(Number { Number::Type::Number, AK::E }, context); + case Keyword::Pi: + // https://drafts.csswg.org/css-values-4/#valdef-calc-pi + return create(Number { Number::Type::Number, AK::Pi }, context); + case Keyword::Infinity: + // https://drafts.csswg.org/css-values-4/#valdef-calc-infinity + return create(Number { Number::Type::Number, AK::Infinity }, context); + case Keyword::NegativeInfinity: + // https://drafts.csswg.org/css-values-4/#valdef-calc--infinity + return create(Number { Number::Type::Number, -AK::Infinity }, context); + case Keyword::Nan: + // https://drafts.csswg.org/css-values-4/#valdef-calc-nan + return create(Number { Number::Type::Number, AK::NaN }, context); + default: + return nullptr; + } +} + NumericCalculationNode::NumericCalculationNode(NumericValue value, CSSNumericType numeric_type) : CalculationNode(Type::Numeric, move(numeric_type)) , m_value(move(value)) @@ -1114,73 +1117,6 @@ bool SignCalculationNode::equals(CalculationNode const& other) const return m_value->equals(*static_cast(other).m_value); } -NonnullRefPtr ConstantCalculationNode::create(ConstantType constant) -{ - return adopt_ref(*new (nothrow) ConstantCalculationNode(constant)); -} - -ConstantCalculationNode::ConstantCalculationNode(ConstantType constant) - // https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation - // Anything else is a terminal value, whose type is determined based on its CSS type: - // -> - // the type is «[ ]» (empty map) - : CalculationNode(Type::Constant, CSSNumericType {}) - , m_constant(constant) -{ -} - -ConstantCalculationNode::~ConstantCalculationNode() = default; - -String ConstantCalculationNode::to_string() const -{ - switch (m_constant) { - case CalculationNode::ConstantType::E: - return "e"_string; - case CalculationNode::ConstantType::Pi: - return "pi"_string; - case CalculationNode::ConstantType::Infinity: - return "infinity"_string; - case CalculationNode::ConstantType::MinusInfinity: - return "-infinity"_string; - case CalculationNode::ConstantType::NaN: - return "NaN"_string; - } - - VERIFY_NOT_REACHED(); -} - -CalculatedStyleValue::CalculationResult ConstantCalculationNode::resolve(CalculationResolutionContext const&) const -{ - switch (m_constant) { - case ConstantType::E: - return { AK::E, CSSNumericType {} }; - case ConstantType::Pi: - return { AK::Pi, CSSNumericType {} }; - case ConstantType::Infinity: - return { AK::Infinity, CSSNumericType {} }; - case ConstantType::MinusInfinity: - return { -AK::Infinity, CSSNumericType {} }; - case ConstantType::NaN: - return { AK::NaN, CSSNumericType {} }; - } - - VERIFY_NOT_REACHED(); -} - -void ConstantCalculationNode::dump(StringBuilder& builder, int indent) const -{ - builder.appendff("{: >{}}CONSTANT: {}\n", "", indent, to_string()); -} - -bool ConstantCalculationNode::equals(CalculationNode const& other) const -{ - if (this == &other) - return true; - if (type() != other.type()) - return false; - return m_constant == static_cast(other).m_constant; -} - NonnullRefPtr SinCalculationNode::create(NonnullRefPtr value) { return adopt_ref(*new (nothrow) SinCalculationNode(move(value))); @@ -2795,24 +2731,13 @@ NonnullRefPtr simplify_a_calculation_tree(CalculationNode const } // 3. If root is a that can be resolved, return what it resolves to, simplified. - // NOTE: This is handled below. + // NOTE: We already resolve our ``s at parse-time. + // FIXME: Revisit this once we support any keywords that need resolving later. // 4. Otherwise, return root. return root; } - // AD-HOC: Step 1.3 is done here as we have a separate node type for them. - if (root->type() == CalculationNode::Type::Constant) { - auto const& root_constant = as(*root); - - // 3. If root is a that can be resolved, return what it resolves to, simplified. - // FIXME: At the moment these are all constant numbers. Revisit this once that's not the case. - // (Notably, relative-color syntax allows some other keywords that are relative to the color.) - auto resolved = root_constant.resolve(resolution_context); - VERIFY(resolved.type()->matches_number({})); - return NumericCalculationNode::create(Number { Number::Type::Number, resolved.value() }, context); - } - // 2. If root is any other leaf node (not an operator node): // FIXME: We don't yet allow any of these inside a calculation tree. Revisit once we do. diff --git a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h index 4848510e11b..9a2ab01c7bc 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h @@ -128,17 +128,6 @@ private: // https://www.w3.org/TR/css-values-4/#calculation-tree class CalculationNode : public RefCounted { public: - // https://drafts.csswg.org/css-values-4/#calc-constants - // https://drafts.csswg.org/css-values-4/#calc-error-constants - enum class ConstantType { - E, - Pi, - NaN, - Infinity, - MinusInfinity, - }; - static Optional constant_type_from_string(StringView); - enum class Type { Numeric, // NOTE: Currently, any value with a `var()` or `attr()` function in it is always an @@ -162,10 +151,6 @@ public: Abs, Sign, - // Constant Nodes - // https://drafts.csswg.org/css-values-4/#calc-constants - Constant, - // Trigonometric functions, a sub-type of operator node // https://drafts.csswg.org/css-values-4/#trig-funcs Sin, @@ -260,6 +245,7 @@ private: class NumericCalculationNode final : public CalculationNode { public: static NonnullRefPtr create(NumericValue, CalculationContext const&); + static RefPtr from_keyword(Keyword, CalculationContext const&); ~NumericCalculationNode(); virtual String to_string() const override; @@ -461,24 +447,6 @@ private: NonnullRefPtr m_value; }; -class ConstantCalculationNode final : public CalculationNode { -public: - static NonnullRefPtr create(CalculationNode::ConstantType); - ~ConstantCalculationNode(); - - virtual String to_string() const override; - virtual bool contains_percentage() const override { return false; } - virtual CalculatedStyleValue::CalculationResult resolve(CalculationResolutionContext const&) const override; - virtual NonnullRefPtr with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override { return *this; } - - virtual void dump(StringBuilder&, int indent) const override; - virtual bool equals(CalculationNode const&) const override; - -private: - ConstantCalculationNode(ConstantType); - CalculationNode::ConstantType m_constant; -}; - class SinCalculationNode final : public CalculationNode { public: static NonnullRefPtr create(NonnullRefPtr); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSKeyword.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSKeyword.cpp index e0eade677dd..b183cec3981 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSKeyword.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSKeyword.cpp @@ -39,6 +39,13 @@ ErrorOr serenity_main(Main::Arguments arguments) return 0; } +static String keyword_name(String const& dashy_name) +{ + if (dashy_name == "-infinity"sv) + return "NegativeInfinity"_string; + return title_casify(dashy_name); +} + ErrorOr generate_header_file(JsonArray& keyword_data, Core::File& file) { StringBuilder builder; @@ -57,7 +64,7 @@ enum class Keyword { keyword_data.for_each([&](auto& name) { auto member_generator = generator.fork(); - member_generator.set("name:titlecase", title_casify(name.as_string())); + member_generator.set("name:titlecase", keyword_name(name.as_string())); member_generator.append(R"~~~( @name:titlecase@, @@ -107,7 +114,7 @@ HashMap g_stringv keyword_data.for_each([&](auto& name) { auto member_generator = generator.fork(); member_generator.set("name", name.as_string()); - member_generator.set("name:titlecase", title_casify(name.as_string())); + member_generator.set("name:titlecase", keyword_name(name.as_string())); member_generator.append(R"~~~( {"@name@"sv, Keyword::@name:titlecase@}, )~~~"); @@ -128,7 +135,7 @@ StringView string_from_keyword(Keyword keyword) { keyword_data.for_each([&](auto& name) { auto member_generator = generator.fork(); member_generator.set("name", name.as_string()); - member_generator.set("name:titlecase", title_casify(name.as_string())); + member_generator.set("name:titlecase", keyword_name(name.as_string())); member_generator.append(R"~~~( case Keyword::@name:titlecase@: return "@name@"sv; diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp index 512fc56d5f9..a92b1d488b4 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp @@ -251,7 +251,7 @@ RefPtr Parser::parse_math_function(Function const& function, Ca // NOTE: We have exactly one default value in the data right now, and it's a ``, // so that's all we handle. if (auto default_value = parameter.get_string("default"sv); default_value.has_value()) { - parameter_generator.set("parameter_default", MUST(String::formatted(" = ConstantCalculationNode::create(CalculationNode::constant_type_from_string(\"{}\"sv).value())", default_value.value()))); + parameter_generator.set("parameter_default", MUST(String::formatted(" = NumericCalculationNode::from_keyword(Keyword::{}, context)", title_casify(default_value.value())))); } else { parameter_generator.set("parameter_default", ""_string); }