LibWeb/CSS: Add flag to disable create-internal-rep type checking

CSSTransformComponents hold other CSSStyleValues as their parameters. We
want to be able to create internal representations from those parameters
without them caring if they would be valid when directly assigned to the
property.

This is a temporary solution to make transform functions work. To be
completely correct, we need to know what is allowed in that context,
along with value ranges - a combination of the contexts we create when
parsing, and when computing calculations. For transform functions, this
doesn't matter, as there's no limit to the range of allowed values.
This commit is contained in:
Sam Atkins 2025-10-13 16:37:54 +01:00
parent 35fd3bda79
commit 5178d1ebe3
Notes: github-actions[bot] 2025-10-14 12:42:59 +00:00
13 changed files with 52 additions and 18 deletions

View file

@ -40,7 +40,7 @@ WebIDL::ExceptionOr<String> CSSImageValue::to_string() const
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSImageValue::create_an_internal_representation(PropertyNameAndID const& property) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSImageValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{ {
// If value is a CSSStyleValue subclass, // If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError. // If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
@ -53,7 +53,7 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSImageValue::create_an_in
} }
return property_accepts_type(property.id(), ValueType::Image); return property_accepts_type(property.id(), ValueType::Image);
}(); }();
if (!matches_grammar) { if (perform_type_check == PerformTypeCheck::Yes && !matches_grammar) {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Property '{}' does not accept <image>", property.name())) }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Property '{}' does not accept <image>", property.name())) };
} }

View file

@ -21,7 +21,7 @@ public:
virtual ~CSSImageValue() override = default; virtual ~CSSImageValue() override = default;
virtual WebIDL::ExceptionOr<String> to_string() const override; virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const override; virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private: private:
explicit CSSImageValue(JS::Realm&, NonnullRefPtr<StyleValue const> source_value); explicit CSSImageValue(JS::Realm&, NonnullRefPtr<StyleValue const> source_value);

View file

@ -68,7 +68,7 @@ WebIDL::ExceptionOr<String> CSSKeywordValue::to_string() const
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSKeywordValue::create_an_internal_representation(PropertyNameAndID const& property) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSKeywordValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{ {
// If value is a CSSStyleValue subclass, // If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError. // If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
@ -87,7 +87,7 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSKeywordValue::create_an_
auto keyword = keyword_from_string(m_value); auto keyword = keyword_from_string(m_value);
return keyword.has_value() && property_accepts_keyword(property.id(), keyword.value()); return keyword.has_value() && property_accepts_keyword(property.id(), keyword.value());
}(); }();
if (!matches_grammar) { if (perform_type_check == PerformTypeCheck::Yes && !matches_grammar) {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Property '{}' does not accept the keyword '{}'", property.name(), m_value)) }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Property '{}' does not accept the keyword '{}'", property.name(), m_value)) };
} }

View file

@ -29,7 +29,7 @@ public:
WebIDL::ExceptionOr<void> set_value(FlyString value); WebIDL::ExceptionOr<void> set_value(FlyString value);
virtual WebIDL::ExceptionOr<String> to_string() const override; virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const override; virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private: private:
explicit CSSKeywordValue(JS::Realm&, FlyString value); explicit CSSKeywordValue(JS::Realm&, FlyString value);

View file

@ -26,7 +26,7 @@ void CSSMathValue::initialize(JS::Realm& realm)
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSMathValue::create_an_internal_representation(PropertyNameAndID const& property) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSMathValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{ {
// If value is a CSSStyleValue subclass, // If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError. // If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
@ -59,7 +59,7 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSMathValue::create_an_int
return false; return false;
}(); }();
if (!matches) if (perform_type_check == PerformTypeCheck::Yes && !matches)
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv };
return CalculatedStyleValue::create(TRY(create_calculation_node(context)), type(), move(context)); return CalculatedStyleValue::create(TRY(create_calculation_node(context)), type(), move(context));

View file

@ -32,7 +32,7 @@ public:
}; };
virtual String serialize_math_value(Nested, Parens) const = 0; virtual String serialize_math_value(Nested, Parens) const = 0;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const final override; virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const final override;
protected: protected:
explicit CSSMathValue(JS::Realm&, Bindings::CSSMathOperator, NumericType); explicit CSSMathValue(JS::Realm&, Bindings::CSSMathOperator, NumericType);

View file

