mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-22 01:08:56 +00:00
LibWeb/CSS: Implement CSSNumericValue.parse()
Reifying the result gets quite ad-hoc. Firstly because "parse a component value" produces a ComponentValue, not a full StyleValue like we need for math functions. And second, because not all math functions can be reified as a CSSNumericValue: Besides the fact that I haven't implemented CalculatedStyleValue reification at all yet, there are a lot of math functions with no corresponding CSSMathValue in the spec yet. If the calculation tree contains any of those, the best we can do is reify as a CSSStyleValue, and that isn't a valid return value from CSSNumericValue.parse(). So, I made us throw a SyntaxError in those cases. This seems to match Chrome's behaviour. Spec issue: https://github.com/w3c/css-houdini-drafts/issues/1090
This commit is contained in:
parent
73d7d6b831
commit
277117eed5
Notes:
github-actions[bot]
2025-08-29 09:58:54 +00:00
Author: https://github.com/AtkinsSJ
Commit: 277117eed5
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5955
Reviewed-by: https://github.com/trflynn89
6 changed files with 104 additions and 17 deletions
|
@ -9,8 +9,11 @@
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/CSS/CSSMathValue.h>
|
#include <LibWeb/CSS/CSSMathValue.h>
|
||||||
#include <LibWeb/CSS/CSSUnitValue.h>
|
#include <LibWeb/CSS/CSSUnitValue.h>
|
||||||
|
#include <LibWeb/CSS/MathFunctions.h>
|
||||||
#include <LibWeb/CSS/NumericType.h>
|
#include <LibWeb/CSS/NumericType.h>
|
||||||
#include <LibWeb/CSS/Serialize.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
|
#include <LibWeb/WebIDL/DOMException.h>
|
||||||
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
@ -131,4 +134,85 @@ GC::Ref<CSSNumericValue> rectify_a_numberish_value(JS::Realm& realm, CSSNumberis
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-typed-om-1/#reify-a-numeric-value
|
||||||
|
static WebIDL::ExceptionOr<GC::Ref<CSSNumericValue>> reify_a_numeric_value(JS::Realm& realm, Parser::ComponentValue const& numeric_value)
|
||||||
|
{
|
||||||
|
// To reify a numeric value num:
|
||||||
|
// 1. If num is a math function, reify a math expression from num and return the result.
|
||||||
|
if (numeric_value.is_function()) {
|
||||||
|
// AD-HOC: The only feasible way is to parse it as a StyleValue and rely on the reification code there.
|
||||||
|
auto parser = Parser::Parser::create(Parser::ParsingParams {}, {});
|
||||||
|
if (auto calculation = parser.parse_calculated_value(numeric_value)) {
|
||||||
|
auto reified = calculation->reify(realm, {});
|
||||||
|
// AD-HOC: Not all math functions can be reified. Until we have clear guidance on that, throw a SyntaxError.
|
||||||
|
// See: https://github.com/w3c/css-houdini-drafts/issues/1090#issuecomment-3200229996
|
||||||
|
if (auto* reified_numeric = as_if<CSSNumericValue>(*reified)) {
|
||||||
|
return GC::Ref { *reified_numeric };
|
||||||
|
}
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Unable to reify this math function."_utf16);
|
||||||
|
}
|
||||||
|
// AD-HOC: If we failed to parse it, I guess we throw a SyntaxError like in step 1 of CSSNumericValue::parse().
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Unable to parse input as a calculation tree."_utf16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If num is the unitless value 0 and num is a <dimension>, return a new CSSUnitValue with its value internal
|
||||||
|
// slot set to 0, and its unit internal slot set to "px".
|
||||||
|
// FIXME: What does this mean? We just have a component value, it doesn't have any knowledge about whether 0 should
|
||||||
|
// be interpreted as a dimension.
|
||||||
|
|
||||||
|
// 3. Return a new CSSUnitValue with its value internal slot set to the numeric value of num, and its unit internal
|
||||||
|
// slot set to "number" if num is a <number>, "percent" if num is a <percentage>, and num’s unit if num is a
|
||||||
|
// <dimension>.
|
||||||
|
// If the value being reified is a computed value, the unit used must be the appropriate canonical unit for the
|
||||||
|
// value’s type, with the numeric value scaled accordingly.
|
||||||
|
// NB: The computed value part is irrelevant here, I think.
|
||||||
|
if (numeric_value.is(Parser::Token::Type::Number))
|
||||||
|
return CSSUnitValue::create(realm, numeric_value.token().number_value(), "number"_fly_string);
|
||||||
|
if (numeric_value.is(Parser::Token::Type::Percentage))
|
||||||
|
return CSSUnitValue::create(realm, numeric_value.token().percentage(), "percent"_fly_string);
|
||||||
|
VERIFY(numeric_value.is(Parser::Token::Type::Dimension));
|
||||||
|
return CSSUnitValue::create(realm, numeric_value.token().dimension_value(), numeric_value.token().dimension_unit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-parse
|
||||||
|
WebIDL::ExceptionOr<GC::Ref<CSSNumericValue>> CSSNumericValue::parse(JS::VM& vm, String const& css_text)
|
||||||
|
{
|
||||||
|
// The parse(cssText) method, when called, must perform the following steps:
|
||||||
|
|
||||||
|
auto& realm = *vm.current_realm();
|
||||||
|
|
||||||
|
// 1. Parse a component value from cssText and let result be the result. If result is a syntax error, throw a
|
||||||
|
// SyntaxError and abort this algorithm.
|
||||||
|
auto maybe_component_value = Parser::Parser::create(Parser::ParsingParams {}, css_text).parse_as_component_value();
|
||||||
|
if (!maybe_component_value.has_value()) {
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Unable to parse input as a component value."_utf16);
|
||||||
|
}
|
||||||
|
auto& result = maybe_component_value.value();
|
||||||
|
|
||||||
|
// 2. If result is not a <number-token>, <percentage-token>, <dimension-token>, or a math function, throw a
|
||||||
|
// SyntaxError and abort this algorithm.
|
||||||
|
auto is_a_math_function = [](Parser::ComponentValue const& component_value) -> bool {
|
||||||
|
if (!component_value.is_function())
|
||||||
|
return false;
|
||||||
|
return math_function_from_string(component_value.function().name).has_value();
|
||||||
|
};
|
||||||
|
if (!(result.is(Parser::Token::Type::Number)
|
||||||
|
|| result.is(Parser::Token::Type::Percentage)
|
||||||
|
|| result.is(Parser::Token::Type::Dimension)
|
||||||
|
|| is_a_math_function(result))) {
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Input not a <number-token>, <percentage-token>, <dimension-token>, or a math function."_utf16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If result is a <dimension-token> and creating a type from result’s unit returns failure, throw a SyntaxError
|
||||||
|
// and abort this algorithm.
|
||||||
|
if (result.is(Parser::Token::Type::Dimension)) {
|
||||||
|
if (!NumericType::create_from_unit(result.token().dimension_unit()).has_value()) {
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Input is <dimension> with an unrecognized unit."_utf16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Reify a numeric value result, and return the result.
|
||||||
|
return reify_a_numeric_value(realm, result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ public:
|
||||||
virtual String to_string() const final override { return to_string({}); }
|
virtual String to_string() const final override { return to_string({}); }
|
||||||
String to_string(SerializationParams const&) const;
|
String to_string(SerializationParams const&) const;
|
||||||
|
|
||||||
|
static WebIDL::ExceptionOr<GC::Ref<CSSNumericValue>> parse(JS::VM&, String const& css_text);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit CSSNumericValue(JS::Realm&, NumericType);
|
explicit CSSNumericValue(JS::Realm&, NumericType);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ interface CSSNumericValue : CSSStyleValue {
|
||||||
// FIXME: CSSMathSum toSum(USVString... units);
|
// FIXME: CSSMathSum toSum(USVString... units);
|
||||||
[ImplementedAs=type_for_bindings] CSSNumericType type();
|
[ImplementedAs=type_for_bindings] CSSNumericType type();
|
||||||
|
|
||||||
[FIXME, Exposed=Window] static CSSNumericValue parse(USVString cssText);
|
[Exposed=Window] static CSSNumericValue parse(USVString cssText);
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish
|
// https://drafts.css-houdini.org/css-typed-om-1/#typedefdef-cssnumberish
|
||||||
|
|
|
@ -146,6 +146,8 @@ public:
|
||||||
|
|
||||||
NonnullRefPtr<StyleValue const> parse_with_a_syntax(Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element = {});
|
NonnullRefPtr<StyleValue const> parse_with_a_syntax(Vector<ComponentValue> const& input, SyntaxNode const& syntax, Optional<DOM::AbstractElement> const& element = {});
|
||||||
|
|
||||||
|
RefPtr<StyleValue const> parse_calculated_value(ComponentValue const&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser(ParsingParams const&, Vector<Token>);
|
Parser(ParsingParams const&, Vector<Token>);
|
||||||
|
|
||||||
|
@ -350,7 +352,6 @@ private:
|
||||||
};
|
};
|
||||||
Optional<PropertyAndValue> parse_css_value_for_properties(ReadonlySpan<PropertyID>, TokenStream<ComponentValue>&);
|
Optional<PropertyAndValue> parse_css_value_for_properties(ReadonlySpan<PropertyID>, TokenStream<ComponentValue>&);
|
||||||
RefPtr<StyleValue const> parse_builtin_value(TokenStream<ComponentValue>&);
|
RefPtr<StyleValue const> parse_builtin_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<StyleValue const> parse_calculated_value(ComponentValue const&);
|
|
||||||
Optional<FlyString> parse_custom_ident(TokenStream<ComponentValue>&, ReadonlySpan<StringView> blacklist);
|
Optional<FlyString> parse_custom_ident(TokenStream<ComponentValue>&, ReadonlySpan<StringView> blacklist);
|
||||||
RefPtr<CustomIdentStyleValue const> parse_custom_ident_value(TokenStream<ComponentValue>&, ReadonlySpan<StringView> blacklist);
|
RefPtr<CustomIdentStyleValue const> parse_custom_ident_value(TokenStream<ComponentValue>&, ReadonlySpan<StringView> blacklist);
|
||||||
Optional<FlyString> parse_dashed_ident(TokenStream<ComponentValue>&);
|
Optional<FlyString> parse_dashed_ident(TokenStream<ComponentValue>&);
|
||||||
|
|
|
@ -2,11 +2,11 @@ Harness status: OK
|
||||||
|
|
||||||
Found 6 tests
|
Found 6 tests
|
||||||
|
|
||||||
1 Pass
|
5 Pass
|
||||||
5 Fail
|
1 Fail
|
||||||
Fail Normalizing a <number> returns a number CSSUnitValue
|
Pass Normalizing a <number> returns a number CSSUnitValue
|
||||||
Fail Normalizing a <percentage> returns a percent CSSUnitValue
|
Pass Normalizing a <percentage> returns a percent CSSUnitValue
|
||||||
Fail Normalizing a <dimension> returns a CSSUnitValue with the correct unit
|
Pass Normalizing a <dimension> returns a CSSUnitValue with the correct unit
|
||||||
Fail Normalizing a <number> with a unitless zero returns 0
|
Pass Normalizing a <number> with a unitless zero returns 0
|
||||||
Fail Normalizing a <calc> returns simplified expression
|
Fail Normalizing a <calc> returns simplified expression
|
||||||
Pass Normalizing a <dimension> with a unitless zero returns 0px
|
Pass Normalizing a <dimension> with a unitless zero returns 0px
|
|
@ -2,14 +2,14 @@ Harness status: OK
|
||||||
|
|
||||||
Found 11 tests
|
Found 11 tests
|
||||||
|
|
||||||
2 Pass
|
8 Pass
|
||||||
9 Fail
|
3 Fail
|
||||||
Fail Parsing an invalid string throws SyntaxError
|
Pass Parsing an invalid string throws SyntaxError
|
||||||
Fail Parsing a string with a non numeric token throws SyntaxError
|
Pass Parsing a string with a non numeric token throws SyntaxError
|
||||||
Fail Parsing a string with left over numeric tokens throws SyntaxError
|
Pass Parsing a string with left over numeric tokens throws SyntaxError
|
||||||
Fail Parsing a calc with incompatible units throws a SyntaxError
|
Pass Parsing a calc with incompatible units throws a SyntaxError
|
||||||
Fail Parsing a <dimension-token> with invalid units throws a SyntaxError
|
Pass Parsing a <dimension-token> with invalid units throws a SyntaxError
|
||||||
Fail Parsing ignores surrounding spaces
|
Pass Parsing ignores surrounding spaces
|
||||||
Fail Parsing min() is successful
|
Fail Parsing min() is successful
|
||||||
Fail Parsing max() is successful
|
Fail Parsing max() is successful
|
||||||
Fail Parsing clamp() is successful
|
Fail Parsing clamp() is successful
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue