Skip to content
Browse files
[web-animations] custom properties should support interpolation with …
…a single keyframe

Reviewed by Antti Koivisto.

Custom properties can specify an initial value when registered. However, that value is not
available in RenderStyle if the custom property is not provided with an explicit value. This
means that when we added basic support for interpolating custom properties in bug 249312, we
would fail to gather the right values for interpolating as we'd have a null value if the keyframes
did not set explicit values.

We can get to the initial value of a custom property through the custom property registry held
by the document. So we add a new document() method to CSSPropertyBlendingClient such that we may
be able to read from this registry when interpolating from within CSSPropertyAnimation.

Then we add a static method customPropertyValuesForBlending that returns a pair of CSSCustomPropertyValue
pointers containing either the explicit value set for a custom property, or its initial value.

Now we are guaranteed to get the correct values when interpolating.

* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/animation/custom-property-animation-length-expected.txt:
* Source/WebCore/animation/CSSPropertyAnimation.cpp:
* Source/WebCore/animation/CSSPropertyBlendingClient.h:
* Source/WebCore/animation/KeyframeEffect.h:

Canonical link:
  • Loading branch information
graouts committed Dec 15, 2022
1 parent 24ceea2 commit c3591d297f097a553bbf668e6096d96fb62e546d
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 8 deletions.
@@ -1,7 +1,7 @@

PASS Animating a custom property of type <length>
FAIL Animating a custom property of type <length> with a single keyframe assert_equals: expected "150px" but got "100px"
PASS Animating a custom property of type <length> with a single keyframe
FAIL Animating a custom property of type <length> with additivity assert_equals: expected "350px" but got "250px"
FAIL Animating a custom property of type <length> with a single keyframe and additivity assert_equals: expected "250px" but got "100px"
FAIL Animating a custom property of type <length> with a single keyframe and additivity assert_equals: expected "250px" but got "200px"
FAIL Animating a custom property of type <length> with iterationComposite assert_equals: expected "250px" but got "50px"

@@ -35,12 +35,14 @@
#include "CSSPrimitiveValue.h"
#include "CSSPropertyBlendingClient.h"
#include "CSSPropertyNames.h"
#include "CSSRegisteredCustomProperty.h"
#include "CachedImage.h"
#include "CalculationValue.h"
#include "ColorBlending.h"
#include "ComputedStyleExtractor.h"
#include "ContentData.h"
#include "CounterDirectives.h"
#include "Document.h"
#include "FloatConversion.h"
#include "FontCascade.h"
#include "FontSelectionAlgorithm.h"
@@ -3932,20 +3934,37 @@ static Ref<CSSCustomPropertyValue> blendedCSSCustomPropertyValue(const CSSCustom
return CSSCustomPropertyValue::create(blendingContext.progress < 0.5 ? from : to);

static std::pair<const CSSCustomPropertyValue*, const CSSCustomPropertyValue*> customPropertyValuesForBlending(const CSSPropertyBlendingClient& client, const AtomString& customProperty, const CSSCustomPropertyValue* fromValue, const CSSCustomPropertyValue* toValue)
// FIXME: it would be convenient if RenderStyle stored the initialValue
// for a custom property that was not explicitly set.
auto initialValue = [&]() -> const CSSCustomPropertyValue* {
if (auto* document = client.document()) {
if (auto registered = document->registeredCSSCustomProperties().get(customProperty))
return registered->initialValue();
return nullptr;

if (!!fromValue == !!toValue)
return { fromValue, toValue };
if (!fromValue)
return { initialValue(), toValue };
return { fromValue, initialValue() };

static void blendCustomProperty(const CSSPropertyBlendingClient& client, const AtomString& customProperty, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress, CompositeOperation compositeOperation, IterationCompositeOperation iterationCompositeOperation, double currentIteration)
CSSPropertyBlendingContext blendingContext { progress, false, compositeOperation, client, iterationCompositeOperation, currentIteration };

auto* fromValue = from.nonInheritedCustomProperties().get(customProperty);
auto* toValue = to.nonInheritedCustomProperties().get(customProperty);
auto [fromValue, toValue] = customPropertyValuesForBlending(client, customProperty, from.nonInheritedCustomProperties().get(customProperty), to.nonInheritedCustomProperties().get(customProperty));
if (fromValue && toValue)
destination.setNonInheritedCustomPropertyValue(customProperty, blendedCSSCustomPropertyValue(*fromValue, *toValue, blendingContext));

auto* fromValue = from.inheritedCustomProperties().get(customProperty);
auto* toValue = to.inheritedCustomProperties().get(customProperty);
auto [fromValue, toValue] = customPropertyValuesForBlending(client, customProperty, from.inheritedCustomProperties().get(customProperty), to.inheritedCustomProperties().get(customProperty));
if (fromValue && toValue)
destination.setInheritedCustomPropertyValue(customProperty, blendedCSSCustomPropertyValue(*fromValue, *toValue, blendingContext));
@@ -29,12 +29,13 @@

namespace WebCore {

class Document;
class RenderElement;
class RenderStyle;

class CSSPropertyBlendingClient {

virtual Document* document() const = 0;
virtual RenderElement* renderer() const = 0;
virtual const RenderStyle& currentStyle() const = 0;
virtual std::optional<unsigned> transformFunctionListPrefix() const = 0;
@@ -135,6 +135,7 @@ class KeyframeEffect : public AnimationEffect

void willChangeRenderer();

Document* document() const override;
RenderElement* renderer() const override;
const RenderStyle& currentStyle() const override;
bool triggersStackingContext() const { return m_triggersStackingContext; }
@@ -203,7 +204,6 @@ class KeyframeEffect : public AnimationEffect
bool m_couldOriginallyPreventAcceleration;

Document* document() const;
void updateEffectStackMembership();
void copyPropertiesFromSource(Ref<KeyframeEffect>&&);
void didChangeTargetStyleable(const std::optional<const Styleable>&);

0 comments on commit c3591d2

Please sign in to comment.