From 12e8f503aa483be715aea77be0da2182366f50d6 Mon Sep 17 00:00:00 2001 From: Callum Law Date: Wed, 5 Nov 2025 13:27:48 +1300 Subject: [PATCH] LibWeb: Support non-fixed This works by generating random values using XorShift128PlusRNG at compute time and then caching them on the document using the relevant random-caching-key --- Libraries/LibWeb/CSS/Keywords.json | 1 + Libraries/LibWeb/CSS/Parser/Parser.cpp | 9 +++ Libraries/LibWeb/CSS/Parser/Parser.h | 1 + Libraries/LibWeb/CSS/Parser/ValueParsing.cpp | 50 ++++++++++++++++- .../CSS/StyleValues/CalculatedStyleValue.cpp | 4 +- .../RandomValueSharingStyleValue.cpp | 41 +++++++++++++- .../RandomValueSharingStyleValue.h | 50 ++++++++++++++++- Libraries/LibWeb/DOM/Document.cpp | 10 ++++ Libraries/LibWeb/DOM/Document.h | 5 ++ Libraries/LibWeb/DOM/Element.cpp | 14 +++++ Libraries/LibWeb/DOM/Element.h | 5 ++ Libraries/LibWeb/Forward.h | 1 + .../LibWeb/GenerateCSSMathFunctions.cpp | 4 +- .../css-values/random-computed.tentative.txt | 29 +++++----- .../css-values/random-serialize.tentative.txt | 56 +++++++++---------- 15 files changed, 226 insertions(+), 54 deletions(-) diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index 2344561ba9f..5d170f418fe 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -192,6 +192,7 @@ "ease-in", "ease-in-out", "ease-out", + "element-shared", "ellipsis", "embed", "emoji", diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index d9e0832f290..069cbc06b6b 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1724,6 +1724,15 @@ bool Parser::context_allows_random_functions() const return m_value_context.find_first_index_if([](ValueParsingContext context) { return context.has(); }).has_value(); } +FlyString Parser::random_value_sharing_auto_name() const +{ + auto top_level_property_context_index = m_value_context.find_first_index_if([](ValueParsingContext const& context) { return context.has(); }); + + auto property_name = string_from_property_id(m_value_context[top_level_property_context_index.value()].get()); + + return MUST(String::formatted("{} {}", property_name, m_random_function_index)); +} + Vector Parser::parse_as_list_of_component_values() { return parse_a_list_of_component_values(m_token_stream); diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 959fd0b5d58..f4532725e63 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -603,6 +603,7 @@ private: bool context_allows_quirky_length() const; bool context_allows_tree_counting_functions() const; bool context_allows_random_functions() const; + FlyString random_value_sharing_auto_name() const; Vector m_rule_context; HashTable m_declared_namespaces; diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 7faf54fbe7e..108fdda8042 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -3798,6 +3798,9 @@ RefPtr Parser::parse_random_value_sharing(To tokens.discard_whitespace(); + if (!tokens.has_next_token()) + return nullptr; + // fixed if (tokens.next_token().is_ident("fixed"sv)) { tokens.discard_a_token(); @@ -3820,8 +3823,51 @@ RefPtr Parser::parse_random_value_sharing(To return nullptr; } - // FIXME: Support non-fixed values - return nullptr; + // [ [ auto | ] || element-shared ] + bool has_explicit_auto = false; + Optional dashed_ident; + bool element_shared = false; + + while (tokens.has_next_token()) { + if (auto maybe_dashed_ident_value = parse_dashed_ident_value(tokens)) { + if (has_explicit_auto || dashed_ident.has_value()) + return nullptr; + + dashed_ident = maybe_dashed_ident_value->custom_ident(); + + tokens.discard_whitespace(); + continue; + } + + auto maybe_keyword_value = parse_keyword_value(tokens); + + if (maybe_keyword_value && maybe_keyword_value->to_keyword() == Keyword::Auto) { + if (has_explicit_auto || dashed_ident.has_value()) + return nullptr; + + has_explicit_auto = true; + + tokens.discard_whitespace(); + continue; + } + + if (maybe_keyword_value && maybe_keyword_value->to_keyword() == Keyword::ElementShared) { + if (element_shared) + return nullptr; + + element_shared = true; + + tokens.discard_whitespace(); + continue; + } + + return nullptr; + } + + if (!dashed_ident.has_value()) + return RandomValueSharingStyleValue::create_auto(random_value_sharing_auto_name(), element_shared); + + return RandomValueSharingStyleValue::create_dashed_ident(dashed_ident.value(), element_shared); } // https://drafts.csswg.org/css-values-4/#typedef-dashed-ident diff --git a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp index 489980c601d..f497a42681f 100644 --- a/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp @@ -2595,7 +2595,9 @@ String RandomCalculationNode::to_string(CalculationContext const& context, Seria StringBuilder builder; builder.append("random("sv); - builder.appendff("{}, ", m_random_value_sharing->to_string(serialization_mode)); + auto random_value_sharing_stringified = m_random_value_sharing->to_string(serialization_mode); + if (!random_value_sharing_stringified.is_empty()) + builder.appendff("{}, ", random_value_sharing_stringified); builder.appendff("{}, ", serialize_a_calculation_tree(m_minimum, context, serialization_mode)); builder.append(serialize_a_calculation_tree(m_maximum, context, serialization_mode)); if (m_step) diff --git a/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.cpp index 78be21267a1..5f30b619cd0 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.cpp @@ -7,11 +7,15 @@ #include "RandomValueSharingStyleValue.h" #include #include +#include namespace Web::CSS { ValueComparingNonnullRefPtr RandomValueSharingStyleValue::absolutized(ComputationContext const& computation_context) const { + // https://drafts.csswg.org/css-values-5/#random-caching + // Each instance of a random function in styles has an associated random base value. + // If the random function’s is fixed , the random base value is that number. if (m_fixed_value) { auto const& absolutized_fixed_value = m_fixed_value->absolutized(computation_context); @@ -21,7 +25,29 @@ ValueComparingNonnullRefPtr RandomValueSharingStyleValue::abso return RandomValueSharingStyleValue::create_fixed(absolutized_fixed_value); } - TODO(); + // Otherwise, the random base value is a pseudo-random real number in the range `[0, 1)` (greater than or equal to 0 + // and less than 1), generated from a uniform distribution, and influenced by the function’s random caching key. + + // A random caching key is a tuple of: + RandomCachingKey random_caching_key { + // 1. A string name: the value of the , if specified in ; or else a string + // of the form "PROPERTY N", where PROPERTY is the name of the property the random function is used in + // (before shorthand expansion, if relevant), and N is the index of the random function among other random + // functions in the same property value. + .name = m_name.value(), + + // 2. An element ID identifying the element the style is being applied to, or null if element-shared is + // specified in . + // FIXME: Use the pseudo element's unique_id() when that's accessible + .element_id = m_element_shared ? Optional { OptionalNone {} } : Optional { computation_context.abstract_element->element().unique_id() }, + + // 3. A document ID identifying the Document the styles are from. + // NB: This is implicit since the cache is stored on the document or the element (which is a child of the document). + }; + + auto random_base_value = const_cast(computation_context.abstract_element->element()).ensure_css_random_base_value(random_caching_key); + + return RandomValueSharingStyleValue::create_fixed(NumberStyleValue::create(random_base_value)); } double RandomValueSharingStyleValue::random_base_value() const @@ -43,7 +69,18 @@ String RandomValueSharingStyleValue::to_string(SerializationMode serialization_m if (m_fixed_value) return MUST(String::formatted("fixed {}", m_fixed_value->to_string(serialization_mode))); - TODO(); + StringBuilder builder; + + if (!m_is_auto) + builder.appendff("{}", m_name.value()); + + if (m_element_shared) { + if (!builder.is_empty()) + builder.append(' '); + builder.append("element-shared"sv); + } + + return builder.to_string_without_validation(); } } diff --git a/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h index 2096c1525c4..237d74c0c63 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h @@ -11,11 +11,26 @@ namespace Web::CSS { +struct RandomCachingKey { + FlyString name; + Optional element_id; +}; + class RandomValueSharingStyleValue : public StyleValueWithDefaultOperators { public: static ValueComparingNonnullRefPtr create_fixed(NonnullRefPtr const& fixed_value) { - return adopt_ref(*new (nothrow) RandomValueSharingStyleValue(fixed_value)); + return adopt_ref(*new (nothrow) RandomValueSharingStyleValue(fixed_value, false, {}, false)); + } + + static ValueComparingNonnullRefPtr create_auto(FlyString name, bool element_shared) + { + return adopt_ref(*new (nothrow) RandomValueSharingStyleValue({}, true, move(name), element_shared)); + } + + static ValueComparingNonnullRefPtr create_dashed_ident(FlyString name, bool element_shared) + { + return adopt_ref(*new (nothrow) RandomValueSharingStyleValue({}, false, move(name), element_shared)); } virtual ~RandomValueSharingStyleValue() override = default; @@ -28,17 +43,46 @@ public: bool properties_equal(RandomValueSharingStyleValue const& other) const { - return m_fixed_value == other.m_fixed_value; + return m_fixed_value == other.m_fixed_value + && m_is_auto == other.m_is_auto + && m_name == other.m_name + && m_element_shared == other.m_element_shared; } private: - explicit RandomValueSharingStyleValue(RefPtr fixed_value) + explicit RandomValueSharingStyleValue(RefPtr fixed_value, bool is_auto, Optional name, bool element_shared) : StyleValueWithDefaultOperators(Type::RandomValueSharing) , m_fixed_value(move(fixed_value)) + , m_is_auto(is_auto) + , m_name(move(name)) + , m_element_shared(element_shared) { } ValueComparingRefPtr m_fixed_value; + bool m_is_auto; + Optional m_name; + bool m_element_shared; +}; + +} + +namespace AK { + +template<> +struct Traits : public DefaultTraits { + static unsigned hash(Web::CSS::RandomCachingKey const& key) + { + if (!key.element_id.has_value()) + return key.name.hash(); + + return pair_int_hash(key.name.hash(), Traits::hash(key.element_id->value())); + } + + static bool equals(Web::CSS::RandomCachingKey const& a, Web::CSS::RandomCachingKey const& b) + { + return a.element_id == b.element_id && a.name == b.name; + } }; } diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index df58a6991fc..e895bde12ef 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -6014,6 +6016,14 @@ void Document::for_each_active_css_style_sheet(Function find_style_sheet_with_url(String const& url, CSS::CSSStyleSheet& style_sheet) { if (style_sheet.href() == url) diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 7900bc18a86..450147ec391 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -267,6 +267,8 @@ public: CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); } + double ensure_element_shared_css_random_base_value(CSS::RandomCachingKey const&); + Optional get_style_sheet_source(CSS::StyleSheetIdentifier const&) const; virtual FlyString node_name() const override { return "#document"_fly_string; } @@ -1351,6 +1353,9 @@ private: HashMap> m_registered_custom_properties; CSS::StyleScope m_style_scope; + + // https://drafts.csswg.org/css-values-5/#random-caching + HashMap m_element_shared_css_random_base_value_cache; }; template<> diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index a2d3b3468d2..366dc7b80ee 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -4311,6 +4312,19 @@ GC::Ref Element::computed_style_map() return *m_computed_style_map_cache; } +double Element::ensure_css_random_base_value(CSS::RandomCachingKey const& random_caching_key) +{ + // NB: We cache element-shared random base values on the Document and non-element-shared ones on the Element itself + // so that when an element is removed it takes its non-shared cache with it. + if (!random_caching_key.element_id.has_value()) + return document().ensure_element_shared_css_random_base_value(random_caching_key); + + return m_element_specific_css_random_base_value_cache.ensure(random_caching_key, []() { + static XorShift128PlusRNG random_number_generator; + return random_number_generator.get(); + }); +} + // The element to inherit style from. // If a pseudo-element is specified, this will return the element itself. // Otherwise, if this element is slotted somewhere, it will return the slot's element to inherit style from. diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 83f0eb5353b..cf234cc4ca3 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -520,6 +520,8 @@ public: // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking virtual bool is_implicitly_potentially_render_blocking() const { return false; } + double ensure_css_random_base_value(CSS::RandomCachingKey const&); + protected: Element(Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; @@ -656,6 +658,9 @@ private: bool m_captured_in_a_view_transition { false }; bool m_is_contained_in_list_subtree { false }; + + // https://drafts.csswg.org/css-values-5/#random-caching + HashMap m_element_specific_css_random_base_value_cache; }; template<> diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index fdf6f998c11..c07cdc318cd 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -410,6 +410,7 @@ struct CalculationResolutionContext; struct CSSStyleSheetInit; struct GridRepeatParams; struct LogicalAliasMappingContext; +struct RandomCachingKey; struct StyleSheetIdentifier; using PaintOrderList = Array; diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp index 0e054c02ec3..af6ef78b4c0 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSMathFunctions.cpp @@ -98,7 +98,6 @@ ErrorOr generate_implementation_file(JsonObject& functions_data, Core::Fil #include #include #include -#include #include namespace Web::CSS { @@ -312,8 +311,7 @@ RefPtr Parser::parse_math_function(Function const& functi 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))"))); + parameter_generator.set("parameter_default", MUST(String::formatted(" = RandomValueSharingStyleValue::create_auto(random_value_sharing_auto_name(), false)"))); } else { // NOTE: This assumes everything not handled above is a calculation node of some kind. parameter_is_calculation = true; diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-computed.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-computed.tentative.txt index 3a8c6c1969a..52e10c542ba 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-computed.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-computed.tentative.txt @@ -2,16 +2,15 @@ Harness status: OK Found 72 tests -59 Pass -13 Fail +72 Pass 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)' +Pass Property scale value 'random(--foo, 2, 12)' +Pass Property scale value 'random(--foo element-shared, 3, 13)' +Pass Property scale value 'random(element-shared --foo, 4, 14)' Pass 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)' +Pass Property scale value 'random(--foo, 10, 20, 5)' +Pass Property scale value 'random(--foo element-shared, 20, 30, 5)' +Pass Property scale value 'random(element-shared --foo, 30, 40, 5)' Pass Property scale value 'random(100, 10)' Pass Property scale value 'random(-10, -100)' Pass Property scale value 'random(-100, -10)' @@ -61,18 +60,18 @@ Pass Property scale value 'calc(10 + random(10, 100, infinity))' Pass Property scale value 'random(10, 100, -infinity)' Pass Property scale value 'calc(10 + random(10, 100, -infinity))' 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)' +Pass Property scale value on pseudo element '::before' 'random(--bar, 8, 18)' +Pass Property scale value on pseudo element '::before' 'random(element-shared, 9, 19)' +Pass Property scale value on pseudo element '::before' 'random(element-shared --foo, 10, 20)' 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)' +Pass Maximum random: 'random(a, b)' +Pass Maximum random - shorthand: random(a, b)) +Pass Shared by name within an element: 'random(--identifier, a, b)' Pass Shared by name within an element - shorthand: random(--identifier, a, b)) 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: random(--identifier element-shared, a, b) Pass Shared globally - shorthand: random(element-shared, a, b)) Pass Fixed: random(fixed , a, b) \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-serialize.tentative.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-serialize.tentative.txt index 8d7e3e2ded0..775f31b4549 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-serialize.tentative.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/random-serialize.tentative.txt @@ -2,37 +2,37 @@ Harness status: OK Found 32 tests -3 Pass -29 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 -Fail e.style['width'] = "random(auto, 0px, 100px)" should set the property value -Fail e.style['width'] = "random(--foo element-shared, 0px, 100px)" should set the property value -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 +29 Pass +3 Fail +Pass e.style['width'] = "random(0px, 100px)" should set the property value +Pass e.style['width'] = "random(0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(--foo, 0px, 100px)" should set the property value +Pass e.style['width'] = "random(auto, 0px, 100px)" should set the property value +Pass e.style['width'] = "random(--foo element-shared, 0px, 100px)" should set the property value +Pass e.style['width'] = "random(auto element-shared, 0px, 100px)" should set the property value +Pass e.style['width'] = "random(element-shared --foo, 0px, 100px)" should set the property value +Pass e.style['width'] = "random(element-shared auto, 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 -Fail e.style['width'] = "random(auto element-shared, 0px, 100px, 50px)" should set the property value -Fail e.style['width'] = "random(element-shared --foo, 0px, 100px, 50px)" should set the property value -Fail e.style['width'] = "random(element-shared auto, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(--foo, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(auto, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(--foo element-shared, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(auto element-shared, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(element-shared --foo, 0px, 100px, 50px)" should set the property value +Pass e.style['width'] = "random(element-shared auto, 0px, 100px, 50px)" should set the property value Pass e.style['width'] = "random(fixed 0.5, 0px, 100px, 50px)" should set the property value -Fail e.style['width'] = "random(10px, 20%)" should set the property value -Fail e.style['width'] = "random(100px, 0px)" should set the property value -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 +Pass e.style['width'] = "random(10px, 20%)" should set the property value +Pass e.style['width'] = "random(100px, 0px)" should set the property value +Pass e.style['width'] = "random(-100px, -10px)" should set the property value +Pass e.style['width'] = "random(-100px, -10px, -5px)" should set the property value +Pass 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 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 +Pass 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 -Fail e.style['scale'] = "random(0.5, 2.5)" should set the property value -Fail e.style['scale'] = "random(0.5, 2.5, 0.1)" should set the property value -Fail e.style['rotate'] = "random(25deg, 1turn)" should set the property value -Fail e.style['rotate'] = "random(25deg, 1turn, 5deg)" should set the property value -Fail e.style['transition-delay'] = "random(25ms, 50s)" should set the property value -Fail e.style['transition-delay'] = "random(25ms, 50s, 5s)" should set the property value \ No newline at end of file +Pass e.style['scale'] = "random(0.5, 2.5)" should set the property value +Pass e.style['scale'] = "random(0.5, 2.5, 0.1)" should set the property value +Pass e.style['rotate'] = "random(25deg, 1turn)" should set the property value +Pass e.style['rotate'] = "random(25deg, 1turn, 5deg)" should set the property value +Pass e.style['transition-delay'] = "random(25ms, 50s)" should set the property value +Pass e.style['transition-delay'] = "random(25ms, 50s, 5s)" should set the property value \ No newline at end of file