⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 286544 in webkit


Ignore:
Timestamp:
Dec 6, 2021, 9:20:08 AM (5 years ago)
Author:
graouts@webkit.org
Message:

Clean up virtual methods on AnimationEffect
https://bugs.webkit.org/show_bug.cgi?id=233868

Reviewed by Simon Fraser.

To this day, the only concrete subclass of AnimationEffect has been KeyframeEffect. Soon, we'll introduce
another concrete subclass for callback-based animations leveraging the Web Animations mode: CustomEffect.
To prepare for this new class, we clean up the virtual methods on AnimationEffect such that they make a
bit more sense:

  • much of the implementation of timeToNextTick() is brought from KeyframeEffect to AnimationEffect to

account for the cases which are purely based on timing properties shared by any potential AnimationEffect
subclass,

  • we add a new ticksContinouslyWhileActive() method which allows subclasses to indicate whether they require

continuous scheduling while active, which is true for KeyframeEffect unless it has no keyframes, no CSS
properties set on its keyframes or is running fully accelerated,

  • much of the implementation of setAnimation() is brought from KeyframeEffect to AnimationEffect since

updating the relevance of the animation based on a change of effect should apply to any AnimationEffect,

  • the apply() and invalidate() methods are moved from AnimationEffect to KeyframeEffect alone since they

really are specific to KeyframeEffect's interaction with style resolution,

  • all the other virtual methods on AnimationEffect have stub implementations such that new subclasses

only need to override them as required: animationDidTick(), animationDidPlay(),
animationDidChangeTimingProperties(), animationWasCanceled(), animationSuspensionStateDidChange(bool)
and animationTimelineDidChange(AnimationTimeline*).

  • animation/AnimationEffect.cpp:

(WebCore::AnimationEffect::setAnimation):
(WebCore::AnimationEffect::timeToNextTick const):

  • animation/AnimationEffect.h:

(WebCore::AnimationEffect::animationDidTick):
(WebCore::AnimationEffect::animationDidPlay):
(WebCore::AnimationEffect::animationDidChangeTimingProperties):
(WebCore::AnimationEffect::animationWasCanceled):
(WebCore::AnimationEffect::animationSuspensionStateDidChange):
(WebCore::AnimationEffect::animationTimelineDidChange):
(WebCore::AnimationEffect::ticksContinouslyWhileActive const):
(WebCore::AnimationEffect::setAnimation): Deleted.
(WebCore::AnimationEffect::timeToNextTick const): Deleted.

  • animation/KeyframeEffect.cpp:

(WebCore::KeyframeEffect::setAnimation):
(WebCore::KeyframeEffect::ticksContinouslyWhileActive const):
(WebCore::KeyframeEffect::timeToNextTick const):

  • animation/KeyframeEffect.h:
  • animation/WebAnimation.cpp:

