LibWeb: Implement basic CSS random() function

At the moment this is limited to only fixed value sharing and does not
support step values
This commit is contained in:
Callum Law 2025-11-04 13:35:17 +13:00 committed by Sam Atkins
parent 8944130fde
commit 2a5e389f63
Notes: github-actions[bot] 2025-12-01 11:02:05 +00:00
17 changed files with 380 additions and 31 deletions

View file

@ -265,6 +265,7 @@ set(SOURCES
CSS/StyleValues/PositionStyleValue.cpp
CSS/StyleValues/RGBColorStyleValue.cpp
CSS/StyleValues/RadialGradientStyleValue.cpp
CSS/StyleValues/RandomValueSharingStyleValue.cpp
CSS/StyleValues/RectStyleValue.cpp
CSS/StyleValues/RepeatStyleStyleValue.cpp
CSS/StyleValues/ScrollbarColorStyleValue.cpp

View file

@ -168,6 +168,28 @@
}
]
},
"random": {
"parameter-validation": "consistent",
"parameters": [
{
"name": "random-value-sharing",
"type": "<random-value-sharing>",
"required": false,
"default": "auto",
"__comment": "NOTE: The actual default is hardcoded and we don't respect the value above, we have it there so we know that this argument has a default"
},
{
"name": "minimum",
"type": "<number>|<dimension>|<percentage>",
"required": true
},
{
"name": "maximum",
"type": "<number>|<dimension>|<percentage>",
"required": true
}
]
},
"rem": {
"parameter-validation": "same",
"parameters": [

View file

@ -1718,6 +1718,12 @@ bool Parser::context_allows_tree_counting_functions() const
return true;
}
bool Parser::context_allows_random_functions() const
{
// For now we only allow random functions within property contexts, see https://drafts.csswg.org/css-values-5/#issue-cd071f29
return m_value_context.find_first_index_if([](ValueParsingContext context) { return context.has<PropertyID>(); }).has_value();
}
Vector<ComponentValue> Parser::parse_as_list_of_component_values()
{
return parse_a_list_of_component_values(m_token_stream);

View file

@ -77,6 +77,7 @@ enum SpecialContext : u8 {
CubicBezierFunctionXCoordinate,
DOMMatrixInitString,
MediaCondition,
RandomValueSharingFixedValue,
ShadowBlurRadius,
StepsIntervalsJumpNone,
StepsIntervalsNormal,
@ -379,6 +380,7 @@ private:
RefPtr<CustomIdentStyleValue const> parse_custom_ident_value(TokenStream<ComponentValue>&, ReadonlySpan<StringView> blacklist);
Optional<FlyString> parse_dashed_ident(TokenStream<ComponentValue>&);
RefPtr<CustomIdentStyleValue const> parse_dashed_ident_value(TokenStream<ComponentValue>&);
RefPtr<RandomValueSharingStyleValue const> parse_random_value_sharing(TokenStream<ComponentValue>&);
// NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp)
RefPtr<CalculationNode const> parse_math_function(Function const&, CalculationContext const&);
RefPtr<CalculationNode const> parse_a_calc_function_node(Function const&, CalculationContext const&);
@ -593,6 +595,7 @@ private:
}
bool context_allows_quirky_length() const;
bool context_allows_tree_counting_functions() const;
bool context_allows_random_functions() const;
Vector<RuleContext> m_rule_context;
HashTable<FlyString> m_declared_namespaces;

View file

@ -60,6 +60,7 @@
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/RGBColorStyleValue.h>
#include <LibWeb/CSS/StyleValues/RadialGradientStyleValue.h>
#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h>
#include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
#include <LibWeb/CSS/StyleValues/RepeatStyleStyleValue.h>
@ -3789,6 +3790,40 @@ RefPtr<CustomIdentStyleValue const> Parser::parse_custom_ident_value(TokenStream
return nullptr;
}
// https://drafts.csswg.org/css-values-5/#typedef-random-value-sharing
RefPtr<RandomValueSharingStyleValue const> Parser::parse_random_value_sharing(TokenStream<ComponentValue>& tokens)
{
// <random-value-sharing> = [ [ auto | <dashed-ident> ] || element-shared ] | fixed <number [0,1]>
auto transaction = tokens.begin_transaction();
tokens.discard_whitespace();
// fixed <number [0,1]>
if (tokens.next_token().is_ident("fixed"sv)) {
tokens.discard_a_token();
tokens.discard_whitespace();
auto context_guard = push_temporary_value_parsing_context(SpecialContext::RandomValueSharingFixedValue);
if (auto fixed_value = parse_number_value(tokens)) {
tokens.discard_whitespace();
if (tokens.has_next_token())
return nullptr;
if (fixed_value->is_number() && (fixed_value->as_number().number() < 0 || fixed_value->as_number().number() >= 1))
return nullptr;
transaction.commit();
return RandomValueSharingStyleValue::create_fixed(fixed_value.release_nonnull());
}
return nullptr;
}
// FIXME: Support non-fixed values
return nullptr;
}
// https://drafts.csswg.org/css-values-4/#typedef-dashed-ident
Optional<FlyString> Parser::parse_dashed_ident(TokenStream<ComponentValue>& tokens)
{
@ -4384,6 +4419,9 @@ RefPtr<CalculatedStyleValue const> Parser::parse_calculated_value(ComponentValue
case SpecialContext::CubicBezierFunctionXCoordinate:
// Coordinates on the X axis must be between 0 and 1
return CalculationContext { .accepted_type_ranges = { { ValueType::Number, { 0, 1 } } } };
case SpecialContext::RandomValueSharingFixedValue:
// Fixed values have to be less than one and numbers serialize with six digits of precision
return CalculationContext { .accepted_type_ranges = { { ValueType::Number, { 0, 0.999999 } } } };
case SpecialContext::StepsIntervalsJumpNone:
return CalculationContext { .resolve_numbers_as_integers = true, .accepted_type_ranges = { { ValueType::Integer, { 2, NumericLimits<float>::max() } } } };
case SpecialContext::StepsIntervalsNormal:

View file

@ -31,6 +31,7 @@
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h>
#include <LibWeb/CSS/StyleValues/ResolutionStyleValue.h>
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
@ -284,6 +285,11 @@ static String serialize_a_math_function(CalculationNode const& fn, CalculationCo
}
}
// AD-HOC: We serialize random() directly since it has abnormal children (e.g. m_random_value_sharing which is not a
// calculation node).
if (fn.type() == CalculationNode::Type::Random)
return as<RandomCalculationNode>(fn).to_string(context, serialization_mode);
// 3. If the calculation trees root node is a numeric value, or a calc-operator node, let s be a string initially
// containing "calc(".
// Otherwise, let s be a string initially containing the name of the root node, lowercased (such as "sin" or
@ -572,6 +578,8 @@ StringView CalculationNode::name() const
return "log"sv;
case Type::Exp:
return "exp"sv;
case Type::Random:
return "random"sv;
case Type::Round:
return "round"sv;
case Type::Mod:
@ -2434,6 +2442,134 @@ bool ModCalculationNode::equals(CalculationNode const& other) const
&& m_y->equals(*static_cast<ModCalculationNode const&>(other).m_y);
}
NonnullRefPtr<RandomCalculationNode const> RandomCalculationNode::create(NonnullRefPtr<RandomValueSharingStyleValue const> random_value_sharing, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum)
{
Optional<NumericType> numeric_type = add_the_types(*minimum, *maximum);
return adopt_ref(*new (nothrow) RandomCalculationNode(move(random_value_sharing), move(minimum), move(maximum), move(numeric_type)));
}
RandomCalculationNode::RandomCalculationNode(NonnullRefPtr<RandomValueSharingStyleValue const> random_value_sharing, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum, Optional<NumericType> numeric_type)
: CalculationNode(Type::Random, move(numeric_type))
, m_random_value_sharing(move(random_value_sharing))
, m_minimum(move(minimum))
, m_maximum(move(maximum))
{
}
RandomCalculationNode::~RandomCalculationNode() = default;
bool RandomCalculationNode::contains_percentage() const
{
return m_minimum->contains_percentage() || m_maximum->contains_percentage();
}
NonnullRefPtr<CalculationNode const> RandomCalculationNode::with_simplified_children(CalculationContext const& context, CalculationResolutionContext const& resolution_context) const
{
ValueComparingRefPtr<RandomValueSharingStyleValue const> simplified_random_value_sharing;
// When we are in the absolutization process we should absolutize m_random_value_sharing
if (resolution_context.length_resolution_context.has_value()) {
ComputationContext computation_context {
.length_resolution_context = resolution_context.length_resolution_context.value(),
.abstract_element = resolution_context.abstract_element
};
simplified_random_value_sharing = m_random_value_sharing->absolutized(computation_context)->as_random_value_sharing();
} else {
simplified_random_value_sharing = m_random_value_sharing;
}
ValueComparingNonnullRefPtr<CalculationNode const> simplified_minimum = simplify_a_calculation_tree(m_minimum, context, resolution_context);
ValueComparingNonnullRefPtr<CalculationNode const> simplified_maximum = simplify_a_calculation_tree(m_maximum, context, resolution_context);
if (simplified_random_value_sharing == m_random_value_sharing && simplified_minimum == m_minimum && simplified_maximum == m_maximum)
return *this;
return RandomCalculationNode::create(simplified_random_value_sharing.release_nonnull(), move(simplified_minimum), move(simplified_maximum));
}
// https://drafts.csswg.org/css-values-5/#random-evaluation
Optional<CalculatedStyleValue::CalculationResult> RandomCalculationNode::run_operation_if_possible(CalculationContext const& context, CalculationResolutionContext const& resolution_context) const
{
// NB: We don't want to resolve this before computation time even if it's possible
if (!resolution_context.abstract_element.has_value() && !resolution_context.length_resolution_context.has_value() && resolution_context.percentage_basis.has<Empty>())
return {};
auto random_base_value = m_random_value_sharing->random_base_value();
auto minimum = try_get_value_with_canonical_unit(m_minimum, context, resolution_context);
auto maximum = try_get_value_with_canonical_unit(m_maximum, context, resolution_context);
if (!minimum.has_value() || !maximum.has_value())
return {};
auto minimum_value = minimum->value();
auto maximum_value = maximum->value();
// https://drafts.csswg.org/css-values-5/#random-infinities
// If the maximum value is less than the minimum value, it behaves as if its equal to the minimum value.
if (maximum_value < minimum_value)
maximum_value = minimum_value;
// https://drafts.csswg.org/css-values-5/#random-infinities
// In random(A, B), if A is infinite, the result is infinite.
if (isinf(minimum_value))
return CalculatedStyleValue::CalculationResult { AK::Infinity<double>, numeric_type() };
// If A is finite, but the difference between A and B is either infinite or large enough to be treated as infinite
// in the user agent, the result is NaN.
if (isinf(maximum_value))
return CalculatedStyleValue::CalculationResult { AK::NaN<double>, numeric_type() };
// Note: As usual for math functions, if any argument calculation is NaN, the result is NaN.
if (isnan(minimum_value) || isnan(maximum_value))
return CalculatedStyleValue::CalculationResult { AK::NaN<double>, numeric_type() };
// Given a random function with a random base value R, the value of the function is:
// - for a random() function with min and max, but no step
// Return min + R * (max - min)
return CalculatedStyleValue::CalculationResult {
minimum_value + (random_base_value * (maximum_value - minimum_value)),
numeric_type()
};
}
String RandomCalculationNode::to_string(CalculationContext const& context, SerializationMode serialization_mode) const
{
StringBuilder builder;
builder.append("random("sv);
builder.appendff("{}, ", m_random_value_sharing->to_string(serialization_mode));
builder.appendff("{}, ", serialize_a_calculation_tree(m_minimum, context, serialization_mode));
builder.appendff("{})", serialize_a_calculation_tree(m_maximum, context, serialization_mode));
return builder.to_string_without_validation();
}
void RandomCalculationNode::dump(StringBuilder& builder, int indent) const
{
builder.appendff("{: >{}}RANDOM:\n", "", indent);
builder.appendff("{}\n", m_random_value_sharing->to_string(SerializationMode::Normal));
m_minimum->dump(builder, indent + 2);
m_maximum->dump(builder, indent + 2);
}
bool RandomCalculationNode::equals(CalculationNode const& other) const
{
if (this == &other)
return true;
if (type() != other.type())
return false;
auto const& other_random = as<RandomCalculationNode>(other);
return m_random_value_sharing == other_random.m_random_value_sharing
&& m_minimum == other_random.m_minimum
&& m_maximum == other_random.m_maximum;
}
NonnullRefPtr<RemCalculationNode const> RemCalculationNode::create(NonnullRefPtr<CalculationNode const> x, NonnullRefPtr<CalculationNode const> y)
{
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation

View file

@ -190,6 +190,10 @@ public:
Mod,
Rem,
// Random value generation
// https://drafts.csswg.org/css-values-5/#random
Random,
// Non-math functions
NonMathFunction
};
@ -228,6 +232,7 @@ public:
case Type::Round:
case Type::Mod:
case Type::Rem:
case Type::Random:
return true;
default:
@ -747,6 +752,30 @@ private:
NonnullRefPtr<CalculationNode const> m_y;
};
class RandomCalculationNode final : public CalculationNode {
public:
static NonnullRefPtr<RandomCalculationNode const> create(NonnullRefPtr<RandomValueSharingStyleValue const>, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum);
~RandomCalculationNode();
virtual bool contains_percentage() const override;
virtual NonnullRefPtr<CalculationNode const> with_simplified_children(CalculationContext const&, CalculationResolutionContext const&) const override;
virtual Optional<CalculatedStyleValue::CalculationResult> run_operation_if_possible(CalculationContext const&, CalculationResolutionContext const&) const override;
// NOTE: We don't return children here as serialization is handled ad-hoc
virtual Vector<NonnullRefPtr<CalculationNode const>> children() const override { return {}; }
String to_string(CalculationContext const&, SerializationMode serialization_mode) const;
virtual void dump(StringBuilder&, int indent) const override;
virtual bool equals(CalculationNode const&) const override;
private:
RandomCalculationNode(NonnullRefPtr<RandomValueSharingStyleValue const>, NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>, Optional<NumericType>);
ValueComparingNonnullRefPtr<RandomValueSharingStyleValue const> m_random_value_sharing;
ValueComparingNonnullRefPtr<CalculationNode const> m_minimum;
ValueComparingNonnullRefPtr<CalculationNode const> m_maximum;
};
class RemCalculationNode final : public CalculationNode {
public:
static NonnullRefPtr<RemCalculationNode const> create(NonnullRefPtr<CalculationNode const>, NonnullRefPtr<CalculationNode const>);

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2025, Callum Law <callumlaw1709@outlook.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RandomValueSharingStyleValue.h"
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
namespace Web::CSS {
ValueComparingNonnullRefPtr<StyleValue const> RandomValueSharingStyleValue::absolutized(ComputationContext const& computation_context) const
{
if (m_fixed_value) {
auto const& absolutized_fixed_value = m_fixed_value->absolutized(computation_context);
if (m_fixed_value == absolutized_fixed_value)
return *this;
return RandomValueSharingStyleValue::create_fixed(absolutized_fixed_value);
}
TODO();
}
double RandomValueSharingStyleValue::random_base_value() const
{
VERIFY(m_fixed_value);
VERIFY(m_fixed_value->is_number() || (m_fixed_value->is_calculated() && m_fixed_value->as_calculated().resolves_to_number()));
if (m_fixed_value->is_number())
return m_fixed_value->as_number().number();
if (m_fixed_value->is_calculated())
return m_fixed_value->as_calculated().resolve_number({}).value();
VERIFY_NOT_REACHED();
}
String RandomValueSharingStyleValue::to_string(SerializationMode serialization_mode) const
{
if (m_fixed_value)
return MUST(String::formatted("fixed {}", m_fixed_value->to_string(serialization_mode)));
TODO();
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2025, Callum Law <callumlaw1709@outlook.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h>
namespace Web::CSS {
class RandomValueSharingStyleValue : public StyleValueWithDefaultOperators<RandomValueSharingStyleValue> {
public:
static ValueComparingNonnullRefPtr<RandomValueSharingStyleValue const> create_fixed(NonnullRefPtr<StyleValue const> const& fixed_value)
{
return adopt_ref(*new (nothrow) RandomValueSharingStyleValue(fixed_value));
}
virtual ~RandomValueSharingStyleValue() override = default;
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
double random_base_value() const;
virtual String to_string(SerializationMode serialization_mode) const override;
bool properties_equal(RandomValueSharingStyleValue const& other) const
{
return m_fixed_value == other.m_fixed_value;
}
private:
explicit RandomValueSharingStyleValue(RefPtr<StyleValue const> fixed_value)
: StyleValueWithDefaultOperators(Type::RandomValueSharing)
, m_fixed_value(move(fixed_value))
{
}
ValueComparingRefPtr<StyleValue const> m_fixed_value;
};
}

View file

@ -55,6 +55,7 @@
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/RadialGradientStyleValue.h>
#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h>
#include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
#include <LibWeb/CSS/StyleValues/RepeatStyleStyleValue.h>

View file

@ -73,6 +73,7 @@ namespace Web::CSS {
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Percentage, percentage, PercentageStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Position, position, PositionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(RadialGradient, radial_gradient, RadialGradientStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(RandomValueSharing, random_value_sharing, RandomValueSharingStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Ratio, ratio, RatioStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Rect, rect, RectStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(RepeatStyle, repeat_style, RepeatStyleStyleValue) \

View file

@ -350,6 +350,7 @@ class PercentageStyleValue;
class PositionStyleValue;
class PropertyNameAndID;
class RadialGradientStyleValue;
class RandomValueSharingStyleValue;
class Ratio;
class RatioStyleValue;
class RectStyleValue;

View file

@ -98,6 +98,8 @@ ErrorOr<void> generate_implementation_file(JsonObject& functions_data, Core::Fil
#include <LibWeb/CSS/Parser/ErrorReporter.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h>
namespace Web::CSS {
@ -260,6 +262,13 @@ RefPtr<CalculationNode const> Parser::parse_math_function(Function const& functi
function_generator.set("min_argument_count", String::number(min_argument_count));
function_generator.set("max_argument_count", String::number(max_argument_count));
if (name == "random") {
function_generator.append(R"~~~(
if (!context_allows_random_functions())
return nullptr;
)~~~");
}
function_generator.append(R"~~~(
if (arguments.size() < @min_argument_count@ || arguments.size() > @max_argument_count@) {
ErrorReporter::the().report(InvalidValueError {
@ -295,6 +304,14 @@ RefPtr<CalculationNode const> Parser::parse_math_function(Function const& functi
} else {
parameter_generator.set("parameter_default", ""_string);
}
} else if (parameter_type_string == "<random-value-sharing>") {
parameter_is_calculation = false;
parameter_generator.set("parameter_type", "RefPtr<RandomValueSharingStyleValue const>"_string);
parameter_generator.set("parse_function", MUST(String::formatted("parse_random_value_sharing(tokens_{})", parameter_index)));
parameter_generator.set("check_function", " != nullptr"_string);
parameter_generator.set("release_function", ".release_nonnull()"_string);
// FIXME: This should be 'auto' rather than 'fixed 0' by default
parameter_generator.set("parameter_default", MUST(String::formatted(" = RandomValueSharingStyleValue::create_fixed(NumberStyleValue::create(0))")));
} else {
// NOTE: This assumes everything not handled above is a calculation node of some kind.
parameter_is_calculation = true;

View file

@ -2,9 +2,9 @@ Harness status: OK
Found 72 tests
4 Pass
68 Fail
Fail Property scale value 'random(1, 11)'
27 Pass
45 Fail
Pass Property scale value 'random(1, 11)'
Fail Property scale value 'random(--foo, 2, 12)'
Fail Property scale value 'random(--foo element-shared, 3, 13)'
Fail Property scale value 'random(element-shared --foo, 4, 14)'
@ -12,15 +12,15 @@ Fail Property scale value 'random(0, 10, 5)'
Fail Property scale value 'random(--foo, 10, 20, 5)'
Fail Property scale value 'random(--foo element-shared, 20, 30, 5)'
Fail Property scale value 'random(element-shared --foo, 30, 40, 5)'
Fail Property scale value 'random(100, 10)'
Fail Property scale value 'random(-10, -100)'
Fail Property scale value 'random(-100, -10)'
Pass Property scale value 'random(100, 10)'
Pass Property scale value 'random(-10, -100)'
Pass Property scale value 'random(-100, -10)'
Fail Property scale value 'random(40, 50, -5)'
Fail Property scale value 'random(5 * 1, 30 / 2)'
Fail Property scale value 'calc(2 * random(6, 16))'
Fail Property scale value 'random(NaN, 100)'
Fail Property scale value 'random(10, NaN)'
Fail Property scale value 'random(NaN, NaN)'
Pass Property scale value 'random(5 * 1, 30 / 2)'
Pass Property scale value 'calc(2 * random(6, 16))'
Pass Property scale value 'random(NaN, 100)'
Pass Property scale value 'random(10, NaN)'
Pass Property scale value 'random(NaN, NaN)'
Fail Property scale value 'random(NaN, 100, 10)'
Fail Property scale value 'random(10, NaN, 10)'
Fail Property scale value 'random(NaN, NaN, 10)'
@ -28,9 +28,9 @@ Fail Property scale value 'random(NaN, 100, NaN)'
Fail Property scale value 'random(10, NaN, NaN)'
Fail Property scale value 'random(NaN, NaN, NaN)'
Fail Property scale value 'random(10, 100, NaN)'
Fail Property scale value 'calc(10 + random(NaN, 100))'
Fail Property scale value 'calc(10 + random(10, NaN))'
Fail Property scale value 'calc(10 + random(NaN, NaN))'
Pass Property scale value 'calc(10 + random(NaN, 100))'
Pass Property scale value 'calc(10 + random(10, NaN))'
Pass Property scale value 'calc(10 + random(NaN, NaN))'
Fail Property scale value 'calc(10 + random(NaN, 100, 10))'
Fail Property scale value 'calc(10 + random(10, NaN, 10))'
Fail Property scale value 'calc(10 + random(NaN, NaN, 10))'
@ -38,35 +38,35 @@ Fail Property scale value 'calc(10 + random(NaN, 100, NaN))'
Fail Property scale value 'calc(10 + random(10, NaN, NaN))'
Fail Property scale value 'calc(10 + random(NaN, NaN, NaN))'
Fail Property scale value 'calc(10 + random(10, 100, NaN))'
Fail Property scale value 'random(infinity, 100)'
Fail Property scale value 'random(infinity, infinity)'
Pass Property scale value 'random(infinity, 100)'
Pass Property scale value 'random(infinity, infinity)'
Fail Property scale value 'random(infinity, 100, 10)'
Fail Property scale value 'random(infinity, infinity, 10)'
Fail Property scale value 'random(infinity, 100, infinity)'
Fail Property scale value 'random(infinity, infinity, infinity)'
Fail Property scale value 'calc(10 + random(infinity, 100))'
Fail Property scale value 'calc(10 + random(infinity, infinity))'
Pass Property scale value 'calc(10 + random(infinity, 100))'
Pass Property scale value 'calc(10 + random(infinity, infinity))'
Fail Property scale value 'calc(10 + random(infinity, infinity, 10))'
Fail Property scale value 'calc(10 + random(infinity, 100, infinity))'
Fail Property scale value 'calc(10 + random(infinity, infinity, infinity))'
Fail Property scale value 'calc(10 + random(infinity, 100, 10))'
Fail Property scale value 'random(10, infinity)'
Pass Property scale value 'random(10, infinity)'
Fail Property scale value 'random(10, infinity, 10)'
Fail Property scale value 'random(10, infinity, infinity)'
Fail Property scale value 'calc(10 + random(10, infinity))'
Pass Property scale value 'calc(10 + random(10, infinity))'
Fail Property scale value 'calc(10 + random(10, infinity, 10))'
Fail Property scale value 'calc(10 + random(10, infinity, infinity))'
Fail Property scale value 'random(10, 100, infinity)'
Fail Property scale value 'calc(10 + random(10, 100, infinity))'
Fail Property scale value 'random(10, 100, -infinity)'
Fail Property scale value 'calc(10 + random(10, 100, -infinity))'
Fail Property scale value on pseudo element '::before' 'random(7, 17)'
Pass Property scale value on pseudo element '::before' 'random(7, 17)'
Fail Property scale value on pseudo element '::before' 'random(--bar, 8, 18)'
Fail Property scale value on pseudo element '::before' 'random(element-shared, 9, 19)'
Fail Property scale value on pseudo element '::before' 'random(element-shared --foo, 10, 20)'
Fail Property translate value 'random(10%, 100%)'
Fail Property translate value 'random(fixed random(1, 2), 10%, 100%)'
Fail Property translate value 'random(fixed random(-2, -1), 10%, 100%)'
Pass Property translate value 'random(10%, 100%)'
Pass Property translate value 'random(fixed random(1, 2), 10%, 100%)'
Pass Property translate value 'random(fixed random(-2, -1), 10%, 100%)'
Fail Maximum random: 'random(a, b)'
Fail Maximum random - shorthand: random(a, b))
Fail Shared by name within an element: 'random(--identifier, a, b)'
@ -75,4 +75,4 @@ Pass Shared between elements within a property: random(element-shared, a, b)
Pass Shared between elements within a property - shorthand: random(element-shared, a, b))
Fail Shared globally: random(--identifier element-shared, a, b)
Pass Shared globally - shorthand: random(element-shared, a, b))
Fail Fixed: random(fixed <number>, a, b)
Pass Fixed: random(fixed <number>, a, b)

View file

@ -2,5 +2,5 @@ Harness status: OK
Found 1 tests
1 Fail
Fail random() is not ignored in keyframe
1 Pass
Pass random() is not ignored in keyframe

View file

@ -25,4 +25,4 @@ Pass e.style['width'] = "random(fixed 0.5 --foo, 1px, 2px)" should not set the p
Pass e.style['width'] = "random(fixed 0.5px, 1px, 2px)" should not set the property value
Pass e.style['width'] = "random(fixed 0.5%, 1px, 2px)" should not set the property value
Pass e.style['width'] = "random(fixed -1, 1px, 2px)" should not set the property value
Pass e.style['width'] = "random(10deg, 20deg)" should not set the property value
Pass e.style['width'] = "random(10deg, 20deg)" should not set the property value

View file

@ -2,7 +2,8 @@ Harness status: OK
Found 32 tests
32 Fail
2 Pass
30 Fail
Fail e.style['width'] = "random(0px, 100px)" should set the property value
Fail e.style['width'] = "random(0px, 100px, 50px)" should set the property value
Fail e.style['width'] = "random(--foo, 0px, 100px)" should set the property value
@ -11,7 +12,7 @@ Fail e.style['width'] = "random(--foo element-shared, 0px, 100px)" should set th
Fail e.style['width'] = "random(auto element-shared, 0px, 100px)" should set the property value
Fail e.style['width'] = "random(element-shared --foo, 0px, 100px)" should set the property value
Fail e.style['width'] = "random(element-shared auto, 0px, 100px)" should set the property value
Fail e.style['width'] = "random(fixed 0.5, 0px, 100px)" should set the property value
Pass e.style['width'] = "random(fixed 0.5, 0px, 100px)" should set the property value
Fail e.style['width'] = "random(--foo, 0px, 100px, 50px)" should set the property value
Fail e.style['width'] = "random(auto, 0px, 100px, 50px)" should set the property value
Fail e.style['width'] = "random(--foo element-shared, 0px, 100px, 50px)" should set the property value
@ -25,7 +26,7 @@ Fail e.style['width'] = "random(-100px, -10px)" should set the property value
Fail e.style['width'] = "random(-100px, -10px, -5px)" should set the property value
Fail e.style['width'] = "random(1em, 200rem)" should set the property value
Fail e.style['width'] = "random(10 * 100px, 200em / 2)" should set the property value
Fail e.style['width'] = "random(fixed calc(2 / 4), 0px, 100px)" should set the property value
Pass e.style['width'] = "random(fixed calc(2 / 4), 0px, 100px)" should set the property value
Fail e.style['width'] = "calc(2 * random(0px, 100px))" should set the property value
Fail e.style['max-lines'] = "random(25, 50)" should set the property value
Fail e.style['max-lines'] = "random(25, 50, 5)" should set the property value