@ -123,7 +123,7 @@ WebIDL::ExceptionOr<String> CSSStyleValue::to_string() const
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSStyleValue::create_an_internal_representation(PropertyNameAndID const&) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSStyleValue::create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const
{ {
// If value is a direct CSSStyleValue, // If value is a direct CSSStyleValue,
// Return values associated value. // Return values associated value.

View file

@ -37,7 +37,12 @@ public:
virtual WebIDL::ExceptionOr<String> to_string() const; virtual WebIDL::ExceptionOr<String> to_string() const;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const; // FIXME: Temporary hack. Really we want to pass something like a CalculationContext with the valid types and ranges.
enum class PerformTypeCheck : u8 {
No,
Yes,
};
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const;
protected: protected:
explicit CSSStyleValue(JS::Realm&); explicit CSSStyleValue(JS::Realm&);

View file

@ -326,7 +326,7 @@ static Optional<CalculationNode::NumericValue> create_numeric_value(double value
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnitValue::create_an_internal_representation(PropertyNameAndID const& property) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnitValue::create_an_internal_representation(PropertyNameAndID const& property, PerformTypeCheck perform_type_check) const
{ {
// If value is a CSSStyleValue subclass, // If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError. // If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.
@ -338,7 +338,7 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnitValue::create_an_int
// Return the value. // Return the value.
// NB: We store all custom properties as UnresolvedStyleValue, so we always need to create one here. // NB: We store all custom properties as UnresolvedStyleValue, so we always need to create one here.
if (property.is_custom_property()) { if (perform_type_check == PerformTypeCheck::Yes && property.is_custom_property()) {
auto token = [this]() { auto token = [this]() {
if (m_unit == "number"_fly_string) if (m_unit == "number"_fly_string)
return Parser::Token::create_number(Number { Number::Type::Number, m_value }); return Parser::Token::create_number(Number { Number::Type::Number, m_value });
@ -361,6 +361,35 @@ WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnitValue::create_an_int
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unrecognized unit '{}'.", m_unit)) }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unrecognized unit '{}'.", m_unit)) };
} }
if (perform_type_check == PerformTypeCheck::No) {
return value->visit(
[&](Number const& number) -> RefPtr<StyleValue const> {
return NumberStyleValue::create(number.value());
},
[&](Percentage const& percentage) -> RefPtr<StyleValue const> {
return PercentageStyleValue::create(percentage);
},
[&](Angle const& angle) -> RefPtr<StyleValue const> {
return AngleStyleValue::create(angle);
},
[&](Flex const& flex) -> RefPtr<StyleValue const> {
return FlexStyleValue::create(flex);
},
[&](Frequency const& frequency) -> RefPtr<StyleValue const> {
return FrequencyStyleValue::create(frequency);
},
[&](Length const& length) -> RefPtr<StyleValue const> {
return LengthStyleValue::create(length);
},
[&](Resolution const& resolution) -> RefPtr<StyleValue const> {
return ResolutionStyleValue::create(resolution);
},
[&](Time const& time) -> RefPtr<StyleValue const> {
return TimeStyleValue::create(time);
})
.release_nonnull();
}
// FIXME: Check types allowed by registered custom properties. // FIXME: Check types allowed by registered custom properties.
auto style_value = value->visit( auto style_value = value->visit(
[&](Number const& number) -> RefPtr<StyleValue const> { [&](Number const& number) -> RefPtr<StyleValue const> {

View file

@ -35,7 +35,7 @@ public:
virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override; virtual bool is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const override;
virtual Optional<SumValue> create_a_sum_value() const override; virtual Optional<SumValue> create_a_sum_value() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const override; virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<CalculationNode const>> create_calculation_node(CalculationContext const&) const override; virtual WebIDL::ExceptionOr<NonnullRefPtr<CalculationNode const>> create_calculation_node(CalculationContext const&) const override;
private: private:

View file

@ -168,7 +168,7 @@ WebIDL::ExceptionOr<String> CSSUnparsedValue::to_string() const
} }
// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation // https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnparsedValue::create_an_internal_representation(PropertyNameAndID const&) const WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> CSSUnparsedValue::create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const
{ {
// If value is a CSSStyleValue subclass, // If value is a CSSStyleValue subclass,
// If value does not match the grammar of a list-valued property iteration of property, throw a TypeError. // If value does not match the grammar of a list-valued property iteration of property, throw a TypeError.

View file

@ -31,7 +31,7 @@ public:
virtual WebIDL::ExceptionOr<void> set_value_of_new_indexed_property(u32, JS::Value) override; virtual WebIDL::ExceptionOr<void> set_value_of_new_indexed_property(u32, JS::Value) override;
virtual WebIDL::ExceptionOr<String> to_string() const override; virtual WebIDL::ExceptionOr<String> to_string() const override;
virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&) const override; virtual WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_representation(PropertyNameAndID const&, PerformTypeCheck) const override;
private: private:
explicit CSSUnparsedValue(JS::Realm&, Vector<CSSUnparsedSegment>); explicit CSSUnparsedValue(JS::Realm&, Vector<CSSUnparsedSegment>);

View file

@ -62,7 +62,7 @@ static WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_r
// To create an internal representation, given a string property and a string or CSSStyleValue value: // To create an internal representation, given a string property and a string or CSSStyleValue value:
return value.visit( return value.visit(
[&property](GC::Root<CSSStyleValue> const& css_style_value) { [&property](GC::Root<CSSStyleValue> const& css_style_value) {
return css_style_value->create_an_internal_representation(property); return css_style_value->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::Yes);
}, },
[&](String const& css_text) -> WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> { [&](String const& css_text) -> WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> {
// If value is a USVString, // If value is a USVString,
@ -71,7 +71,7 @@ static WebIDL::ExceptionOr<NonnullRefPtr<StyleValue const>> create_an_internal_r
// FIXME: Avoid passing name as a string, as it gets immediately converted back to PropertyNameAndID. // FIXME: Avoid passing name as a string, as it gets immediately converted back to PropertyNameAndID.
auto result = TRY(CSSStyleValue::parse_a_css_style_value(vm, property.name(), css_text, CSSStyleValue::ParseMultiple::No)); auto result = TRY(CSSStyleValue::parse_a_css_style_value(vm, property.name(), css_text, CSSStyleValue::ParseMultiple::No));
// AD-HOC: Result is a CSSStyleValue but we want an internal representation, so... convert it again I guess? // AD-HOC: Result is a CSSStyleValue but we want an internal representation, so... convert it again I guess?
return result.get<GC::Ref<CSSStyleValue>>()->create_an_internal_representation(property); return result.get<GC::Ref<CSSStyleValue>>()->create_an_internal_representation(property, CSSStyleValue::PerformTypeCheck::Yes);
}); });
} }