Skip to content

Commit

Permalink
[web-animations] KeyframeValue and AcceleratedEffectKeyframe shou…
Browse files Browse the repository at this point in the history
…ld derive from a shared base class

https://bugs.webkit.org/show_bug.cgi?id=264747

Reviewed by Dean Jackson.

As part of the work on threaded animation resolution (see bug 250970), we will need to have the keyframe
interpolation logic currently contained in `KeyframeEffect` also apply to `AcceleratedEffect`. This will
require a fair amount of refactoring to move all the relevant code in a shared base class for both
`KeyframeEffect` and `AcceleratedEffect`.

The first step towards this goal is to have the keyframe class backing those animation effect classes,
`KeyframeValue` and `AcceleratedEffectKeyframe`, use a shared base class. In this patch we introduce a
new `KeyframeInterpolation` class, which is currently empty except for the nested `KeyframeInterpolation::Keyframe`.
It exposes a simple set of methods that `KeyframeValue` and `AcceleratedEffectKeyframe` now override:

    double offset() const;
    std::optional<CompositeOperation> compositeOperation() const;
    bool animatesProperty(Property) const;

We also add a new abstract `AnimatedProperty` which can be either `AnimatableCSSProperty` or `AcceleratedEffectProperty`.

* Source/WebCore/Headers.cmake:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::getKeyframes):
(WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
(WebCore::KeyframeEffect::computeExtentOfTransformAnimation const):
(WebCore::KeyframeEffect::progressUntilNextStep const):
(WebCore::KeyframeEffect::computeHasImplicitKeyframeForAcceleratedProperty):
* Source/WebCore/animation/KeyframeInterpolation.h: Added.
(WebCore::KeyframeInterpolation::Keyframe::isAcceleratedEffectKeyframe const):
(WebCore::KeyframeInterpolation::Keyframe::isKeyframeValue const):
* Source/WebCore/animation/WebAnimationTypes.h:
* Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp:
(WebCore::buildObjectForKeyframes):
* Source/WebCore/platform/animation/AcceleratedEffect.cpp:
(WebCore::AcceleratedEffect::Keyframe::Keyframe):
(WebCore::AcceleratedEffect::Keyframe::animatesProperty const):
(WebCore::AcceleratedEffect::Keyframe::clone const):
(WebCore::AcceleratedEffect::create):
(WebCore::AcceleratedEffect::AcceleratedEffect):
(WebCore::AcceleratedEffectKeyframe::clone const): Deleted.
* Source/WebCore/platform/animation/AcceleratedEffect.h:
(WebCore::AcceleratedEffect::keyframes const):
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::startAnimation):
* Source/WebCore/rendering/style/KeyframeList.cpp:
(WebCore::KeyframeList::operator== const):
(WebCore::KeyframeList::insert):
(WebCore::KeyframeList::hasImplicitKeyframes const):
(WebCore::KeyframeList::copyKeyframes):
(WebCore::KeyframeList::fillImplicitKeyframes):
(WebCore::KeyframeValue::animatesProperty const):
(WebCore::KeyframeValue::containsProperty const): Deleted.
* Source/WebCore/rendering/style/KeyframeList.h:
(WebCore::KeyframeValue::KeyframeValue): Deleted.
(WebCore::KeyframeValue::properties const): Deleted.
(WebCore::KeyframeValue::key const): Deleted.
(WebCore::KeyframeValue::setKey): Deleted.
(WebCore::KeyframeValue::style const): Deleted.
(WebCore::KeyframeValue::setStyle): Deleted.
(WebCore::KeyframeValue::timingFunction const): Deleted.
(WebCore::KeyframeValue::setTimingFunction): Deleted.
(WebCore::KeyframeValue::compositeOperation const): Deleted.
(WebCore::KeyframeValue::setCompositeOperation): Deleted.
(WebCore::KeyframeValue::containsDirectionAwareProperty const): Deleted.
(WebCore::KeyframeValue::setContainsDirectionAwareProperty): Deleted.
* Source/WebCore/style/StyleResolver.cpp:
(WebCore::Style::Resolver::keyframeStylesForAnimation):
* Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in:

Canonical link: https://commits.webkit.org/270648@main
  • Loading branch information
