diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.cpp b/Libraries/LibWeb/CSS/CSSUnitValue.cpp index 43d72e901ba..1f233b0a5fa 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.cpp +++ b/Libraries/LibWeb/CSS/CSSUnitValue.cpp @@ -7,7 +7,19 @@ #include "CSSUnitValue.h" #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -285,4 +297,127 @@ Optional CSSUnitValue::create_a_sum_value() const return SumValue { SumValueItem { value, { { unit, 1 } } } }; } +// https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation +WebIDL::ExceptionOr> CSSUnitValue::create_an_internal_representation(PropertyNameAndID const& property) const +{ + // 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 any component of property’s CSS grammar has a limited numeric range, and the corresponding part of value + // is a CSSUnitValue that is outside of that range, replace that value with the result of wrapping it in a + // fresh CSSMathSum whose values internal slot contains only that part of value. + // + // Return the value. + + // NB: We store all custom properties as UnresolvedStyleValue, so we always need to create one here. + if (property.is_custom_property()) { + auto token = [this]() { + if (m_unit == "number"_fly_string) + return Parser::Token::create_number(Number { Number::Type::Number, m_value }); + if (m_unit == "percent"_fly_string) + return Parser::Token::create_percentage(Number { Number::Type::Number, m_value }); + return Parser::Token::create_dimension(m_value, m_unit); + }(); + return UnresolvedStyleValue::create({ Parser::ComponentValue { move(token) } }, Parser::SubstitutionFunctionsPresence {}); + } + + auto wrap_in_math_sum = [this, &property](auto&& value) -> NonnullRefPtr { + CalculationContext context { + .percentages_resolve_as = property_resolves_percentages_relative_to(property.id()), + .resolve_numbers_as_integers = property_accepts_type(property.id(), ValueType::Integer), + .accepted_type_ranges = property_accepted_type_ranges(property.id()), + }; + auto numeric_node = NumericCalculationNode::create(value, context); + auto math_sum_node = SumCalculationNode::create({ move(numeric_node) }); + return CalculatedStyleValue::create(move(math_sum_node), NumericType::create_from_unit(m_unit).release_value(), context); + }; + + if (m_unit == "number"_fly_string) { + // NB: Number before Integer, because a custom property accepts either and we want to avoid rounding in that case. + if (property_accepts_type(property.id(), ValueType::Number)) { + if (property_accepts_number(property.id(), m_value)) + return NumberStyleValue::create(m_value); + return wrap_in_math_sum(Number { Number::Type::Number, m_value }); + } + + if (property_accepts_type(property.id(), ValueType::Integer)) { + // NB: Same rounding as CalculatedStyleValue::resolve_integer(). Maybe this should go somewhere central? + auto integer = llround(m_value); + if (property_accepts_integer(property.id(), integer)) + return IntegerStyleValue::create(integer); + return wrap_in_math_sum(Number { Number::Type::Number, m_value }); + } + + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv }; + } + + if (m_unit == "percent"_fly_string) { + if (property_accepts_type(property.id(), ValueType::Percentage)) { + Percentage percentage { m_value }; + if (property_accepts_percentage(property.id(), percentage)) + return PercentageStyleValue::create(percentage); + return wrap_in_math_sum(percentage); + } + + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv }; + } + + auto dimension_type = dimension_for_unit(m_unit); + if (!dimension_type.has_value()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Unrecognized unit '{}'.", m_unit)) }; + + switch (*dimension_type) { + case DimensionType::Angle: + if (property_accepts_type(property.id(), ValueType::Angle)) { + Angle value { m_value, string_to_angle_unit(m_unit).release_value() }; + if (property_accepts_angle(property.id(), value)) + return AngleStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + case DimensionType::Flex: + if (property_accepts_type(property.id(), ValueType::Flex)) { + Flex value { m_value, string_to_flex_unit(m_unit).release_value() }; + if (property_accepts_flex(property.id(), value)) + return FlexStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + case DimensionType::Frequency: + if (property_accepts_type(property.id(), ValueType::Frequency)) { + Frequency value { m_value, string_to_frequency_unit(m_unit).release_value() }; + if (property_accepts_frequency(property.id(), value)) + return FrequencyStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + case DimensionType::Length: + if (property_accepts_type(property.id(), ValueType::Length)) { + Length value { m_value, string_to_length_unit(m_unit).release_value() }; + if (property_accepts_length(property.id(), value)) + return LengthStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + case DimensionType::Resolution: + if (property_accepts_type(property.id(), ValueType::Resolution)) { + Resolution value { m_value, string_to_resolution_unit(m_unit).release_value() }; + if (property_accepts_resolution(property.id(), value)) + return ResolutionStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + case DimensionType::Time: + if (property_accepts_type(property.id(), ValueType::Time)) { + Time value { m_value, string_to_time_unit(m_unit).release_value() }; + if (property_accepts_time(property.id(), value)) + return TimeStyleValue::create(value); + return wrap_in_math_sum(value); + } + break; + } + + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Property does not accept values of this type."sv }; +} + } diff --git a/Libraries/LibWeb/CSS/CSSUnitValue.h b/Libraries/LibWeb/CSS/CSSUnitValue.h index 1c7c8e0fddc..f7527955e03 100644 --- a/Libraries/LibWeb/CSS/CSSUnitValue.h +++ b/Libraries/LibWeb/CSS/CSSUnitValue.h @@ -35,6 +35,8 @@ public: virtual bool is_equal_numeric_value(GC::Ref other) const override; virtual Optional create_a_sum_value() const override; + virtual WebIDL::ExceptionOr> create_an_internal_representation(PropertyNameAndID const&) const override; + private: explicit CSSUnitValue(JS::Realm&, double value, FlyString unit, NumericType type); diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.txt index 6d9f33835a8..b42a24ec7a3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/declared.tentative.txt @@ -2,12 +2,12 @@ Harness status: OK Found 7 tests -5 Pass -2 Fail +6 Pass +1 Fail Fail Declared StylePropertyMap only contains properties in the style rule Pass Declared StylePropertyMap contains CSS property declarations in style rules Pass Declared StylePropertyMap does not contain inline styles Pass Declared StylePropertyMap contains custom property declarations Pass Declared StylePropertyMap does not contain properties with invalid values Pass Declared StylePropertyMap contains properties with their last valid value -Fail Declared StylePropertyMap is live \ No newline at end of file +Pass Declared StylePropertyMap is live \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/set.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/set.tentative.txt index c7899693bac..4d06aefd6f5 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/set.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/declared/set.tentative.txt @@ -2,8 +2,8 @@ Harness status: OK Found 14 tests -9 Pass -5 Fail +10 Pass +4 Fail Pass Setting a StylePropertyMap with an unsupported property name throws TypeError Pass Setting a StylePropertyMap with an null property name throws TypeError Pass Setting a StylePropertyMap with a descriptor throws TypeError @@ -13,7 +13,7 @@ Pass Setting a non list-valued property with multiple arguments throws TypeError Pass Setting a non list-valued property with list-valued string throws TypeError Pass Setting a list-valued property with a CSSUnparsedValue and other values throws TypeError Pass Setting a list-valued property with a var ref() and other values throws TypeError -Fail Setting a property with CSSStyleValue or String updates its value +Pass Setting a property with CSSStyleValue or String updates its value Fail Setting a list-valued property with CSSStyleValue or String updates its values Fail Setting a list-valued property with a list-valued string updates its value Fail Setting a custom property with CSSStyleValue or String updates its value diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/inline/set.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/inline/set.tentative.txt index c7899693bac..4d06aefd6f5 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/inline/set.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/inline/set.tentative.txt @@ -2,8 +2,8 @@ Harness status: OK Found 14 tests -9 Pass -5 Fail +10 Pass +4 Fail Pass Setting a StylePropertyMap with an unsupported property name throws TypeError Pass Setting a StylePropertyMap with an null property name throws TypeError Pass Setting a StylePropertyMap with a descriptor throws TypeError @@ -13,7 +13,7 @@ Pass Setting a non list-valued property with multiple arguments throws TypeError Pass Setting a non list-valued property with list-valued string throws TypeError Pass Setting a list-valued property with a CSSUnparsedValue and other values throws TypeError Pass Setting a list-valued property with a var ref() and other values throws TypeError -Fail Setting a property with CSSStyleValue or String updates its value +Pass Setting a property with CSSStyleValue or String updates its value Fail Setting a list-valued property with CSSStyleValue or String updates its values Fail Setting a list-valued property with a list-valued string updates its value Fail Setting a custom property with CSSStyleValue or String updates its value diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/cursor.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/cursor.txt index 24b295ae40b..31a44bb2724 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/cursor.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/cursor.txt @@ -2,8 +2,8 @@ Harness status: OK Found 69 tests -64 Pass -5 Fail +61 Pass +8 Fail Fail Can set 'cursor' to CSS-wide keywords: initial Fail Can set 'cursor' to CSS-wide keywords: inherit Fail Can set 'cursor' to CSS-wide keywords: unset @@ -64,9 +64,9 @@ Pass Setting 'cursor' to an angle: calc(0rad + 0deg) throws TypeError Pass Setting 'cursor' to a flexible length: 0fr throws TypeError Pass Setting 'cursor' to a flexible length: 1fr throws TypeError Pass Setting 'cursor' to a flexible length: -3.14fr throws TypeError -Pass Setting 'cursor' to a number: 0 throws TypeError -Pass Setting 'cursor' to a number: -3.14 throws TypeError -Pass Setting 'cursor' to a number: 3.14 throws TypeError +Fail Setting 'cursor' to a number: 0 throws TypeError +Fail Setting 'cursor' to a number: -3.14 throws TypeError +Fail Setting 'cursor' to a number: 3.14 throws TypeError Pass Setting 'cursor' to a number: calc(2 + 3) throws TypeError Pass Setting 'cursor' to a transform: translate(50%, 50%) throws TypeError Pass Setting 'cursor' to a transform: perspective(10em) throws TypeError diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/line-height.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/line-height.txt index 6394e2bb158..6ce981b7f00 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/line-height.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/line-height.txt @@ -2,25 +2,25 @@ Harness status: OK Found 32 tests -15 Pass -17 Fail +22 Pass +10 Fail Fail Can set 'line-height' to CSS-wide keywords: initial Fail Can set 'line-height' to CSS-wide keywords: inherit Fail Can set 'line-height' to CSS-wide keywords: unset Fail Can set 'line-height' to CSS-wide keywords: revert Fail Can set 'line-height' to var() references: var(--A) Pass Can set 'line-height' to the 'normal' keyword: normal -Fail Can set 'line-height' to a length: 0px -Fail Can set 'line-height' to a length: -3.14em -Fail Can set 'line-height' to a length: 3.14cm +Pass Can set 'line-height' to a length: 0px +Pass Can set 'line-height' to a length: -3.14em +Pass Can set 'line-height' to a length: 3.14cm Fail Can set 'line-height' to a length: calc(0px + 0em) Fail Can set 'line-height' to a number: 0 Fail Can set 'line-height' to a number: -3.14 -Fail Can set 'line-height' to a number: 3.14 +Pass Can set 'line-height' to a number: 3.14 Fail Can set 'line-height' to a number: calc(2 + 3) -Fail Can set 'line-height' to a percent: 0% -Fail Can set 'line-height' to a percent: -3.14% -Fail Can set 'line-height' to a percent: 3.14% +Pass Can set 'line-height' to a percent: 0% +Pass Can set 'line-height' to a percent: -3.14% +Pass Can set 'line-height' to a percent: 3.14% Fail Can set 'line-height' to a percent: calc(0% + 0%) Pass Setting 'line-height' to a time: 0s throws TypeError Pass Setting 'line-height' to a time: -3.14ms throws TypeError diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/padding.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/padding.txt index b03c09ad4b2..aaf954285a3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/padding.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/padding.txt @@ -2,20 +2,20 @@ Harness status: OK Found 124 tests -72 Pass -52 Fail +92 Pass +32 Fail Fail Can set 'padding-top' to CSS-wide keywords: initial Fail Can set 'padding-top' to CSS-wide keywords: inherit Fail Can set 'padding-top' to CSS-wide keywords: unset Fail Can set 'padding-top' to CSS-wide keywords: revert Fail Can set 'padding-top' to var() references: var(--A) -Fail Can set 'padding-top' to a percent: 0% +Pass Can set 'padding-top' to a percent: 0% Fail Can set 'padding-top' to a percent: -3.14% -Fail Can set 'padding-top' to a percent: 3.14% +Pass Can set 'padding-top' to a percent: 3.14% Fail Can set 'padding-top' to a percent: calc(0% + 0%) -Fail Can set 'padding-top' to a length: 0px -Fail Can set 'padding-top' to a length: -3.14em -Fail Can set 'padding-top' to a length: 3.14cm +Pass Can set 'padding-top' to a length: 0px +Pass Can set 'padding-top' to a length: -3.14em +Pass Can set 'padding-top' to a length: 3.14cm Fail Can set 'padding-top' to a length: calc(0px + 0em) Pass Setting 'padding-top' to a time: 0s throws TypeError Pass Setting 'padding-top' to a time: -3.14ms throws TypeError @@ -40,13 +40,13 @@ Fail Can set 'padding-left' to CSS-wide keywords: inherit Fail Can set 'padding-left' to CSS-wide keywords: unset Fail Can set 'padding-left' to CSS-wide keywords: revert Fail Can set 'padding-left' to var() references: var(--A) -Fail Can set 'padding-left' to a percent: 0% +Pass Can set 'padding-left' to a percent: 0% Fail Can set 'padding-left' to a percent: -3.14% -Fail Can set 'padding-left' to a percent: 3.14% +Pass Can set 'padding-left' to a percent: 3.14% Fail Can set 'padding-left' to a percent: calc(0% + 0%) -Fail Can set 'padding-left' to a length: 0px -Fail Can set 'padding-left' to a length: -3.14em -Fail Can set 'padding-left' to a length: 3.14cm +Pass Can set 'padding-left' to a length: 0px +Pass Can set 'padding-left' to a length: -3.14em +Pass Can set 'padding-left' to a length: 3.14cm Fail Can set 'padding-left' to a length: calc(0px + 0em) Pass Setting 'padding-left' to a time: 0s throws TypeError Pass Setting 'padding-left' to a time: -3.14ms throws TypeError @@ -71,13 +71,13 @@ Fail Can set 'padding-right' to CSS-wide keywords: inherit Fail Can set 'padding-right' to CSS-wide keywords: unset Fail Can set 'padding-right' to CSS-wide keywords: revert Fail Can set 'padding-right' to var() references: var(--A) -Fail Can set 'padding-right' to a percent: 0% +Pass Can set 'padding-right' to a percent: 0% Fail Can set 'padding-right' to a percent: -3.14% -Fail Can set 'padding-right' to a percent: 3.14% +Pass Can set 'padding-right' to a percent: 3.14% Fail Can set 'padding-right' to a percent: calc(0% + 0%) -Fail Can set 'padding-right' to a length: 0px -Fail Can set 'padding-right' to a length: -3.14em -Fail Can set 'padding-right' to a length: 3.14cm +Pass Can set 'padding-right' to a length: 0px +Pass Can set 'padding-right' to a length: -3.14em +Pass Can set 'padding-right' to a length: 3.14cm Fail Can set 'padding-right' to a length: calc(0px + 0em) Pass Setting 'padding-right' to a time: 0s throws TypeError Pass Setting 'padding-right' to a time: -3.14ms throws TypeError @@ -102,13 +102,13 @@ Fail Can set 'padding-bottom' to CSS-wide keywords: inherit Fail Can set 'padding-bottom' to CSS-wide keywords: unset Fail Can set 'padding-bottom' to CSS-wide keywords: revert Fail Can set 'padding-bottom' to var() references: var(--A) -Fail Can set 'padding-bottom' to a percent: 0% +Pass Can set 'padding-bottom' to a percent: 0% Fail Can set 'padding-bottom' to a percent: -3.14% -Fail Can set 'padding-bottom' to a percent: 3.14% +Pass Can set 'padding-bottom' to a percent: 3.14% Fail Can set 'padding-bottom' to a percent: calc(0% + 0%) -Fail Can set 'padding-bottom' to a length: 0px -Fail Can set 'padding-bottom' to a length: -3.14em -Fail Can set 'padding-bottom' to a length: 3.14cm +Pass Can set 'padding-bottom' to a length: 0px +Pass Can set 'padding-bottom' to a length: -3.14em +Pass Can set 'padding-bottom' to a length: 3.14cm Fail Can set 'padding-bottom' to a length: calc(0px + 0em) Pass Setting 'padding-bottom' to a time: 0s throws TypeError Pass Setting 'padding-bottom' to a time: -3.14ms throws TypeError diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/width.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/width.txt index 5b9182f7f13..352060e66b2 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/width.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/the-stylepropertymap/properties/width.txt @@ -2,21 +2,21 @@ Harness status: OK Found 95 tests -56 Pass -39 Fail +71 Pass +24 Fail Fail Can set 'width' to CSS-wide keywords: initial Fail Can set 'width' to CSS-wide keywords: inherit Fail Can set 'width' to CSS-wide keywords: unset Fail Can set 'width' to CSS-wide keywords: revert Fail Can set 'width' to var() references: var(--A) Pass Can set 'width' to the 'auto' keyword: auto -Fail Can set 'width' to a percent: 0% +Pass Can set 'width' to a percent: 0% Fail Can set 'width' to a percent: -3.14% -Fail Can set 'width' to a percent: 3.14% +Pass Can set 'width' to a percent: 3.14% Fail Can set 'width' to a percent: calc(0% + 0%) -Fail Can set 'width' to a length: 0px -Fail Can set 'width' to a length: -3.14em -Fail Can set 'width' to a length: 3.14cm +Pass Can set 'width' to a length: 0px +Pass Can set 'width' to a length: -3.14em +Pass Can set 'width' to a length: 3.14cm Fail Can set 'width' to a length: calc(0px + 0em) Pass Setting 'width' to a time: 0s throws TypeError Pass Setting 'width' to a time: -3.14ms throws TypeError @@ -41,13 +41,13 @@ Fail Can set 'min-width' to CSS-wide keywords: inherit Fail Can set 'min-width' to CSS-wide keywords: unset Fail Can set 'min-width' to CSS-wide keywords: revert Fail Can set 'min-width' to var() references: var(--A) -Fail Can set 'min-width' to a percent: 0% +Pass Can set 'min-width' to a percent: 0% Fail Can set 'min-width' to a percent: -3.14% -Fail Can set 'min-width' to a percent: 3.14% +Pass Can set 'min-width' to a percent: 3.14% Fail Can set 'min-width' to a percent: calc(0% + 0%) -Fail Can set 'min-width' to a length: 0px -Fail Can set 'min-width' to a length: -3.14em -Fail Can set 'min-width' to a length: 3.14cm +Pass Can set 'min-width' to a length: 0px +Pass Can set 'min-width' to a length: -3.14em +Pass Can set 'min-width' to a length: 3.14cm Fail Can set 'min-width' to a length: calc(0px + 0em) Pass Setting 'min-width' to a time: 0s throws TypeError Pass Setting 'min-width' to a time: -3.14ms throws TypeError @@ -73,13 +73,13 @@ Fail Can set 'max-width' to CSS-wide keywords: unset Fail Can set 'max-width' to CSS-wide keywords: revert Fail Can set 'max-width' to var() references: var(--A) Pass Can set 'max-width' to the 'none' keyword: none -Fail Can set 'max-width' to a percent: 0% +Pass Can set 'max-width' to a percent: 0% Fail Can set 'max-width' to a percent: -3.14% -Fail Can set 'max-width' to a percent: 3.14% +Pass Can set 'max-width' to a percent: 3.14% Fail Can set 'max-width' to a percent: calc(0% + 0%) -Fail Can set 'max-width' to a length: 0px -Fail Can set 'max-width' to a length: -3.14em -Fail Can set 'max-width' to a length: 3.14cm +Pass Can set 'max-width' to a length: 0px +Pass Can set 'max-width' to a length: -3.14em +Pass Can set 'max-width' to a length: 3.14cm Fail Can set 'max-width' to a length: calc(0px + 0em) Pass Setting 'max-width' to a time: 0s throws TypeError Pass Setting 'max-width' to a time: -3.14ms throws TypeError