(WebCore::WebAnimation::invalidateEffect):
(WebCore::WebAnimation::resolve):
(WebCore::WebAnimation::timeToNextTick const):

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r286543 r286544  
     12021-12-06  Antoine Quint  <graouts@webkit.org>
     2
     3        Clean up virtual methods on AnimationEffect
     4        https://bugs.webkit.org/show_bug.cgi?id=233868
     5
     6        Reviewed by Simon Fraser.
     7
     8        To this day, the only concrete subclass of AnimationEffect has been KeyframeEffect. Soon, we'll introduce
     9        another concrete subclass for callback-based animations leveraging the Web Animations mode: CustomEffect.
     10        To prepare for this new class, we clean up the virtual methods on AnimationEffect such that they make a
     11        bit more sense:
     12
     13        - much of the implementation of timeToNextTick() is brought from KeyframeEffect to AnimationEffect to
     14        account for the cases which are purely based on timing properties shared by any potential AnimationEffect
     15        subclass,
     16
     17        - we add a new ticksContinouslyWhileActive() method which allows subclasses to indicate whether they require
     18        continuous scheduling while active, which is true for KeyframeEffect unless it has no keyframes, no CSS
     19        properties set on its keyframes or is running fully accelerated,
     20
     21        - much of the implementation of setAnimation() is brought from KeyframeEffect to AnimationEffect since
     22        updating the relevance of the animation based on a change of effect should apply to any AnimationEffect,
     23
     24        - the apply() and invalidate() methods are moved from AnimationEffect to KeyframeEffect alone since they
     25        really are specific to KeyframeEffect's interaction with style resolution,
     26
     27        - all the other virtual methods on AnimationEffect have stub implementations such that new subclasses
     28        only need to override them as required: animationDidTick(), animationDidPlay(),
     29        animationDidChangeTimingProperties(), animationWasCanceled(), animationSuspensionStateDidChange(bool)
     30        and animationTimelineDidChange(AnimationTimeline*).
     31
     32        * animation/AnimationEffect.cpp:
     33        (WebCore::AnimationEffect::setAnimation):
     34        (WebCore::AnimationEffect::timeToNextTick const):
     35        * animation/AnimationEffect.h:
     36        (WebCore::AnimationEffect::animationDidTick):
     37        (WebCore::AnimationEffect::animationDidPlay):
     38        (WebCore::AnimationEffect::animationDidChangeTimingProperties):
     39        (WebCore::AnimationEffect::animationWasCanceled):
     40        (WebCore::AnimationEffect::animationSuspensionStateDidChange):
     41        (WebCore::AnimationEffect::animationTimelineDidChange):
     42        (WebCore::AnimationEffect::ticksContinouslyWhileActive const):
     43        (WebCore::AnimationEffect::setAnimation): Deleted.
     44        (WebCore::AnimationEffect::timeToNextTick const): Deleted.
     45        * animation/KeyframeEffect.cpp:
     46        (WebCore::KeyframeEffect::setAnimation):
     47        (WebCore::KeyframeEffect::ticksContinouslyWhileActive const):
     48        (WebCore::KeyframeEffect::timeToNextTick const):
     49        * animation/KeyframeEffect.h:
     50        * animation/WebAnimation.cpp:
     51        (WebCore::WebAnimation::invalidateEffect):
     52        (WebCore::WebAnimation::resolve):
     53        (WebCore::WebAnimation::timeToNextTick const):
     54
    1552021-12-06  Nikolas Zimmermann  <nzimmermann@igalia.com>
    256
  • trunk/Source/WebCore/animation/AnimationEffect.cpp

    r284095 r286544  
    4444}
    4545
     46void AnimationEffect::setAnimation(WebAnimation* animation)
     47{
     48    if (m_animation == animation)
     49        return;
     50
     51    m_animation = animation;
     52    if (animation)
     53        animation->updateRelevance();
     54}
     55
    4656EffectTiming AnimationEffect::getBindingsTiming() const
    4757{
     
    552562}
    553563
     564Seconds AnimationEffect::timeToNextTick(BasicEffectTiming timing) const
     565{
     566    switch (timing.phase) {
     567    case AnimationEffectPhase::Before:
     568        // The effect is in its "before" phase, in this case we can wait until it enters its "active" phase.
     569        return delay() - *timing.localTime;
     570    case AnimationEffectPhase::Active: {
     571        if (!ticksContinouslyWhileActive())
     572            return endTime() - *timing.localTime;
     573        if (auto iterationProgress = getComputedTiming().simpleIterationProgress) {
     574            // In case we're in a range that uses a steps() timing function, we can compute the time until the next step starts.
     575            if (auto progressUntilNextStep = this->progressUntilNextStep(*iterationProgress))
     576                return iterationDuration() * *progressUntilNextStep;
     577        }
     578        // Other effects that continuously tick in the "active" phase will need to update their animated
     579        // progress at the immediate next opportunity.
     580        return 0_s;
     581    }
     582    case AnimationEffectPhase::After:
     583        // The effect is in its after phase, which means it will no longer update its progress, so it doens't need a tick.
     584        return Seconds::infinity();
     585    case AnimationEffectPhase::Idle:
     586        ASSERT_NOT_REACHED();
     587        return Seconds::infinity();
     588    }
     589
     590    ASSERT_NOT_REACHED();
     591    return Seconds::infinity();
     592}
     593
    554594} // namespace WebCore
  • trunk/Source/WebCore/animation/AnimationEffect.h

    r284693 r286544  
    4848namespace WebCore {
    4949
    50 namespace Style {
    51 struct ResolutionContext;
    52 }
    53 
    5450class AnimationEffect : public RefCounted<AnimationEffect>, public CanMakeWeakPtr<AnimationEffect> {
    5551public:
     
    6662    ExceptionOr<void> updateTiming(std::optional<OptionalEffectTiming>);
    6763
    68     virtual void apply(RenderStyle& targetStyle, const Style::ResolutionContext&, std::optional<Seconds> = std::nullopt) = 0;
    69     virtual void invalidate() = 0;
    70     virtual void animationDidTick() = 0;
    71     virtual void animationDidPlay() = 0;
    72     virtual void animationDidChangeTimingProperties() = 0;
    73     virtual void animationWasCanceled() = 0;
    74     virtual void animationSuspensionStateDidChange(bool) = 0;
    75     virtual void animationTimelineDidChange(AnimationTimeline*) = 0;
     64    virtual void animationDidTick() { };
     65    virtual void animationDidPlay() { };
     66    virtual void animationDidChangeTimingProperties() { };
     67    virtual void animationWasCanceled() { };
     68    virtual void animationSuspensionStateDidChange(bool) { };
     69    virtual void animationTimelineDidChange(AnimationTimeline*) { };
    7670
    7771    WebAnimation* animation() const { return m_animation.get(); }
    78     virtual void setAnimation(WebAnimation* animation) { m_animation = animation; }
     72    virtual void setAnimation(WebAnimation*);
    7973
    8074    Seconds delay() const { return m_delay; }
     
    107101    void updateStaticTimingProperties();
    108102
    109     virtual Seconds timeToNextTick() const { return Seconds::infinity(); }
     103    virtual Seconds timeToNextTick(BasicEffectTiming) const;
    110104
    111105protected:
    112106    explicit AnimationEffect();
    113107
     108    virtual bool ticksContinouslyWhileActive() const { return false; }
    114109    virtual std::optional<double> progressUntilNextStep(double) const;
    115110
  • trunk/Source/WebCore/animation/KeyframeEffect.cpp

    r285728 r286544  
    11631163    bool animationChanged = animation != this->animation();
    11641164    AnimationEffect::setAnimation(animation);
    1165 
    1166     if (!animationChanged)
    1167         return;
    1168 
    1169     if (animation)
    1170         animation->updateRelevance();
    1171     updateEffectStackMembership();
     1165    if (animationChanged)
     1166        updateEffectStackMembership();
    11721167}
    11731168
     
    21212116}
    21222117
    2123 Seconds KeyframeEffect::timeToNextTick() const
    2124 {
    2125     auto timing = getBasicTiming();
    2126     switch (timing.phase) {
    2127     case AnimationEffectPhase::Before:
    2128         // The effect is in its "before" phase, in this case we can wait until it enters its "active" phase.
    2129         return delay() - *timing.localTime;
    2130     case AnimationEffectPhase::Active: {
    2131         auto doesNotAffectStyles = m_blendingKeyframes.isEmpty() || m_blendingKeyframes.properties().isEmpty();
    2132         auto completelyAcceleratedAndRunning = isCompletelyAccelerated() && isRunningAccelerated();
    2133         if (doesNotAffectStyles || completelyAcceleratedAndRunning) {
    2134             // In the case of fully accelerated running effects and effects that don't actually target any CSS property,
    2135             // we do not have a need to invalidate styles.
    2136             if (is<CSSAnimation>(animation())) {
    2137                 // However, CSS Animations need to trigger "animationiteration" events, in this case we must wait until the next iteration.
    2138                 if (auto iterationProgress = getComputedTiming().simpleIterationProgress)
    2139                     return iterationDuration() * (1 - *iterationProgress);
    2140             }
    2141             // Other running effects in the "active" phase can wait until they end.
    2142             return endTime() - *timing.localTime;
    2143         }
    2144         if (auto iterationProgress = getComputedTiming().simpleIterationProgress) {
    2145             // In case we're in a range that uses a steps() timing function, we can compute the time until the next step starts.
    2146             if (auto progressUntilNextStep = this->progressUntilNextStep(*iterationProgress))
    2147                 return iterationDuration() * *progressUntilNextStep;
    2148         }
    2149         // Other effects in the "active" phase will need to update their animated value at the immediate next opportunity.
    2150         return 0_s;
    2151     }
    2152     case AnimationEffectPhase::After:
    2153         // The effect is in its after phase, which means it will no longer update its value, so it doens't need a tick.
    2154         return Seconds::infinity();
    2155     case AnimationEffectPhase::Idle:
    2156         ASSERT_NOT_REACHED();
    2157         return Seconds::infinity();
    2158     }
    2159 
    2160     ASSERT_NOT_REACHED();
    2161     return Seconds::infinity();
     2118bool KeyframeEffect::ticksContinouslyWhileActive() const
     2119{
     2120    auto doesNotAffectStyles = m_blendingKeyframes.isEmpty() || m_blendingKeyframes.properties().isEmpty();
     2121    if (doesNotAffectStyles)
     2122        return false;
     2123
     2124    if (isCompletelyAccelerated() && isRunningAccelerated())
     2125        return false;
     2126
     2127    return true;
     2128}
     2129
     2130Seconds KeyframeEffect::timeToNextTick(BasicEffectTiming timing) const
     2131{
     2132    if (timing.phase == AnimationEffectPhase::Active) {
     2133        // CSS Animations need to trigger "animationiteration" events even if there is no need to
     2134        // update styles while animating, so if we're dealing with one we must wait until the next iteration.
     2135        if (!ticksContinouslyWhileActive() && is<CSSAnimation>(animation())) {
     2136            if (auto iterationProgress = getComputedTiming().simpleIterationProgress)
     2137                return iterationDuration() * (1 - *iterationProgress);
     2138        }
     2139    }
     2140
     2141    return AnimationEffect::timeToNextTick(timing);
    21622142}
    21632143
  • trunk/Source/WebCore/animation/KeyframeEffect.h

    r285397 r286544  
    4747class FilterOperations;
    4848
     49namespace Style {
     50struct ResolutionContext;
     51}
     52
    4953class KeyframeEffect : public AnimationEffect
    5054    , public CSSPropertyBlendingClient {
     
    5458    static Ref<KeyframeEffect> create(const Element&, PseudoId);
    5559    ~KeyframeEffect() { }
    56 
    57     bool isKeyframeEffect() const final { return true; }
    5860
    5961    struct BasePropertyIndexedKeyframe {
     
    124126
    125127    void getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle);
    126     void apply(RenderStyle& targetStyle, const Style::ResolutionContext&, std::optional<Seconds> = std::nullopt) override;
    127     void invalidate() override;
    128     void animationDidTick() final;
    129     void animationDidPlay() final;
    130     void animationDidChangeTimingProperties() final;
    131     void animationWasCanceled() final;
    132     void animationSuspensionStateDidChange(bool) final;
    133     void animationTimelineDidChange(AnimationTimeline*) final;
     128    void apply(RenderStyle& targetStyle, const Style::ResolutionContext&, std::optional<Seconds> = std::nullopt);
     129    void invalidate();
     130
    134131    void animationTimingDidChange();
    135132    void transformRelatedPropertyDidChange();
     
    137134
    138135    void willChangeRenderer();
    139 
    140     void setAnimation(WebAnimation*) final;
    141136
    142137    RenderElement* renderer() const override;
     
    205200    void computeAcceleratedPropertiesState();
    206201    void setBlendingKeyframes(KeyframeList&);
    207     Seconds timeToNextTick() const final;
    208     std::optional<double> progressUntilNextStep(double) const final;
    209202    bool isTargetingTransformRelatedProperty() const;
    210203    void checkForMatchingTransformFunctionLists();
     
    215208    void checkForMatchingBackdropFilterFunctionLists();
    216209#endif
     210
     211    // AnimationEffect
     212    bool isKeyframeEffect() const final { return true; }
     213    void animationDidTick() final;
     214    void animationDidPlay() final;
     215    void animationDidChangeTimingProperties() final;
     216    void animationWasCanceled() final;
     217    void animationSuspensionStateDidChange(bool) final;
     218    void animationTimelineDidChange(AnimationTimeline*) final;
     219    void setAnimation(WebAnimation*) final;
     220    Seconds timeToNextTick(BasicEffectTiming) const final;
     221    bool ticksContinouslyWhileActive() const final;
     222    std::optional<double> progressUntilNextStep(double) const final;
    217223
    218224    KeyframeList m_blendingKeyframes { emptyString() };
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r285195 r286544  
    798798void WebAnimation::invalidateEffect()
    799799{
    800     if (!isEffectInvalidationSuspended() && m_effect)
    801         m_effect->invalidate();
     800    if (!isEffectInvalidationSuspended() && is<KeyframeEffect>(m_effect))
     801        downcast<KeyframeEffect>(*m_effect).invalidate();
    802802}
    803803
     
    12351235    m_shouldSkipUpdatingFinishedStateWhenResolving = false;
    12361236
    1237     if (m_effect)
    1238         m_effect->apply(targetStyle, resolutionContext, startTime);
     1237    if (is<KeyframeEffect>(m_effect))
     1238        downcast<KeyframeEffect>(*m_effect).apply(targetStyle, resolutionContext, startTime);
    12391239}
    12401240
     
    14541454
    14551455    ASSERT(effect());
    1456     return effect()->timeToNextTick() / playbackRate;
     1456    return effect()->timeToNextTick(effect()->getBasicTiming()) / playbackRate;
    14571457}
    14581458
Note: See TracChangeset for help on using the changeset viewer.