graouts committed Nov 13, 2023
1 parent 4395f5a commit 893f4b2
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 119 deletions.
1 change: 1 addition & 0 deletions Source/WebCore/Headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
animation/IterationCompositeOperation.h
animation/KeyframeAnimationOptions.h
animation/KeyframeEffectOptions.h
animation/KeyframeInterpolation.h
animation/PlaybackDirection.h
animation/WebAnimationTypes.h

Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,7 @@
71025EE01F99F1EC004A250C /* JSDocumentTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = 71025EDC1F99F1A8004A250C /* JSDocumentTimeline.h */; };
71025EE21F99F1EC004A250C /* JSWebAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 71025ED81F99F1A6004A250C /* JSWebAnimation.h */; };
7108EEE62418ED5A005C3DA7 /* WebAnimationTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 7108EEE42418ED53005C3DA7 /* WebAnimationTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
710BB1032AFE585F005C3139 /* KeyframeInterpolation.h in Headers */ = {isa = PBXBuildFile; fileRef = 710BB1012AFE584F005C3139 /* KeyframeInterpolation.h */; settings = {ATTRIBUTES = (Private, ); }; };
7111243E216FA71100EB7B67 /* CompositeOperationOrAuto.h in Headers */ = {isa = PBXBuildFile; fileRef = 7111243B216FA6E000EB7B67 /* CompositeOperationOrAuto.h */; settings = {ATTRIBUTES = (Private, ); }; };
71112441216FA7CD00EB7B67 /* JSCompositeOperationOrAuto.h in Headers */ = {isa = PBXBuildFile; fileRef = 71112440216FA7BE00EB7B67 /* JSCompositeOperationOrAuto.h */; settings = {ATTRIBUTES = (Private, ); }; };
7116E2CC1FED75DC00C06FDE /* ComputedEffectTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 7116E2CB1FED75D100C06FDE /* ComputedEffectTiming.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -12536,6 +12537,7 @@
71025EDB1F99F1A8004A250C /* JSDocumentTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDocumentTimeline.cpp; sourceTree = "<group>"; };
71025EDC1F99F1A8004A250C /* JSDocumentTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDocumentTimeline.h; sourceTree = "<group>"; };
7108EEE42418ED53005C3DA7 /* WebAnimationTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebAnimationTypes.h; sourceTree = "<group>"; };
710BB1012AFE584F005C3139 /* KeyframeInterpolation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyframeInterpolation.h; sourceTree = "<group>"; };
710FA74B1DEE576D004C715E /* controls-bar.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "controls-bar.js"; sourceTree = "<group>"; };
710FA74C1DEE577E004C715E /* controls-visibility-support.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "controls-visibility-support.js"; sourceTree = "<group>"; };
7111243B216FA6E000EB7B67 /* CompositeOperationOrAuto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompositeOperationOrAuto.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -27689,6 +27691,7 @@
71247E301FEA5F7E008C08CE /* KeyframeEffectOptions.idl */,
71A58195236F466500D81A24 /* KeyframeEffectStack.cpp */,
71A58193236F466400D81A24 /* KeyframeEffectStack.h */,
710BB1012AFE584F005C3139 /* KeyframeInterpolation.h */,
7120733D216DFAF100C78329 /* OptionalEffectTiming.h */,
7120733F216DFAF200C78329 /* OptionalEffectTiming.idl */,
712BE47E1FE8649D002031CC /* PlaybackDirection.h */,
Expand Down Expand Up @@ -40148,6 +40151,7 @@
71556CB41F9F09BA00E78D08 /* KeyframeEffect.h in Headers */,
71247E3A1FEA5F86008C08CE /* KeyframeEffectOptions.h in Headers */,
71A58196236F467600D81A24 /* KeyframeEffectStack.h in Headers */,
710BB1032AFE585F005C3139 /* KeyframeInterpolation.h in Headers */,
BC5EBA110E823E4700B25965 /* KeyframeList.h in Headers */,
E15FF7D518C9553800FE4C87 /* KeypressCommand.h in Headers */,
A456FA2711AD4A830020B420 /* LabelsNodeList.h in Headers */,
Expand Down
38 changes: 19 additions & 19 deletions Source/WebCore/animation/KeyframeEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,11 +704,11 @@ auto KeyframeEffect::getKeyframes() -> Vector<ComputedKeyframe>

