From 050d3a58cf14c81e5473633284e209cc79ef6a08 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 11 Sep 2025 15:57:35 +0100 Subject: [PATCH] LibWeb/CSS: Generate canonical/compatible unit functions These are used by `CSSNumericValue.to(unit)` which attempts to convert to the provided unit. --- Documentation/CSSGeneratedFiles.md | 2 ++ .../LibWeb/GenerateCSSUnits.cpp | 35 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Documentation/CSSGeneratedFiles.md b/Documentation/CSSGeneratedFiles.md index 6aeb4a4865..eef2cc41f0 100644 --- a/Documentation/CSSGeneratedFiles.md +++ b/Documentation/CSSGeneratedFiles.md @@ -354,7 +354,9 @@ The generated code provides: - `Optional dimension_for_unit(StringView)` for querying which dimension a unit applies to, if any. - A `FooUnit` enum for each dimension "foo", which lists all the units of that dimension. - For each of those... + - `constexpr FooUnit canonical_foo_unit()` which is the canonical unit for that type. - `Optional string_to_foo_unit(StringView)` for parsing a unit from a string. - `StringView to_string(FooUnit)` for serializing those units. + - `bool units_are_compatible(FooUnit, FooUnit)` which returns whether these are compatible - basically whether you can convert from one to the other. - `double ratio_between_units(FooUnit, FooUnit)` to get a multiplier for converting the first unit into the second. - `bool is_absolute(LengthUnit)`, `bool is_font_relative(LengthUnit)`, `bool is_viewport_relative(LengthUnit)`, and `bool is_relative(LengthUnit)` for checking the category of length units. diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp index e51def0ae4..6a466c1cdb 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp @@ -150,15 +150,20 @@ Optional dimension_for_unit(StringView); enum_generator.append(R"~~~( enum class @dimension_name:titlecase@Unit : @enum_type@ { )~~~"); - units.for_each_member([&](auto& unit_name, auto&) { + units.for_each_member([&](auto& unit_name, auto& unit_value) { + auto& unit = unit_value.as_object(); + if (unit.get_bool("is-canonical-unit"sv) == true) + enum_generator.set("canonical_unit:titlecase", title_casify(unit_name)); auto unit_generator = enum_generator.fork(); unit_generator.set("unit_name:titlecase", title_casify(unit_name)); unit_generator.appendln(" @unit_name:titlecase@,"); }); enum_generator.append(R"~~~( }; +constexpr @dimension_name:titlecase@Unit canonical_@dimension_name:snakecase@_unit() { return @dimension_name:titlecase@Unit::@canonical_unit:titlecase@; } Optional<@dimension_name:titlecase@Unit> string_to_@dimension_name:snakecase@_unit(StringView); FlyString to_string(@dimension_name:titlecase@Unit); +bool units_are_compatible(@dimension_name:titlecase@Unit, @dimension_name:titlecase@Unit); double ratio_between_units(@dimension_name:titlecase@Unit, @dimension_name:titlecase@Unit); )~~~"); }); @@ -263,6 +268,34 @@ FlyString to_string(@dimension_name:titlecase@Unit value) } } +bool units_are_compatible(@dimension_name:titlecase@Unit a, @dimension_name:titlecase@Unit b) +{ + auto is_absolute = [](@dimension_name:titlecase@Unit unit) -> bool { + switch (unit) { +)~~~"); + // https://drafts.csswg.org/css-values-4/#compatible-units + // NB: The spec describes two ways units can be compatible. Absolute ones always are, but it also lists em/px + // as compatible at computed value time. We should already have absolutized the units by then, but perhaps + // there is some case where we need to handle that here instead. + units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) { + auto const& unit = unit_value.as_object(); + if (unit.has("relative-to"sv)) + return; + auto unit_generator = dimension_generator.fork(); + unit_generator.set("unit_name:titlecase", title_casify(unit_name)); + unit_generator.appendln(" case @dimension_name:titlecase@Unit::@unit_name:titlecase@:"); + }); + + dimension_generator.append(R"~~~( + return true; + default: + return false; + } + }; + + return is_absolute(a) && is_absolute(b); +} + double ratio_between_units(@dimension_name:titlecase@Unit from, @dimension_name:titlecase@Unit to) { if (from == to)