mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-21 16:58:58 +00:00
LibWeb/CSS: Reify math functions into CSSMathValue types
This commit is contained in:
parent
9264f540dd
commit
d29084997e
Notes:
github-actions[bot]
2025-08-29 09:58:30 +00:00
Author: https://github.com/AtkinsSJ
Commit: d29084997e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5955
Reviewed-by: https://github.com/trflynn89
5 changed files with 121 additions and 11 deletions
|
@ -10,6 +10,16 @@
|
|||
#include "CalculatedStyleValue.h"
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/CSS/CSSMathClamp.h>
|
||||
#include <LibWeb/CSS/CSSMathInvert.h>
|
||||
#include <LibWeb/CSS/CSSMathMax.h>
|
||||
#include <LibWeb/CSS/CSSMathMin.h>
|
||||
#include <LibWeb/CSS/CSSMathNegate.h>
|
||||
#include <LibWeb/CSS/CSSMathProduct.h>
|
||||
#include <LibWeb/CSS/CSSMathSum.h>
|
||||
#include <LibWeb/CSS/CSSNumericArray.h>
|
||||
#include <LibWeb/CSS/CSSUnitValue.h>
|
||||
#include <LibWeb/CSS/Percentage.h>
|
||||
#include <LibWeb/CSS/PropertyID.h>
|
||||
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
||||
|
@ -190,6 +200,18 @@ static CalculationNode::NumericValue clamp_and_censor_numeric_value(NumericCalcu
|
|||
});
|
||||
}
|
||||
|
||||
static GC::Ptr<CSSNumericArray> reify_children(JS::Realm& realm, ReadonlySpan<NonnullRefPtr<CalculationNode const>> children)
|
||||
{
|
||||
GC::RootVector<GC::Ref<CSSNumericValue>> 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<NumericCalculationNode const&>(other).m_value;
|
||||
}
|
||||
|
||||
GC::Ptr<CSSNumericValue> 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 const> SumCalculationNode::create(Vector<NonnullRefPtr<CalculationNode const>> 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<CSSNumericValue> 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 const> ProductCalculationNode::create(Vector<NonnullRefPtr<CalculationNode const>> 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<CSSNumericValue> 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 const> NegateCalculationNode::create(NonnullRefPtr<CalculationNode const> 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<NegateCalculationNode const&>(other).m_value);
|
||||
}
|
||||
|
||||
GC::Ptr<CSSNumericValue> 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 const> InvertCalculationNode::create(NonnullRefPtr<CalculationNode const> 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<InvertCalculationNode const&>(other).m_value);
|
||||
}
|
||||
|
||||
GC::Ptr<CSSNumericValue> 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 const> MinCalculationNode::create(Vector<NonnullRefPtr<CalculationNode const>> 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<CSSNumericValue> 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 const> MaxCalculationNode::create(Vector<NonnullRefPtr<CalculationNode const>> 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<CSSNumericValue> 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 const> ClampCalculationNode::create(NonnullRefPtr<CalculationNode const> min, NonnullRefPtr<CalculationNode const> center, NonnullRefPtr<CalculationNode const> 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<ClampCalculationNode const&>(other).m_max_value);
|
||||
}
|
||||
|
||||
GC::Ptr<CSSNumericValue> 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 const> AbsCalculationNode::create(NonnullRefPtr<CalculationNode const> 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<CSSStyleValue> 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<NumericCalculationNode const> child;
|
||||
size_t index;
|
||||
|
|
|
@ -117,6 +117,8 @@ public:
|
|||
|
||||
String dump() const;
|
||||
|
||||
virtual GC::Ref<CSSStyleValue> reify(JS::Realm&, String const& associated_property) const override;
|
||||
|
||||
private:
|
||||
explicit CalculatedStyleValue(NonnullRefPtr<CalculationNode const> 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<CSSNumericValue> reify(JS::Realm&) const { return nullptr; }
|
||||
|
||||
protected:
|
||||
CalculationNode(Type, Optional<NumericType>);
|
||||
|
@ -287,6 +290,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> 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<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
SumCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
|
||||
|
@ -325,6 +330,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
ProductCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
|
||||
|
@ -345,6 +351,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
explicit NegateCalculationNode(NonnullRefPtr<CalculationNode const>);
|
||||
|
@ -365,6 +372,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
InvertCalculationNode(NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
|
||||
|
@ -385,6 +393,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
MinCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
|
||||
|
@ -405,6 +414,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
MaxCalculationNode(Vector<NonnullRefPtr<CalculationNode const>>, Optional<NumericType>);
|
||||
|
@ -425,6 +435,7 @@ public:
|
|||
|
||||
virtual void dump(StringBuilder&, int indent) const override;
|
||||
virtual bool equals(CalculationNode const&) const override;
|
||||
virtual GC::Ptr<CSSNumericValue> reify(JS::Realm&) const override;
|
||||
|
||||
private:
|
||||
ClampCalculationNode(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
|
||||
|
|
|
@ -2,6 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
2 Fail
|
||||
Fail Parsing calc(1% + 2em + 3px)
|
||||
Fail Parsing calc(1px + 2% + 3em)
|
||||
2 Pass
|
||||
Pass Parsing calc(1% + 2em + 3px)
|
||||
Pass Parsing calc(1px + 2% + 3em)
|
|
@ -2,11 +2,10 @@ Harness status: OK
|
|||
|
||||
Found 6 tests
|
||||
|
||||
5 Pass
|
||||
1 Fail
|
||||
6 Pass
|
||||
Pass Normalizing a <number> returns a number CSSUnitValue
|
||||
Pass Normalizing a <percentage> returns a percent CSSUnitValue
|
||||
Pass Normalizing a <dimension> returns a CSSUnitValue with the correct unit
|
||||
Pass Normalizing a <number> with a unitless zero returns 0
|
||||
Fail Normalizing a <calc> returns simplified expression
|
||||
Pass Normalizing a <calc> returns simplified expression
|
||||
Pass Normalizing a <dimension> with a unitless zero returns 0px
|
|
@ -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 <dimension-token> 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
|
Loading…
Add table
Add a link
Reference in a new issue