for (auto& keyframe : computedKeyframeList) {
auto& style = *keyframe.style();
auto* keyframeRule = keyframeRuleForKey(keyframe.key());
auto* keyframeRule = keyframeRuleForKey(keyframe.offset());

ComputedKeyframe computedKeyframe;
computedKeyframe.offset = keyframe.key();
computedKeyframe.computedOffset = keyframe.key();
computedKeyframe.offset = keyframe.offset();
computedKeyframe.computedOffset = keyframe.offset();
// For CSS transitions, all keyframes should return "linear" since the effect's global timing function applies.
computedKeyframe.easing = is<CSSTransition>(animation()) ? "linear"_s : timingFunctionForBlendingKeyframe(keyframe)->cssText();

Expand Down Expand Up @@ -1516,8 +1516,8 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
unsigned numberOfKeyframesWithOneOffset = 0;
Vector<const KeyframeValue*> propertySpecificKeyframes;
for (auto& keyframe : m_blendingKeyframes) {
auto offset = keyframe.key();
if (!keyframe.containsProperty(property))
auto offset = keyframe.offset();
if (!keyframe.animatesProperty(property))
continue;
if (!offset)
numberOfKeyframesWithZeroOffset++;
Expand Down Expand Up @@ -1570,7 +1570,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
size_t indexOfLastKeyframeWithZeroOffset = 0;
int indexOfFirstKeyframeToAddToIntervalEndpoints = -1;
for (size_t i = 0; i < propertySpecificKeyframes.size(); ++i) {
auto offset = propertySpecificKeyframes[i]->key();
auto offset = propertySpecificKeyframes[i]->offset();
if (!offset)
indexOfLastKeyframeWithZeroOffset = i;
if (offset <= iterationProgress && offset < 1)
Expand Down Expand Up @@ -1610,7 +1610,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
// Only do this for the 0 keyframe if it was provided explicitly, since otherwise we want to use the "neutral value
// for composition" which really means we don't want to do anything but rather just use the underlying style which
// is already set on startKeyframe.
if (startKeyframe.key() || !hasImplicitZeroKeyframe) {
if (startKeyframe.offset() || !hasImplicitZeroKeyframe) {
auto startKeyframeCompositeOperation = startKeyframe.compositeOperation().value_or(m_compositeOperation);
if (startKeyframeCompositeOperation != CompositeOperation::Replace)
CSSPropertyAnimation::blendProperty(*this, property, startKeyframeStyle, targetStyle, *startKeyframe.style(), 1, startKeyframeCompositeOperation);
Expand All @@ -1619,7 +1619,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
// Only do this for the 1 keyframe if it was provided explicitly, since otherwise we want to use the "neutral value
// for composition" which really means we don't want to do anything but rather just use the underlying style which
// is already set on endKeyframe.
if (endKeyframe.key() != 1 || !hasImplicitOneKeyframe) {
if (endKeyframe.offset() != 1 || !hasImplicitOneKeyframe) {
auto endKeyframeCompositeOperation = endKeyframe.compositeOperation().value_or(m_compositeOperation);
if (endKeyframeCompositeOperation != CompositeOperation::Replace)
CSSPropertyAnimation::blendProperty(*this, property, endKeyframeStyle, targetStyle, *endKeyframe.style(), 1, endKeyframeCompositeOperation);
Expand All @@ -1633,9 +1633,9 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
// replace the property value of target property on keyframe with the result of combining the
// property value on the final keyframe in property-specific keyframes (Va) with the property
// value on keyframe (Vb) using the accumulation procedure defined for target property.
if (!startKeyframe.key() && !hasImplicitZeroKeyframe)
if (!startKeyframe.offset() && !hasImplicitZeroKeyframe)
CSSPropertyAnimation::blendProperty(*this, property, startKeyframeStyle, *endKeyframe.style(), startKeyframeStyle, 1, CompositeOperation::Accumulate);
if (endKeyframe.key() == 1 && !hasImplicitOneKeyframe)
if (endKeyframe.offset() == 1 && !hasImplicitOneKeyframe)
CSSPropertyAnimation::blendProperty(*this, property, endKeyframeStyle, *endKeyframe.style(), endKeyframeStyle, 1, CompositeOperation::Accumulate);
}
}
Expand All @@ -1648,10 +1648,10 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
}

// 14. Let start offset be the computed keyframe offset of the first keyframe in interval endpoints.
auto startOffset = startKeyframe.key();
auto startOffset = startKeyframe.offset();

// 15. Let end offset be the computed keyframe offset of last keyframe in interval endpoints.
auto endOffset = endKeyframe.key();
auto endOffset = endKeyframe.offset();

// 16. Let interval distance be the result of evaluating (iteration progress - start offset) / (end offset - start offset).
auto intervalDistance = (iterationProgress - startOffset) / (endOffset - startOffset);
Expand Down Expand Up @@ -2256,9 +2256,9 @@ bool KeyframeEffect::computeExtentOfTransformAnimation(LayoutRect& bounds) const
const auto* keyframeStyle = keyframe.style();

// FIXME: maybe for declarative animations we always say it's true for the first and last keyframe.
if (!keyframe.containsProperty(CSSPropertyTransform)) {
if (!keyframe.animatesProperty(CSSPropertyTransform)) {
// If the first keyframe is missing transform style, use the current style.
if (!keyframe.key())
if (!keyframe.offset())
keyframeStyle = implicitStyle;
else
continue;
Expand Down Expand Up @@ -2372,7 +2372,7 @@ std::optional<double> KeyframeEffect::progressUntilNextStep(double iterationProg
};

for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
auto intervalEndProgress = m_blendingKeyframes[i].key();
auto intervalEndProgress = m_blendingKeyframes[i].offset();
// We can stop once we find a keyframe for which the progress is more than the provided iteration progress.
if (intervalEndProgress <= iterationProgress)
continue;
Expand All @@ -2386,15 +2386,15 @@ std::optional<double> KeyframeEffect::progressUntilNextStep(double iterationProg
return std::nullopt;
}

return progressUntilNextStepInInterval(m_blendingKeyframes[i - 1].key(), intervalEndProgress, timingFunctionForKeyframeAtIndex(i - 1));
return progressUntilNextStepInInterval(m_blendingKeyframes[i - 1].offset(), intervalEndProgress, timingFunctionForKeyframeAtIndex(i - 1));
}

// If we end up here, then this means we are dealing with an implicit 100% keyframe.
// This will be a linear timing function unless we're dealing with a CSS Animation which might have
// the default timing function for its keyframes defined on its backing Animation object.
auto& lastExplicitKeyframe = m_blendingKeyframes[m_blendingKeyframes.size() - 1];
if (auto* cssAnimation = dynamicDowncast<CSSAnimation>(animation()))
return progressUntilNextStepInInterval(lastExplicitKeyframe.key(), 1, cssAnimation->backingAnimation().timingFunction());
return progressUntilNextStepInInterval(lastExplicitKeyframe.offset(), 1, cssAnimation->backingAnimation().timingFunction());

// In any other case, we are not dealing with an interval with a steps() timing function.
return std::nullopt;
Expand Down Expand Up @@ -2481,11 +2481,11 @@ void KeyframeEffect::computeHasImplicitKeyframeForAcceleratedProperty()
for (auto& keyframe : m_blendingKeyframes) {
// If the keyframe is for 0% or 100%, let's remove all of its properties from
// our list of implicit properties.
if (!implicitZeroProperties.isEmpty() && !keyframe.key()) {
if (!implicitZeroProperties.isEmpty() && !keyframe.offset()) {
for (auto property : keyframe.properties())
implicitZeroProperties.remove(property);
}
if (!implicitOneProperties.isEmpty() && keyframe.key() == 1) {
if (!implicitOneProperties.isEmpty() && keyframe.offset() == 1) {
for (auto property : keyframe.properties())
implicitOneProperties.remove(property);
}
Expand Down
57 changes: 57 additions & 0 deletions Source/WebCore/animation/KeyframeInterpolation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include "CompositeOperation.h"
#include "WebAnimationTypes.h"
#include <optional>
#include <wtf/Seconds.h>

namespace WebCore {

class KeyframeInterpolation {
public:
using Property = std::variant<AnimatableCSSProperty, AcceleratedEffectProperty>;

class Keyframe {
public:
virtual double offset() const = 0;
virtual std::optional<CompositeOperation> compositeOperation() const = 0;
virtual bool animatesProperty(Property) const = 0;

virtual bool isAcceleratedEffectKeyframe() const { return false; }
virtual bool isKeyframeValue() const { return false; }

virtual ~Keyframe() = default;
};
};

} // namespace WebCore

#define SPECIALIZE_TYPE_TRAITS_KEYFRAME_INTERPOLATION_KEYFRAME(ToValueTypeName, predicate) \
SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \
static bool isType(const WebCore::KeyframeInterpolation::Keyframe& value) { return value.predicate; } \
SPECIALIZE_TYPE_TRAITS_END()
18 changes: 18 additions & 0 deletions Source/WebCore/animation/WebAnimationTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ using CSSAnimationCollection = ListHashSet<Ref<CSSAnimation>>;
using AnimatableCSSProperty = std::variant<CSSPropertyID, AtomString>;
using AnimatableCSSPropertyToTransitionMap = HashMap<AnimatableCSSProperty, Ref<CSSTransition>>;

enum class AcceleratedEffectProperty : uint16_t {
Invalid = 1 << 0,
Opacity = 1 << 1,
Transform = 1 << 2,
Translate = 1 << 3,
Rotate = 1 << 4,
Scale = 1 << 5,
OffsetPath = 1 << 6,
OffsetDistance = 1 << 7,
OffsetPosition = 1 << 8,
OffsetAnchor = 1 << 9,
OffsetRotate = 1 << 10,
Filter = 1 << 11,
#if ENABLE(FILTERS_LEVEL_2)
BackdropFilter = 1 << 12
#endif
};

struct CSSPropertiesBitSet {
WTF::BitSet<numCSSProperties> m_properties { };
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ static Ref<JSON::ArrayOf<Protocol::Animation::Keyframe>> buildObjectForKeyframes
auto& style = *blendingKeyframe.style();

auto keyframePayload = Protocol::Animation::Keyframe::create()
.setOffset(blendingKeyframe.key())
.setOffset(blendingKeyframe.offset())
.release();

RefPtr<TimingFunction> timingFunction;
Expand Down
Loading

0 comments on commit 893f4b2

Please sign in to comment.