Chrome · Animation
CVE-2026-110166
OOB in Animation
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc |
modified | |
ifthird_party/blink/renderer/core/animation/css_transform_interpolation_type.cc |
modified | |
forthird_party/blink/renderer/core/animation/invalidatable_interpolation.cc |
modified | |
ifthird_party/blink/renderer/core/animation/invalidatable_interpolation.cc |
modified |
Files Changed
third_party/blink/renderer/core/animation/css_transform_interpolation_type.ccthird_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.htmlthird_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.htmlthird_party/blink/renderer/core/animation/invalidatable_interpolation.ccthird_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.htmlthird_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
Patch
From 544a340956293550ca5eeb89d7a046879527df18 Mon Sep 17 00:00:00 2001
From: Robert Flack <flackr@chromium.org>
Date: Wed, 07 Oct 2026 06:30:41 -0700
Subject: [PATCH] Fix pre-interpolation composition with mismatched underlying values
When converting non-neutral additive or accumulative keyframes,
InvalidatableInterpolation::MaybeConvertPairwise and
ConvertSingleKeyframe should only pass underlying_value_owner.Value() to
with a matching InterpolationType.
This fixes issues when animating a registered custom property whose
underlying value may be absent, or not convertible by any
InterpolationType, or produced by a different InterpolationType.
Bug: 566496747
Change-Id: Ide25c19ff4a63c8e000f90c26fcd4dc1a5b7bc65
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8521821
Commit-Queue: Robert Flack <flackr@chromium.org>
Reviewed-by: Kevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1713374}
---
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
index b3fda63..cdf44e25 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -73,6 +73,12 @@
const InterpolationValue& underlying,
EffectModel::CompositeOperation composite,
ConversionCheckers& conversion_checkers) const {
+ conversion_checkers.push_back(MakeGarbageCollected<UnderlyingLengthChecker>(
+ UnderlyingLengthChecker::GetUnderlyingLength(underlying)));
+ if (!underlying) {
+ return value;
+ }
+
// This adapts a ListInterpolationFunctions::CompositeItemCallback function
// such that we can use the InterpolationType::Composite function of the
// inner interpolation type to get the answer.
@@ -82,12 +88,10 @@
UnderlyingValueOwner owner;
owner.Set(this, underlying);
- ConversionCheckers null_checkers;
-
const CSSInterpolationType* interpolation_type =
inner_interpolation_type_.Get();
auto composite_callback =
- [interpolation_type, composite, &null_checkers](
+ [interpolation_type, composite, &conversion_checkers](
UnderlyingValue& underlying_value, double underlying_fraction,
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value) {
@@ -99,7 +103,7 @@
underlying_value.GetNonInterpolableValue());
InterpolationValue composite_result =
interpolation_type->PreInterpolationCompositeIfNeeded(
- std::move(value), underlying, composite, null_checkers);
+ std::move(value), underlying, composite, conversion_checkers);
composite_result = composite_result.Clone();
underlying_value.SetInterpolableValue(
composite_result.interpolable_value);
diff --git a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
index 860bf75..3fc0f43c 100644
--- a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
@@ -156,6 +156,10 @@
conversion_checkers.push_back(
MakeGarbageCollected<AlwaysInvalidateChecker>());
+ if (!underlying) {
+ return value;
+ }
+
InterpolableTransformList& transform_list =
To<InterpolableTransformList>(*value.interpolable_value);
const InterpolableTransformList& underlying_transform_list =
diff --git a/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc b/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
index 7f46d93..b757be9 100644
--- a/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
+++ b/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
@@ -42,17 +42,23 @@
InvalidatableInterpolation::MaybeConvertPairwise(
const CSSInterpolationEnvironment& environment,
const UnderlyingValueOwner& underlying_value_owner) const {
+ const InterpolationValue null_underlying(nullptr);
for (const InterpolationType* interpolation_type : *interpolation_types_) {
if ((start_keyframe_->IsNeutral() || end_keyframe_->IsNeutral()) &&
(!underlying_value_owner ||
underlying_value_owner.GetType() != interpolation_type)) {
continue;
}
+ const InterpolationValue& underlying =
+ (underlying_value_owner &&
+ underlying_value_owner.GetType() == interpolation_type)
+ ? underlying_value_owner.Value()
+ : null_underlying;
ConversionCheckers conversion_checkers;
PairwiseInterpolationValue result =
interpolation_type->MaybeConvertPairwise(
- *start_keyframe_, *end_keyframe_, environment,
- underlying_value_owner.Value(), conversion_checkers);
+ *start_keyframe_, *end_keyframe_, environment, underlying,
+ conversion_checkers);
AddConversionCheckers(interpolation_type, conversion_checkers);
if (result) {
return MakeGarbageCollected<PairwisePrimitiveInterpolation>(
@@ -71,15 +77,20 @@
if (keyframe.IsNeutral() && !underlying_value_owner) {
return nullptr;
}
+ const InterpolationValue null_underlying(nullptr);
for (const InterpolationType* interpolation_type : *interpolation_types_) {
if (keyframe.IsNeutral() &&
underlying_value_owner.GetType() != interpolation_type) {
continue;
}
+ const InterpolationValue& underlying =
+ (underlying_value_owner &&
+ underlying_value_owner.GetType() == interpolation_type)
+ ? underlying_value_owner.Value()
+ : null_underlying;
ConversionCheckers conversion_checkers;
InterpolationValue result = interpolation_type->MaybeConvertSingle(
- keyframe, environment, underlying_value_owner.Value(),
- conversion_checkers);
+ keyframe, environment, underlying, conversion_checkers);
AddConversionCheckers(interpolation_type, conversion_checkers);
if (result) {
return MakeGarbageCollected<TypedInterpolationValue>(interpolation_type,
@@ -154,8 +165,14 @@
return false;
}
}
+ const InterpolationValue null_underlying(nullptr);
for (const auto& checker : conversion_checkers_) {
- if (!checker->IsValid(environment, underlying_value_owner.Value())) {
+ const InterpolationValue& underlying =
+ (underlying_value_owner &&
+ underlying_value_owner.GetType() == checker->GetType())
+ ? underlying_value_owner.Value()
+ : null_underlying;
+ if (!checker->IsValid(environment, underlying)) {
return false;
}
}
diff --git a/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html b/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
index f8e0a01..0ef9f7c3 100644
--- a/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
+++ b/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
@@ -144,5 +144,70 @@
{at: 1.5, is: '100px'},
]);
+CSS.registerProperty({
+ name: '--transform-list-or-none',
+ syntax: '<transform-list> | none',
+ initialValue: 'none',
+ inherits: false
+});
+
+CSS.registerProperty({
+ name: '--number-list-or-transform-list',
+ syntax: '<number>+ | <transform-list>',
+ initialValue: '100',
+ inherits: false
+});
+
+CSS.registerProperty({
+ name: '--length-list-or-transform-function-list',
+ syntax: '<length># | <transform-function>#',
+ initialValue: '10px, 20px',
+ inherits: false
+});
+
+assertComposition({
+ property: '--transform-list-or-none',
+ underlying: 'none',
+ addFrom: 'translateX(10px) scale(2)',
+ addTo: 'translateX(100px) scale(4)',
+}, [
+ {at: 0, is: 'translateX(10px) scale(2)'},
+ {at: 0.5, is: 'translateX(55px) scale(3)'},
+ {at: 1, is: 'translateX(100px) scale(4)'},
+]);
+
+assertComposition({
+ property: '--number-list-or-transform-list',
+ underlying: '100',
+ addFrom: 'translateX(10px)',
+ addTo: 'translateX(100px)',
+}, [
+ {at: 0, is: 'translateX(10px)'},
+ {at: 0.5, is: 'translateX(55px)'},
+ {at: 1, is: 'translateX(100px)'},
+]);
+
+assertComposition({
+ property: '--number-list-or-transform-list',
+ underlying: 'translateX(100px)',
+ addFrom: '10',
+ addTo: '100',
+}, [
+ {at: 0, is: '10'},
+ {at: 0.5, is: '55'},
+ {at: 1, is: '100'},
+]);
+
+assertComposition({
+ property: '--length-list-or-transform-function-list',
+ underlying: '10px, 20px',
+ addFrom: 'translateX(10px), scale(2)',
+ addTo: 'translateX(100px), scale(4)',
+}, [
+ {at: 0, is: 'translateX(10px), scale(2)'},
+ {at: 0.5, is: 'translateX(55px), scale(3)'},
+ {at: 1, is: 'translateX(100px), scale(4)'},
+]);
+
</script>
</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
index 9575744..3f0fc5bf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
@@ -15,4 +15,88 @@
expected: "200"
}, 'Animating a custom property allowing multiple list types with two mismatching types');
+animation_test({
+ syntax: "<number>+ | <transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px)"
+}, {
+ composite: "add",
+ keyframes: ["100", "200"],
+ expected: "150"
+}, 'Additive animation of <number>+ when underlying value is <transform-list>');
+
+animation_test({
+ syntax: "<number>+ | <transform-list>",
+ inherits: false,
+ initialValue: "100"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px)", "translateX(200px)"],
+ expected: "translateX(150px)"
+}, 'Additive animation of <transform-list> when underlying value is <number>+');
+
+animation_test({
+ syntax: "<transform-function> | <transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+}, {
+ composite: "add",
+ keyframes: ["translateX(200px)", "translateX(300px)"],
+ expected: "translateX(250px)"
+}, 'Additive animation of <transform-function> when underlying value is a multi-item <transform-list>');
+
+animation_test({
+ syntax: "<length># | <transform-function>#",
+ inherits: false,
+ initialValue: "10px, 20px"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px), scale(2)", "translateX(200px), scale(4)"],
+ expected: "translateX(150px), scale(3)"
+}, 'Additive animation of <transform-function># when underlying value is <length>#');
+
+animation_test({
+ syntax: "<transform-function># | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px), scale(2)", "translateX(200px), scale(4)"],
+ expected: "translateX(150px), scale(3)"
+}, 'Additive animation of <transform-function># when underlying value is none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-function>#",
+ inherits: false,
+ initialValue: "translateX(10px), scale(2)"
+ });
+
+ target.style.setProperty(name, "translateX(10px)");
+ const anim = target.animate(
+ { [name]: ["translateX(20px), scale(3)", "translateX(40px), scale(5)"] },
+ { duration: 1000, composite: "add" }
+ );
+ anim.pause();
+ anim.currentTime = 500;
+
+ // Underlying list length (1) != keyframe list length (2), falls back to replace.
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(30px), scale(4)"
+ );
+
+ // Dynamically updating underlying list to length 2 invalidates cache and composites.
+ target.style.setProperty(name, "translateX(10px), scale(2)");
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(40px), scale(5)"
+ );
+
+ target.style.removeProperty(name);
+ anim.cancel();
+}, 'Additive <transform-function># animation with dynamic underlying list length and value update');
+
</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
index 161703a6..f090251 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
@@ -65,4 +65,123 @@
assert_function: assert_matrix_equals
}, 'Animating a custom property of type <transform-list> containing multiple values and with mismatching list lengths');
+animation_test({
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px) scale(2)", "translateX(200px) scale(4)"],
+ expected: "translateX(150px) scale(3)"
+}, 'Animating a custom property of type <transform-list> | none with additivity when underlying value is none');
+
+animation_test({
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "accumulate",
+ keyframes: ["translateX(100px) scale(2)", "translateX(200px) scale(4)"],
+ expected: "translateX(150px) scale(3)"
+}, 'Animating a custom property of type <transform-list> | none with accumulation when underlying value is none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+ });
+
+ target.style.setProperty(name, "none");
+ const animation = target.animate(
+ { [name]: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"] },
+ { duration: 1000, composite: "add" }
+ );
+ animation.pause();
+ animation.currentTime = 500;
+
+ // When the underlying value is none, composition falls back to replace.
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(250px) scale(3.5)"
+ );
+
+ // Dynamically restoring a valid underlying <transform-list> invalidates the
+ // conversion cache and composites with the new underlying value.
+ target.style.setProperty(name, "translateX(100px) scale(2)");
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(100px) scale(2) translateX(250px) scale(3.5)"
+ );
+
+ target.style.removeProperty(name);
+ animation.cancel();
+}, 'Additive <transform-list> | none animation with dynamic underlying value update from none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+ });
+
+ const baseAnim = target.animate(
+ { [name]: ["var(--invalid, invalid)", "var(--invalid, invalid)"] },
+ { duration: 1000 }
+ );
+ baseAnim.pause();
+ baseAnim.currentTime = 500;
+
+ const addAnim = target.animate(
+ { [name]: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"] },
+ { duration: 1000, composite: "add" }
+ );
+ addAnim.pause();
+ addAnim.currentTime = 500;
+
+ assert_equals(
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html b/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
index f8e0a01..0ef9f7c3 100644
--- a/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
+++ b/third_party/blink/web_tests/animations/custom-properties/custom-list-type-interpolation.html
@@ -144,5 +144,70 @@
{at: 1.5, is: '100px'},
]);
+CSS.registerProperty({
+ name: '--transform-list-or-none',
+ syntax: '<transform-list> | none',
+ initialValue: 'none',
+ inherits: false
+});
+
+CSS.registerProperty({
+ name: '--number-list-or-transform-list',
+ syntax: '<number>+ | <transform-list>',
+ initialValue: '100',
+ inherits: false
+});
+
+CSS.registerProperty({
+ name: '--length-list-or-transform-function-list',
+ syntax: '<length># | <transform-function>#',
+ initialValue: '10px, 20px',
+ inherits: false
+});
+
+assertComposition({
+ property: '--transform-list-or-none',
+ underlying: 'none',
+ addFrom: 'translateX(10px) scale(2)',
+ addTo: 'translateX(100px) scale(4)',
+}, [
+ {at: 0, is: 'translateX(10px) scale(2)'},
+ {at: 0.5, is: 'translateX(55px) scale(3)'},
+ {at: 1, is: 'translateX(100px) scale(4)'},
+]);
+
+assertComposition({
+ property: '--number-list-or-transform-list',
+ underlying: '100',
+ addFrom: 'translateX(10px)',
+ addTo: 'translateX(100px)',
+}, [
+ {at: 0, is: 'translateX(10px)'},
+ {at: 0.5, is: 'translateX(55px)'},
+ {at: 1, is: 'translateX(100px)'},
+]);
+
+assertComposition({
+ property: '--number-list-or-transform-list',
+ underlying: 'translateX(100px)',
+ addFrom: '10',
+ addTo: '100',
+}, [
+ {at: 0, is: '10'},
+ {at: 0.5, is: '55'},
+ {at: 1, is: '100'},
+]);
+
+assertComposition({
+ property: '--length-list-or-transform-function-list',
+ underlying: '10px, 20px',
+ addFrom: 'translateX(10px), scale(2)',
+ addTo: 'translateX(100px), scale(4)',
+}, [
+ {at: 0, is: 'translateX(10px), scale(2)'},
+ {at: 0.5, is: 'translateX(55px), scale(3)'},
+ {at: 1, is: 'translateX(100px), scale(4)'},
+]);
+
</script>
</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
index 9575744..3f0fc5bf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-list-type-mismatch.html
@@ -15,4 +15,88 @@
expected: "200"
}, 'Animating a custom property allowing multiple list types with two mismatching types');
+animation_test({
+ syntax: "<number>+ | <transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px)"
+}, {
+ composite: "add",
+ keyframes: ["100", "200"],
+ expected: "150"
+}, 'Additive animation of <number>+ when underlying value is <transform-list>');
+
+animation_test({
+ syntax: "<number>+ | <transform-list>",
+ inherits: false,
+ initialValue: "100"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px)", "translateX(200px)"],
+ expected: "translateX(150px)"
+}, 'Additive animation of <transform-list> when underlying value is <number>+');
+
+animation_test({
+ syntax: "<transform-function> | <transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+}, {
+ composite: "add",
+ keyframes: ["translateX(200px)", "translateX(300px)"],
+ expected: "translateX(250px)"
+}, 'Additive animation of <transform-function> when underlying value is a multi-item <transform-list>');
+
+animation_test({
+ syntax: "<length># | <transform-function>#",
+ inherits: false,
+ initialValue: "10px, 20px"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px), scale(2)", "translateX(200px), scale(4)"],
+ expected: "translateX(150px), scale(3)"
+}, 'Additive animation of <transform-function># when underlying value is <length>#');
+
+animation_test({
+ syntax: "<transform-function># | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px), scale(2)", "translateX(200px), scale(4)"],
+ expected: "translateX(150px), scale(3)"
+}, 'Additive animation of <transform-function># when underlying value is none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-function>#",
+ inherits: false,
+ initialValue: "translateX(10px), scale(2)"
+ });
+
+ target.style.setProperty(name, "translateX(10px)");
+ const anim = target.animate(
+ { [name]: ["translateX(20px), scale(3)", "translateX(40px), scale(5)"] },
+ { duration: 1000, composite: "add" }
+ );
+ anim.pause();
+ anim.currentTime = 500;
+
+ // Underlying list length (1) != keyframe list length (2), falls back to replace.
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(30px), scale(4)"
+ );
+
+ // Dynamically updating underlying list to length 2 invalidates cache and composites.
+ target.style.setProperty(name, "translateX(10px), scale(2)");
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(40px), scale(5)"
+ );
+
+ target.style.removeProperty(name);
+ anim.cancel();
+}, 'Additive <transform-function># animation with dynamic underlying list length and value update');
+
</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
index 161703a6..f090251 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-animation-transform-list-multiple-values.html
@@ -65,4 +65,123 @@
assert_function: assert_matrix_equals
}, 'Animating a custom property of type <transform-list> containing multiple values and with mismatching list lengths');
+animation_test({
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "add",
+ keyframes: ["translateX(100px) scale(2)", "translateX(200px) scale(4)"],
+ expected: "translateX(150px) scale(3)"
+}, 'Animating a custom property of type <transform-list> | none with additivity when underlying value is none');
+
+animation_test({
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "none"
+}, {
+ composite: "accumulate",
+ keyframes: ["translateX(100px) scale(2)", "translateX(200px) scale(4)"],
+ expected: "translateX(150px) scale(3)"
+}, 'Animating a custom property of type <transform-list> | none with accumulation when underlying value is none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+ });
+
+ target.style.setProperty(name, "none");
+ const animation = target.animate(
+ { [name]: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"] },
+ { duration: 1000, composite: "add" }
+ );
+ animation.pause();
+ animation.currentTime = 500;
+
+ // When the underlying value is none, composition falls back to replace.
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(250px) scale(3.5)"
+ );
+
+ // Dynamically restoring a valid underlying <transform-list> invalidates the
+ // conversion cache and composites with the new underlying value.
+ target.style.setProperty(name, "translateX(100px) scale(2)");
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(100px) scale(2) translateX(250px) scale(3.5)"
+ );
+
+ target.style.removeProperty(name);
+ animation.cancel();
+}, 'Additive <transform-list> | none animation with dynamic underlying value update from none');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-list>",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+ });
+
+ const baseAnim = target.animate(
+ { [name]: ["var(--invalid, invalid)", "var(--invalid, invalid)"] },
+ { duration: 1000 }
+ );
+ baseAnim.pause();
+ baseAnim.currentTime = 500;
+
+ const addAnim = target.animate(
+ { [name]: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"] },
+ { duration: 1000, composite: "add" }
+ );
+ addAnim.pause();
+ addAnim.currentTime = 500;
+
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(250px) scale(3.5)"
+ );
+
+ addAnim.cancel();
+ baseAnim.cancel();
+}, 'Additive <transform-list> animation stacked on top of a discrete var(--invalid, invalid) animation');
+
+test(() => {
+ const name = generate_name();
+ CSS.registerProperty({
+ name,
+ syntax: "<transform-list> | none",
+ inherits: false,
+ initialValue: "translateX(100px) scale(2)"
+ });
+
+ const baseAnim = target.animate(
+ { [name]: ["none", "none"] },
+ { duration: 1000 }
+ );
+ baseAnim.pause();
+ baseAnim.currentTime = 500;
+
+ const addAnim = target.animate(
+ { [name]: ["translateX(200px) scale(3)", "translateX(300px) scale(4)"] },
+ { duration: 1000, composite: "add" }
+ );
+ addAnim.pause();
+ addAnim.currentTime = 500;
+
+ assert_equals(
+ getComputedStyle(target).getPropertyValue(name),
+ "translateX(250px) scale(3.5)"
+ );
+
+ addAnim.cancel();
+ baseAnim.cancel();
+}, 'Additive <transform-list> animation stacked on top of a discrete none animation');
+
</script>
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page