diff --git a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp index 2fb5c502838..338598256e4 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp @@ -10,6 +10,16 @@ #include "CalculatedStyleValue.h" #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -190,6 +200,18 @@ static CalculationNode::NumericValue clamp_and_censor_numeric_value(NumericCalcu }); } +static GC::Ptr reify_children(JS::Realm& realm, ReadonlySpan> children) +{ + GC::RootVector> reified_children { realm.heap() }; + for (auto const& child : children) { + auto reified_child = child->reify(realm); + if (!reified_child) + return nullptr; + reified_children.append(reified_child.as_nonnull()); + } + return CSSNumericArray::create(realm, move(reified_children)); +} + static String serialize_a_calculation_tree(CalculationNode const&, CalculationContext const&, SerializationMode); // https://drafts.csswg.org/css-values-4/#serialize-a-math-function @@ -803,6 +825,14 @@ bool NumericCalculationNode::equals(CalculationNode const& other) const return m_value == static_cast(other).m_value; } +GC::Ptr NumericCalculationNode::reify(JS::Realm& realm) const +{ + return m_value.visit( + [&realm](Number const& number) { return CSSUnitValue::create(realm, number.value(), "number"_fly_string); }, + [&realm](Percentage const& percentage) { return CSSUnitValue::create(realm, percentage.value(), "percent"_fly_string); }, + [&realm](auto const& dimension) { return CSSUnitValue::create(realm, dimension.raw_value(), FlyString::from_utf8_without_validation(dimension.unit_name().bytes())); }); +} + NonnullRefPtr SumCalculationNode::create(Vector> values) { // https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation @@ -874,6 +904,14 @@ bool SumCalculationNode::equals(CalculationNode const& other) const return true; } +GC::Ptr SumCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_children = reify_children(realm, m_values); + if (!reified_children) + return nullptr; + return CSSMathSum::create(realm, numeric_type().value(), reified_children.as_nonnull()); +} + NonnullRefPtr ProductCalculationNode::create(Vector> values) { // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation @@ -944,6 +982,14 @@ bool ProductCalculationNode::equals(CalculationNode const& other) const return true; } +GC::Ptr ProductCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_children = reify_children(realm, m_values); + if (!reified_children) + return nullptr; + return CSSMathProduct::create(realm, numeric_type().value(), reified_children.as_nonnull()); +} + NonnullRefPtr NegateCalculationNode::create(NonnullRefPtr value) { return adopt_ref(*new (nothrow) NegateCalculationNode(move(value))); @@ -990,6 +1036,14 @@ bool NegateCalculationNode::equals(CalculationNode const& other) const return m_value->equals(*static_cast(other).m_value); } +GC::Ptr NegateCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_value = m_value->reify(realm); + if (!reified_value) + return nullptr; + return CSSMathNegate::create(realm, numeric_type().value(), reified_value.as_nonnull()); +} + NonnullRefPtr InvertCalculationNode::create(NonnullRefPtr value) { // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation @@ -1042,6 +1096,14 @@ bool InvertCalculationNode::equals(CalculationNode const& other) const return m_value->equals(*static_cast(other).m_value); } +GC::Ptr InvertCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_value = m_value->reify(realm); + if (!reified_value) + return nullptr; + return CSSMathInvert::create(realm, numeric_type().value(), reified_value.as_nonnull()); +} + NonnullRefPtr MinCalculationNode::create(Vector> values) { // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation @@ -1166,6 +1228,14 @@ bool MinCalculationNode::equals(CalculationNode const& other) const return true; } +GC::Ptr MinCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_children = reify_children(realm, m_values); + if (!reified_children) + return nullptr; + return CSSMathMin::create(realm, numeric_type().value(), reified_children.as_nonnull()); +} + NonnullRefPtr MaxCalculationNode::create(Vector> values) { // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation @@ -1243,6 +1313,14 @@ bool MaxCalculationNode::equals(CalculationNode const& other) const return true; } +GC::Ptr MaxCalculationNode::reify(JS::Realm& realm) const +{ + auto reified_children = reify_children(realm, m_values); + if (!reified_children) + return nullptr; + return CSSMathMax::create(realm, numeric_type().value(), reified_children.as_nonnull()); +} + NonnullRefPtr ClampCalculationNode::create(NonnullRefPtr min, NonnullRefPtr center, NonnullRefPtr max) { // https://drafts.csswg.org/css-values-4/#determine-the-type-of-a-calculation @@ -1358,6 +1436,17 @@ bool ClampCalculationNode::equals(CalculationNode const& other) const && m_max_value->equals(*static_cast(other).m_max_value); } +GC::Ptr ClampCalculationNode::reify(JS::Realm& realm) const +{ + auto lower = m_min_value->reify(realm); + auto value = m_center_value->reify(realm); + auto upper = m_max_value->reify(realm); + if (!lower || !value || !upper) + return nullptr; + + return CSSMathClamp::create(realm, numeric_type().value(), lower.as_nonnull(), value.as_nonnull(), upper.as_nonnull()); +} + NonnullRefPtr AbsCalculationNode::create(NonnullRefPtr value) { return adopt_ref(*new (nothrow) AbsCalculationNode(move(value))); @@ -3037,6 +3126,18 @@ String CalculatedStyleValue::dump() const return builder.to_string_without_validation(); } +// https://drafts.css-houdini.org/css-typed-om-1/#reify-a-math-expression +GC::Ref CalculatedStyleValue::reify(JS::Realm& realm, String const& associated_property) const +{ + // NB: This spec algorithm isn't really implementable here - it's incomplete, and assumes we don't already have a + // calculation tree. So we have a per-node method instead. + if (auto reified = m_calculation->reify(realm)) + return *reified; + // Some math functions are not reifiable yet. If we contain one, we have to fall back to CSSStyleValue. + // https://github.com/w3c/css-houdini-drafts/issues/1090 + return StyleValue::reify(realm, associated_property); +} + struct NumericChildAndIndex { NonnullRefPtr child; size_t index; diff --git a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h index d227a51e3b3..d27dd8f6a52 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h @@ -117,6 +117,8 @@ public: String dump() const; + virtual GC::Ref reify(JS::Realm&, String const& associated_property) const override; + private: explicit CalculatedStyleValue(NonnullRefPtr calculation, NumericType resolved_type, CalculationContext context) : StyleValue(Type::Calculated) @@ -249,6 +251,7 @@ public: virtual void dump(StringBuilder&, int indent) const = 0; virtual bool equals(CalculationNode const&) const = 0; + virtual GC::Ptr reify(JS::Realm&) const { return nullptr; } protected: CalculationNode(Type, Optional); @@ -287,6 +290,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: NumericCalculationNode(NumericValue, NumericType); @@ -306,6 +310,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: SumCalculationNode(Vector>, Optional); @@ -325,6 +330,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: ProductCalculationNode(Vector>, Optional); @@ -345,6 +351,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: explicit NegateCalculationNode(NonnullRefPtr); @@ -365,6 +372,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: InvertCalculationNode(NonnullRefPtr, Optional); @@ -385,6 +393,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: MinCalculationNode(Vector>, Optional); @@ -405,6 +414,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: MaxCalculationNode(Vector>, Optional); @@ -425,6 +435,7 @@ public: virtual void dump(StringBuilder&, int indent) const override; virtual bool equals(CalculationNode const&) const override; + virtual GC::Ptr reify(JS::Realm&) const override; private: ClampCalculationNode(NonnullRefPtr, NonnullRefPtr, NonnullRefPtr, Optional); diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/parse-calc-expressions.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/parse-calc-expressions.txt index 8c57aa85752..4aa49626b32 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/parse-calc-expressions.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/parse-calc-expressions.txt @@ -2,6 +2,6 @@ Harness status: OK Found 2 tests -2 Fail -Fail Parsing calc(1% + 2em + 3px) -Fail Parsing calc(1px + 2% + 3em) \ No newline at end of file +2 Pass +Pass Parsing calc(1% + 2em + 3px) +Pass Parsing calc(1px + 2% + 3em) \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-normalization/normalize-numeric.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-normalization/normalize-numeric.tentative.txt index e8cda78920f..7ded96eebb4 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-normalization/normalize-numeric.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-normalization/normalize-numeric.tentative.txt @@ -2,11 +2,10 @@ Harness status: OK Found 6 tests -5 Pass -1 Fail +6 Pass Pass Normalizing a returns a number CSSUnitValue Pass Normalizing a returns a percent CSSUnitValue Pass Normalizing a returns a CSSUnitValue with the correct unit Pass Normalizing a with a unitless zero returns 0 -Fail Normalizing a returns simplified expression +Pass Normalizing a returns simplified expression Pass Normalizing a with a unitless zero returns 0px \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt index 201387869c4..004c98c4712 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-typed-om/stylevalue-subclasses/numeric-objects/parse.tentative.txt @@ -2,16 +2,15 @@ Harness status: OK Found 11 tests -8 Pass -3 Fail +11 Pass Pass Parsing an invalid string throws SyntaxError Pass Parsing a string with a non numeric token throws SyntaxError Pass Parsing a string with left over numeric tokens throws SyntaxError Pass Parsing a calc with incompatible units throws a SyntaxError Pass Parsing a with invalid units throws a SyntaxError Pass Parsing ignores surrounding spaces -Fail Parsing min() is successful -Fail Parsing max() is successful -Fail Parsing clamp() is successful +Pass Parsing min() is successful +Pass Parsing max() is successful +Pass Parsing clamp() is successful Pass Parsing sum of multiple min() is successful Pass Parsing product of multiple min() is successful \ No newline at end of file