| /* |
| * Copyright (C) 2007-2023 Apple Inc. All rights reserved. |
| * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved. |
| * Copyright (C) 2025 Sam Weinig. 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. |
| * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 |
| |
| #ifndef STYLE_INTERPOLATION_GENERATED_INCLUDE_TRAP |
| #error "Please do not include this file anywhere except from generated code." |
| #endif |
| |
| #include "AnimationMalloc.h" |
| #include "StyleInterpolationFunctions.h" |
| #include "StyleInterpolationWrapperBase.h" |
| #include "StylePrimitiveNumericTypes+Logging.h" |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/text/TextStream.h> |
| |
| namespace WebCore::Style::Interpolation { |
| |
| // MARK: - Base Wrappers |
| |
| template<typename T, typename GetterType = T> |
| class WrapperWithGetter : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(WrapperWithGetter, Animation); |
| public: |
| WrapperWithGetter(CSSPropertyID property, GetterType (RenderStyle::*getter)() const) |
| : WrapperBase(property) |
| , m_getter(getter) |
| { |
| } |
| |
| GetterType value(const RenderStyle& style) const |
| { |
| return (style.*m_getter)(); |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const override |
| { |
| if (&a == &b) |
| return true; |
| return value(a) == value(b); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << value(from) << " to " << value(to) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(destination)); |
| } |
| #endif |
| |
| private: |
| GetterType (RenderStyle::*m_getter)() const; |
| }; |
| |
| template<typename T, typename GetterType = T, typename SetterType = T> |
| class Wrapper : public WrapperWithGetter<T, GetterType> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(Wrapper, Animation); |
| public: |
| Wrapper(CSSPropertyID property, GetterType (RenderStyle::*getter)() const, void (RenderStyle::*setter)(SetterType)) |
| : WrapperWithGetter<T, GetterType>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| (destination.*m_setter)(blendFunc(this->value(from), this->value(to), context)); |
| } |
| |
| protected: |
| void (RenderStyle::*m_setter)(SetterType); |
| }; |
| |
| // Deduction guide for getter/setters that return and take values. |
| template<typename T> |
| Wrapper(CSSPropertyID, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) -> Wrapper<T, T, T>; |
| |
| // Deduction guide for getter/setters that return const references and take r-value references. |
| template<typename T> |
| Wrapper(CSSPropertyID, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) -> Wrapper<T, const T&, T&&>; |
| |
| template<typename T> |
| class RefCountedWrapper : public WrapperWithGetter<T*> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(RefCountedWrapper, Animation); |
| public: |
| RefCountedWrapper(CSSPropertyID property, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<T>&&)) |
| : WrapperWithGetter<T*>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| (destination.*this->m_setter)(blendFunc(this->value(from), this->value(to), context)); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(RefPtr<T>&&); |
| }; |
| |
| // MARK: - Typed Wrappers |
| |
| template<typename T, typename GetterType = T, typename SetterType = T> |
| class StyleTypeWrapper : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(StyleTypeWrapper, Animation); |
| public: |
| StyleTypeWrapper(CSSPropertyID property, GetterType (RenderStyle::*getter)() const, void (RenderStyle::*setter)(SetterType)) |
| : WrapperBase(property) |
| , m_getter(getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool equals(const RenderStyle& from, const RenderStyle& to) const override |
| { |
| if (&from == &to) |
| return true; |
| return Style::equalsForBlending(this->value(from), this->value(to), from, to); |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const override |
| { |
| return Style::canBlend(this->value(from), this->value(to), from, to); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle& from, const RenderStyle& to) const override |
| { |
| return Style::requiresInterpolationForAccumulativeIteration(this->value(from), this->value(to), from, to); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| (destination.*m_setter)(Style::blend(this->value(from), this->value(to), from, to, context)); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const override |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << this->value(from) << " to " << this->value(to) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << this->value(destination)); |
| } |
| #endif |
| |
| private: |
| GetterType value(const RenderStyle& style) const |
| { |
| return (style.*m_getter)(); |
| } |
| |
| GetterType (RenderStyle::*m_getter)() const; |
| void (RenderStyle::*m_setter)(SetterType); |
| }; |
| |
| // Deduction guide for getter/setters that return and take values. |
| template<typename T> |
| StyleTypeWrapper(CSSPropertyID, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) -> StyleTypeWrapper<T, T, T>; |
| |
| // Deduction guide for getter/setters that return const references and take r-value references. |
| template<typename T> |
| StyleTypeWrapper(CSSPropertyID, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) -> StyleTypeWrapper<T, const T&, T&&>; |
| |
| // Deduction guide for getter/setters that return values and take r-value references. |
| template<typename T> |
| StyleTypeWrapper(CSSPropertyID, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) -> StyleTypeWrapper<T, T, T&&>; |
| |
| template<typename T> class VisitedAffectedStyleTypeWrapper : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(VisitedAffectedStyleTypeWrapper, Animation); |
| public: |
| VisitedAffectedStyleTypeWrapper(CSSPropertyID property, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&), const T& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(T&&)) |
| : WrapperBase(property) |
| , m_wrapper(StyleTypeWrapper<T, const T&, T&&>(property, getter, setter)) |
| , m_visitedWrapper(StyleTypeWrapper<T, const T&, T&&>(property, visitedGetter, visitedSetter)) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const override |
| { |
| return m_wrapper.equals(a, b) && m_visitedWrapper.equals(a, b); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle& a, const RenderStyle& b) const override |
| { |
| return m_wrapper.requiresInterpolationForAccumulativeIteration(a, b) && m_visitedWrapper.requiresInterpolationForAccumulativeIteration(a, b); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| m_wrapper.interpolate(destination, from, to, context); |
| m_visitedWrapper.interpolate(destination, from, to, context); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const override |
| { |
| m_wrapper.log(from, to, destination, progress); |
| m_visitedWrapper.log(from, to, destination, progress); |
| } |
| #endif |
| |
| StyleTypeWrapper<T, const T&, T&&> m_wrapper; |
| StyleTypeWrapper<T, const T&, T&&> m_visitedWrapper; |
| }; |
| |
| template<typename T> |
| class AutoWrapper final : public Wrapper<T> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AutoWrapper, Animation); |
| public: |
| AutoWrapper(CSSPropertyID property, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T), bool (RenderStyle::*autoGetter)() const, void (RenderStyle::*autoSetter)(), std::optional<T> minValue = std::nullopt) |
| : Wrapper<T>(property, getter, setter) |
| , m_autoGetter(autoGetter) |
| , m_autoSetter(autoSetter) |
| , m_minValue(minValue) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return !(from.*m_autoGetter)() && !(to.*m_autoGetter)(); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| auto blendedValue = blendFunc(this->value(from), this->value(to), context); |
| if (m_minValue) |
| blendedValue = blendedValue > *m_minValue ? blendedValue : *m_minValue; |
| (destination.*this->m_setter)(blendedValue); |
| |
| if (!context.isDiscrete) |
| return; |
| |
| ASSERT(!context.progress || context.progress == 1.0); |
| if (!context.progress) { |
| if ((from.*m_autoGetter)()) |
| (destination.*m_autoSetter)(); |
| } else { |
| if ((to.*m_autoGetter)()) |
| (destination.*m_autoSetter)(); |
| } |
| } |
| |
| private: |
| bool (RenderStyle::*m_autoGetter)() const; |
| void (RenderStyle::*m_autoSetter)(); |
| std::optional<T> m_minValue; |
| }; |
| |
| class FloatWrapper : public Wrapper<float> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FloatWrapper, Animation); |
| public: |
| enum class ValueRange : uint8_t { |
| All, |
| NonNegative, |
| Positive |
| }; |
| FloatWrapper(CSSPropertyID property, float (RenderStyle::*getter)() const, void (RenderStyle::*setter)(float), ValueRange valueRange = ValueRange::All) |
| : Wrapper(property, getter, setter) |
| , m_valueRange(valueRange) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| auto blendedValue = blendFunc(value(from), value(to), context); |
| if (m_valueRange == ValueRange::NonNegative && blendedValue <= 0) |
| blendedValue = 0; |
| else if (m_valueRange == ValueRange::Positive && blendedValue < 0) |
| blendedValue = std::numeric_limits<float>::epsilon(); |
| (destination.*m_setter)(blendedValue); |
| } |
| |
| private: |
| ValueRange m_valueRange; |
| }; |
| |
| template<typename T> |
| class PositiveWrapper final : public Wrapper<T> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(PositiveWrapper, Animation); |
| public: |
| PositiveWrapper(CSSPropertyID property, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) |
| : Wrapper<T>(property, getter, setter) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| auto blendedValue = blendFunc(this->value(from), this->value(to), context); |
| (destination.*this->m_setter)(blendedValue > 1 ? blendedValue : 1); |
| } |
| }; |
| |
| class LengthWrapper : public WrapperWithGetter<const WebCore::Length&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(LengthWrapper, Animation); |
| public: |
| enum class Flags { |
| IsLengthPercentage = 1 << 0, |
| NegativeLengthsAreInvalid = 1 << 1, |
| }; |
| LengthWrapper(CSSPropertyID property, const WebCore::Length& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(WebCore::Length&&), OptionSet<Flags> flags = { }) |
| : WrapperWithGetter(property, getter) |
| , m_setter(setter) |
| , m_flags(flags) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const override |
| { |
| return canInterpolateLengths(value(from), value(to), m_flags.contains(Flags::IsLengthPercentage)); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle& from, const RenderStyle& to) const final |
| { |
| return lengthsRequireInterpolationForAccumulativeIteration(value(from), value(to)); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| auto valueRange = m_flags.contains(Flags::NegativeLengthsAreInvalid) ? ValueRange::NonNegative : ValueRange::All; |
| (destination.*m_setter)(blendFunc(value(from), value(to), context, valueRange)); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(WebCore::Length&&); |
| OptionSet<Flags> m_flags; |
| }; |
| |
| template<typename T> |
| class LengthVariantWrapper final : public WrapperWithGetter<const T&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(LengthVariantWrapper, Animation); |
| public: |
| LengthVariantWrapper(CSSPropertyID property, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) |
| : WrapperWithGetter<const T&>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return canInterpolateLengthVariants(this->value(from), this->value(to)); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle& from, const RenderStyle& to) const final |
| { |
| return lengthVariantRequiresInterpolationForAccumulativeIteration(this->value(from), this->value(to)); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| (destination.*m_setter)(blendFunc(this->value(from), this->value(to), context)); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(T&&); |
| }; |
| |
| // MARK: - Discrete Wrappers |
| |
| template<typename T, typename GetterType = T, typename SetterType = T> class DiscreteWrapper : public WrapperWithGetter<T, GetterType> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DiscreteWrapper, Animation); |
| public: |
| DiscreteWrapper(CSSPropertyID property, GetterType (RenderStyle::*getter)() const, void (RenderStyle::*setter)(SetterType)) |
| : WrapperWithGetter<T, GetterType>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const final |
| { |
| return false; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| ASSERT(!context.progress || context.progress == 1.0); |
| (destination.*this->m_setter)(T { this->value(context.progress ? to : from) }); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(SetterType); |
| }; |
| |
| // Deduction guide for getter/setters that return and take values. |
| template<typename T> |
| DiscreteWrapper(CSSPropertyID, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) -> DiscreteWrapper<T, T, T>; |
| |
| // Deduction guide for getter/setters that return const references and take r-value references. |
| template<typename T> |
| DiscreteWrapper(CSSPropertyID, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) -> DiscreteWrapper<T, const T&, T&&>; |
| |
| // Deduction guide for getter/setters that return values and take r-value references. |
| template<typename T> |
| DiscreteWrapper(CSSPropertyID, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) -> DiscreteWrapper<T, T, T&&>; |
| |
| class FontSizeWrapper final : public Wrapper<float> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontSizeWrapper, Animation); |
| public: |
| FontSizeWrapper() |
| : Wrapper<float>(CSSPropertyID::CSSPropertyFontSize, &RenderStyle::computedFontSize, &RenderStyle::setFontSize) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return a.specifiedFontSize() == b.specifiedFontSize(); |
| } |
| }; |
| |
| class DiscreteFontDescriptionWrapper : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DiscreteFontDescriptionWrapper, Animation); |
| public: |
| DiscreteFontDescriptionWrapper(CSSPropertyID property) |
| : WrapperBase(property) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const override |
| { |
| return propertiesInFontDescriptionAreEqual(a.fontDescription(), b.fontDescription()); |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const override |
| { |
| return false; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| ASSERT(!context.progress || context.progress == 1.0); |
| auto destinationDescription = destination.fontDescription(); |
| auto& sourceDescription = (context.progress ? to : from).fontDescription(); |
| setPropertiesInFontDescription(sourceDescription, destinationDescription); |
| destination.setFontDescription(WTFMove(destinationDescription)); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle&, const RenderStyle&, const RenderStyle&, double) const override |
| { |
| } |
| #endif |
| |
| protected: |
| virtual bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription&, const FontCascadeDescription&) const { return false; } |
| virtual void setPropertiesInFontDescription(const FontCascadeDescription&, FontCascadeDescription&) const { } |
| }; |
| |
| template<typename T> |
| class DiscreteFontDescriptionTypedWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DiscreteFontDescriptionTypedWrapper, Animation); |
| public: |
| DiscreteFontDescriptionTypedWrapper(CSSPropertyID property, T (FontCascadeDescription::*getter)() const, void (FontCascadeDescription::*setter)(T)) |
| : DiscreteFontDescriptionWrapper(property) |
| , m_getter(getter) |
| , m_setter(setter) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return this->value(a) == this->value(b); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| (destination.*this->m_setter)(this->value(source)); |
| } |
| |
| T value(const FontCascadeDescription& description) const |
| { |
| return (description.*this->m_getter)(); |
| } |
| |
| T (FontCascadeDescription::*m_getter)() const; |
| void (FontCascadeDescription::*m_setter)(T); |
| }; |
| |
| template<typename T> |
| class NonNormalizedDiscreteWrapper final : public Wrapper<T> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(NonNormalizedDiscreteWrapper, Animation); |
| public: |
| NonNormalizedDiscreteWrapper(CSSPropertyID property, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) |
| : Wrapper<T>(property, getter, setter) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const final |
| { |
| return false; |
| } |
| }; |
| |
| class FontFeatureSettingsWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontFeatureSettingsWrapper, Animation); |
| public: |
| FontFeatureSettingsWrapper() |
| : DiscreteFontDescriptionWrapper(CSSPropertyFontFeatureSettings) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return a.featureSettings() == b.featureSettings(); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| destination.setFeatureSettings(FontFeatureSettings(source.featureSettings())); |
| } |
| }; |
| |
| class FontVariantEastAsianWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontVariantEastAsianWrapper, Animation); |
| public: |
| FontVariantEastAsianWrapper() |
| : DiscreteFontDescriptionWrapper(CSSPropertyFontVariantEastAsian) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return a.variantEastAsianVariant() == b.variantEastAsianVariant() |
| && a.variantEastAsianWidth() == b.variantEastAsianWidth() |
| && a.variantEastAsianRuby() == b.variantEastAsianRuby(); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| destination.setVariantEastAsianVariant(source.variantEastAsianVariant()); |
| destination.setVariantEastAsianWidth(source.variantEastAsianWidth()); |
| destination.setVariantEastAsianRuby(source.variantEastAsianRuby()); |
| } |
| }; |
| |
| class FontVariantLigaturesWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontVariantLigaturesWrapper, Animation); |
| public: |
| FontVariantLigaturesWrapper() |
| : DiscreteFontDescriptionWrapper(CSSPropertyFontVariantLigatures) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return a.variantCommonLigatures() == b.variantCommonLigatures() |
| && a.variantDiscretionaryLigatures() == b.variantDiscretionaryLigatures() |
| && a.variantHistoricalLigatures() == b.variantHistoricalLigatures() |
| && a.variantContextualAlternates() == b.variantContextualAlternates(); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| destination.setVariantCommonLigatures(source.variantCommonLigatures()); |
| destination.setVariantDiscretionaryLigatures(source.variantDiscretionaryLigatures()); |
| destination.setVariantHistoricalLigatures(source.variantHistoricalLigatures()); |
| destination.setVariantContextualAlternates(source.variantContextualAlternates()); |
| } |
| }; |
| |
| class FontFamilyWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontFamilyWrapper, Animation); |
| public: |
| FontFamilyWrapper() |
| : DiscreteFontDescriptionWrapper(CSSPropertyFontFamily) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return a.families() == b.families(); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| destination.setFamilies(source.families()); |
| } |
| }; |
| |
| class FontVariantNumericWrapper final : public DiscreteFontDescriptionWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontVariantNumericWrapper, Animation); |
| public: |
| FontVariantNumericWrapper() |
| : DiscreteFontDescriptionWrapper(CSSPropertyFontVariantNumeric) |
| { |
| } |
| |
| private: |
| bool propertiesInFontDescriptionAreEqual(const FontCascadeDescription& a, const FontCascadeDescription& b) const override |
| { |
| return a.variantNumericFigure() == b.variantNumericFigure() |
| && a.variantNumericSpacing() == b.variantNumericSpacing() |
| && a.variantNumericFraction() == b.variantNumericFraction() |
| && a.variantNumericOrdinal() == b.variantNumericOrdinal() |
| && a.variantNumericSlashedZero() == b.variantNumericSlashedZero(); |
| } |
| |
| void setPropertiesInFontDescription(const FontCascadeDescription& source, FontCascadeDescription& destination) const override |
| { |
| destination.setVariantNumericFigure(source.variantNumericFigure()); |
| destination.setVariantNumericSpacing(source.variantNumericSpacing()); |
| destination.setVariantNumericFraction(source.variantNumericFraction()); |
| destination.setVariantNumericOrdinal(source.variantNumericOrdinal()); |
| destination.setVariantNumericSlashedZero(source.variantNumericSlashedZero()); |
| } |
| }; |
| |
| // MARK: - Customized Wrappers |
| |
| class NinePieceImageRepeatWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(NinePieceImageRepeatWrapper, Animation); |
| public: |
| NinePieceImageRepeatWrapper(CSSPropertyID property, NinePieceImageRule (RenderStyle::*horizontalGetter)() const, void (RenderStyle::*horizontalSetter)(NinePieceImageRule), NinePieceImageRule (RenderStyle::*verticalGetter)() const, void (RenderStyle::*verticalSetter)(NinePieceImageRule)) |
| : WrapperBase(property) |
| , m_horizontalWrapper(DiscreteWrapper<NinePieceImageRule>(property, horizontalGetter, horizontalSetter)) |
| , m_verticalWrapper(DiscreteWrapper<NinePieceImageRule>(property, verticalGetter, verticalSetter)) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return m_horizontalWrapper.equals(a, b) && m_verticalWrapper.equals(a, b); |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const final |
| { |
| return false; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| m_horizontalWrapper.interpolate(destination, from, to, context); |
| m_verticalWrapper.interpolate(destination, from, to, context); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const final |
| { |
| m_horizontalWrapper.log(from, to, destination, progress); |
| m_verticalWrapper.log(from, to, destination, progress); |
| } |
| #endif |
| |
| DiscreteWrapper<NinePieceImageRule> m_horizontalWrapper; |
| DiscreteWrapper<NinePieceImageRule> m_verticalWrapper; |
| }; |
| |
| class LengthBoxWrapper : public WrapperWithGetter<const LengthBox&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(LengthBoxWrapper, Animation); |
| public: |
| enum class Flags : uint8_t { |
| IsLengthPercentage = 1 << 0, |
| UsesFillKeyword = 1 << 1, |
| AllowsNegativeValues = 1 << 2, |
| MayOverrideBorderWidths = 1 << 3, |
| }; |
| LengthBoxWrapper(CSSPropertyID property, const LengthBox& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(LengthBox&&), OptionSet<Flags> flags = { }) |
| : WrapperWithGetter(property, getter) |
| , m_setter(setter) |
| , m_flags(flags) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const override |
| { |
| if (m_flags.contains(Flags::UsesFillKeyword)) { |
| if (property() == CSSPropertyBorderImageSlice && from.borderImage().fill() != to.borderImage().fill()) |
| return false; |
| if (property() == CSSPropertyMaskBorderSlice && from.maskBorder().fill() != to.maskBorder().fill()) |
| return false; |
| } |
| |
| bool isLengthPercentage = m_flags.contains(Flags::IsLengthPercentage); |
| |
| if (m_flags.contains(Flags::MayOverrideBorderWidths)) { |
| bool overridesBorderWidths = from.borderImage().overridesBorderWidths(); |
| if (overridesBorderWidths != to.borderImage().overridesBorderWidths()) |
| return false; |
| // Even if this property accepts <length-percentage>, border widths can only be a <length>. |
| if (overridesBorderWidths) |
| isLengthPercentage = false; |
| } |
| |
| auto& fromLengthBox = value(from); |
| auto& toLengthBox = value(to); |
| return canInterpolateLengths(fromLengthBox.top(), toLengthBox.top(), isLengthPercentage) |
| && canInterpolateLengths(fromLengthBox.right(), toLengthBox.right(), isLengthPercentage) |
| && canInterpolateLengths(fromLengthBox.bottom(), toLengthBox.bottom(), isLengthPercentage) |
| && canInterpolateLengths(fromLengthBox.left(), toLengthBox.left(), isLengthPercentage); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle& from, const RenderStyle& to) const final |
| { |
| auto& fromLengthBox = value(from); |
| auto& toLengthBox = value(to); |
| return lengthsRequireInterpolationForAccumulativeIteration(fromLengthBox.top(), toLengthBox.top()) |
| && lengthsRequireInterpolationForAccumulativeIteration(fromLengthBox.right(), toLengthBox.right()) |
| && lengthsRequireInterpolationForAccumulativeIteration(fromLengthBox.bottom(), toLengthBox.bottom()) |
| && lengthsRequireInterpolationForAccumulativeIteration(fromLengthBox.left(), toLengthBox.left()); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| if (m_flags.contains(Flags::UsesFillKeyword)) { |
| if (property() == CSSPropertyBorderImageSlice) |
| destination.setBorderImageSliceFill((!context.progress || !context.isDiscrete ? from : to).borderImage().fill()); |
| else if (property() == CSSPropertyMaskBorderSlice) |
| destination.setMaskBorderSliceFill((!context.progress || !context.isDiscrete ? from : to).maskBorder().fill()); |
| } |
| if (m_flags.contains(Flags::MayOverrideBorderWidths)) |
| destination.setBorderImageWidthOverridesBorderWidths((!context.progress || !context.isDiscrete ? from : to).borderImage().overridesBorderWidths()); |
| if (context.isDiscrete) { |
| // It is important we have this non-interpolated shortcut because certain CSS properties |
| // represented as a LengthBox, such as border-image-slice, don't know how to deal with |
| // calculated Length values, see for instance valueForImageSliceSide(const Length&). |
| (destination.*m_setter)(context.progress ? LengthBox(value(to)) : LengthBox(value(from))); |
| return; |
| } |
| auto valueRange = m_flags.contains(Flags::AllowsNegativeValues) ? ValueRange::All : ValueRange::NonNegative; |
| (destination.*m_setter)(blendFunc(value(from), value(to), context, valueRange)); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(LengthBox&&); |
| OptionSet<Flags> m_flags; |
| }; |
| |
| #if ENABLE(VARIATION_FONTS) |
| |
| class FontVariationSettingsWrapper final : public Wrapper<FontVariationSettings> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontVariationSettingsWrapper, Animation); |
| public: |
| FontVariationSettingsWrapper() |
| : Wrapper(CSSPropertyFontVariationSettings, &RenderStyle::fontVariationSettings, &RenderStyle::setFontVariationSettings) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| // If the style pointers are the same, don't bother doing the test. |
| if (&a == &b) |
| return true; |
| return value(a) == value(b); |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| auto fromVariationSettings = value(from); |
| auto toVariationSettings = value(to); |
| |
| if (fromVariationSettings.size() != toVariationSettings.size()) |
| return false; |
| |
| auto size = fromVariationSettings.size(); |
| for (unsigned i = 0; i < size; ++i) { |
| if (fromVariationSettings.at(i).tag() != toVariationSettings.at(i).tag()) |
| return false; |
| } |
| |
| return true; |
| } |
| }; |
| |
| #endif |
| |
| class StyleImageWrapper final : public RefCountedWrapper<StyleImage> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(StyleImageWrapper, Animation); |
| public: |
| StyleImageWrapper(CSSPropertyID property, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<StyleImage>&&)) |
| : RefCountedWrapper(property, getter, setter) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| if (&a == &b) |
| return true; |
| |
| auto* imageA = value(a); |
| auto* imageB = value(b); |
| return arePointingToEqualData(imageA, imageB); |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return value(from) && value(to); |
| } |
| }; |
| |
| class TransformOperationsWrapper final : public WrapperWithGetter<const TransformOperations&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(TransformOperationsWrapper, Animation); |
| public: |
| TransformOperationsWrapper() |
| : WrapperWithGetter<const TransformOperations&>(CSSPropertyTransform, &RenderStyle::transform) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation compositeOperation) const override |
| { |
| if (compositeOperation == CompositeOperation::Replace) |
| return !this->value(to).shouldFallBackToDiscreteAnimation(this->value(from), { }); |
| return true; |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle&, const RenderStyle&) const final |
| { |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| destination.setTransform(blendFunc(this->value(from), this->value(to), context)); |
| } |
| }; |
| |
| class FilterWrapper final : public WrapperWithGetter<const FilterOperations&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FilterWrapper, Animation); |
| public: |
| FilterWrapper(CSSPropertyID property, const FilterOperations& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(FilterOperations&&)) |
| : WrapperWithGetter<const FilterOperations&>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation compositeOperation) const final |
| { |
| return value(from).canInterpolate(value(to), compositeOperation); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle&, const RenderStyle&) const final |
| { |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| (destination.*m_setter)(blendFunc(value(from), value(to), context)); |
| } |
| |
| void (RenderStyle::*m_setter)(FilterOperations&&); |
| }; |
| |
| inline const BoxShadow& shadowForInterpolation(const BoxShadow& shadowToMatch) |
| { |
| static NeverDestroyed<BoxShadow> defaultShadowData { |
| BoxShadow { |
| .color = { WebCore::Color::transparentBlack }, |
| .location = { { 0 }, { 0 } }, |
| .blur = { 0 }, |
| .spread = { 0 }, |
| .inset = std::nullopt, |
| .isWebkitBoxShadow = false |
| } |
| }; |
| static NeverDestroyed<BoxShadow> defaultInsetShadowData { |
| BoxShadow { |
| .color = { WebCore::Color::transparentBlack }, |
| .location = { { 0 }, { 0 } }, |
| .blur = { 0 }, |
| .spread = { 0 }, |
| .inset = CSS::Keyword::Inset { }, |
| .isWebkitBoxShadow = false |
| } |
| }; |
| static NeverDestroyed<BoxShadow> defaultWebKitBoxShadowData { |
| BoxShadow { |
| .color = { WebCore::Color::transparentBlack }, |
| .location = { { 0 }, { 0 } }, |
| .blur = { 0 }, |
| .spread = { 0 }, |
| .inset = std::nullopt, |
| .isWebkitBoxShadow = true |
| } |
| }; |
| static NeverDestroyed<BoxShadow> defaultInsetWebKitBoxShadowData { |
| BoxShadow { |
| .color = { WebCore::Color::transparentBlack }, |
| .location = { { 0 }, { 0 } }, |
| .blur = { 0 }, |
| .spread = { 0 }, |
| .inset = CSS::Keyword::Inset { }, |
| .isWebkitBoxShadow = true |
| } |
| }; |
| |
| if (isInset(shadowToMatch)) |
| return shadowToMatch.isWebkitBoxShadow ? defaultInsetWebKitBoxShadowData.get() : defaultInsetShadowData.get(); |
| else |
| return shadowToMatch.isWebkitBoxShadow ? defaultWebKitBoxShadowData.get() : defaultShadowData.get(); |
| } |
| |
| inline const TextShadow& shadowForInterpolation(const TextShadow&) |
| { |
| static NeverDestroyed<TextShadow> defaultShadowData { |
| TextShadow { |
| .color = { WebCore::Color::transparentBlack }, |
| .location = { { 0 }, { 0 } }, |
| .blur = { 0 }, |
| } |
| }; |
| return defaultShadowData.get(); |
| } |
| |
| template<typename ShadowListType> class ShadowWrapper final : public WrapperWithGetter<const ShadowListType&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(ShadowWrapper, Animation); |
| public: |
| using ShadowType = typename ShadowListType::value_type; |
| |
| ShadowWrapper(CSSPropertyID property, const ShadowListType& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowListType&&)) |
| : WrapperWithGetter<const ShadowListType&>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& fromStyle, const RenderStyle& toStyle, CompositeOperation compositeOperation) const final |
| { |
| if (compositeOperation != CompositeOperation::Replace) |
| return true; |
| |
| // The only scenario where we can't interpolate is if specified items don't have the same shadow style. |
| |
| auto& fromShadowList = this->value(fromStyle); |
| auto& toShadowList = this->value(toStyle); |
| |
| auto fromLength = fromShadowList.size(); |
| auto toLength = toShadowList.size(); |
| |
| // FIXME: Something like LLVM ADT's zip_shortest (https://llvm.org/doxygen/structllvm_1_1detail_1_1zip__shortest.html) would allow this to be done without indexing: |
| // |
| // return std::ranges::all_of( |
| // zip_shortest(makeReversedRange(fromShadowList), makeReversedRange(toShadowList)), |
| // [](const auto& pair) { |
| // return shadowStyle(std::get<0>(pair)) == shadowStyle(std::get<1>(pair)); |
| // } |
| // ); |
| |
| size_t minLength = std::min(fromLength, toLength); |
| for (size_t i = 0; i < minLength; ++i) { |
| auto fromIndex = fromLength - i - 1; |
| auto toIndex = toLength - i - 1; |
| if (shadowStyle(fromShadowList[fromIndex]) != shadowStyle(toShadowList[toIndex])) |
| return false; |
| } |
| return true; |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle&, const RenderStyle&) const final |
| { |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& fromStyle, const RenderStyle& toStyle, const Context& context) const final |
| { |
| auto& fromShadowList = this->value(fromStyle); |
| auto& toShadowList = this->value(toStyle); |
| |
| if (context.isDiscrete) { |
| ASSERT(!context.progress || context.progress == 1.0); |
| (destination.*m_setter)(ShadowListType { context.progress ? toShadowList : fromShadowList }); |
| return; |
| } |
| |
| auto fromLength = fromShadowList.size(); |
| auto toLength = toShadowList.size(); |
| |
| if (!fromLength && !toLength) |
| (destination.*m_setter)(CSS::Keyword::None { }); |
| else if (fromLength == toLength) |
| (destination.*m_setter)(blendMatchedShadowLists(fromShadowList, toShadowList, fromLength, fromStyle, toStyle, context)); |
| else |
| (destination.*m_setter)(blendMismatchedShadowLists(fromShadowList, toShadowList, fromLength, toLength, fromStyle, toStyle, context)); |
| } |
| |
| ShadowListType addShadowLists(const ShadowListType& fromShadowList, const ShadowListType& toShadowList, size_t fromLength, size_t toLength) const |
| { |
| auto combinedSize = fromLength + toLength; |
| return { FixedVector<ShadowType>::createWithSizeFromGenerator(combinedSize, [&](auto index) -> ShadowType { |
| if (index < toLength) |
| return toShadowList[index]; |
| return fromShadowList[index - toLength]; |
| }) }; |
| } |
| |
| ShadowListType blendMatchedShadowLists(const ShadowListType& fromShadowList, const ShadowListType& toShadowList, size_t length, const RenderStyle& fromStyle, const RenderStyle& toStyle, const Context& context) const |
| { |
| // from or to might be empty in which case we don't want to do additivity, but do replace instead. |
| if (!fromShadowList.isNone() && !toShadowList.isNone() && context.compositeOperation == CompositeOperation::Add) |
| return addShadowLists(fromShadowList, toShadowList, length, length); |
| |
| return { FixedVector<ShadowType>::createWithSizeFromGenerator(length, [&](auto index) -> ShadowType { |
| return Style::blend(fromShadowList[index], toShadowList[index], fromStyle, toStyle, context); |
| }) }; |
| } |
| |
| ShadowListType blendMismatchedShadowLists(const ShadowListType& fromShadowList, const ShadowListType& toShadowList, size_t fromLength, size_t toLength, const RenderStyle& fromStyle, const RenderStyle& toStyle, const Context& context) const |
| { |
| if (!fromShadowList.isNone() && !toShadowList.isNone() && context.compositeOperation != CompositeOperation::Replace) |
| return addShadowLists(fromShadowList, toShadowList, fromLength, toLength); |
| |
| auto maxLength = std::max(fromLength, toLength); |
| return { FixedVector<ShadowType>::createWithSizeFromGenerator(maxLength, [&](auto index) -> ShadowType { |
| auto indexFromEnd = maxLength - index - 1; |
| bool hasFrom = indexFromEnd < fromLength; |
| bool hasTo = indexFromEnd < toLength; |
| |
| if (hasFrom && hasTo) { |
| const auto& fromShadow = fromShadowList[index - (maxLength - fromLength)]; |
| const auto& toShadow = toShadowList[index - (maxLength - toLength)]; |
| return Style::blend(fromShadow, toShadow, fromStyle, toStyle, context); |
| } else if (hasFrom) { |
| const auto& fromShadow = fromShadowList[index - (maxLength - fromLength)]; |
| const auto& toShadow = shadowForInterpolation(fromShadow); |
| return Style::blend(fromShadow, toShadow, fromStyle, toStyle, context); |
| } else if (hasTo) { |
| const auto& toShadow = toShadowList[index - (maxLength - toLength)]; |
| const auto& fromShadow = shadowForInterpolation(toShadow); |
| return Style::blend(fromShadow, toShadow, fromStyle, toStyle, context); |
| } |
| |
| RELEASE_ASSERT_NOT_REACHED(); |
| }) }; |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(ShadowListType&&); |
| }; |
| |
| class ColorWrapper final : public WrapperWithGetter<const WebCore::Color&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(ColorWrapper, Animation); |
| public: |
| ColorWrapper(CSSPropertyID property, const WebCore::Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(WebCore::Color&&)) |
| : WrapperWithGetter<const WebCore::Color&>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const override |
| { |
| (destination.*m_setter)(blendFunc(value(from), value(to), context)); |
| } |
| |
| private: |
| void (RenderStyle::*m_setter)(WebCore::Color&&); |
| }; |
| |
| class VisitedAffectedColorWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(VisitedAffectedColorWrapper, Animation); |
| public: |
| VisitedAffectedColorWrapper(CSSPropertyID property, const WebCore::Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(WebCore::Color&&), const WebCore::Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(WebCore::Color&&)) |
| : WrapperBase(property) |
| , m_wrapper(ColorWrapper(property, getter, setter)) |
| , m_visitedWrapper(ColorWrapper(property, visitedGetter, visitedSetter)) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return m_wrapper.equals(a, b) && m_visitedWrapper.equals(a, b); |
| } |
| |
| bool requiresInterpolationForAccumulativeIteration(const RenderStyle&, const RenderStyle&) const final |
| { |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| m_wrapper.interpolate(destination, from, to, context); |
| m_visitedWrapper.interpolate(destination, from, to, context); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const final |
| { |
| m_wrapper.log(from, to, destination, progress); |
| m_visitedWrapper.log(from, to, destination, progress); |
| } |
| #endif |
| |
| ColorWrapper m_wrapper; |
| ColorWrapper m_visitedWrapper; |
| }; |
| |
| class AccentColorWrapper final : public StyleTypeWrapper<Color, const Color&, Color&&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AccentColorWrapper, Animation); |
| public: |
| AccentColorWrapper() |
| : StyleTypeWrapper(CSSPropertyAccentColor, &RenderStyle::accentColor, &RenderStyle::setAccentColor) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return a.hasAutoAccentColor() == b.hasAutoAccentColor() |
| && StyleTypeWrapper::equals(a, b); |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return !from.hasAutoAccentColor() && !to.hasAutoAccentColor(); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| if (canInterpolate(from, to, context.compositeOperation)) { |
| StyleTypeWrapper::interpolate(destination, from, to, context); |
| return; |
| } |
| |
| ASSERT(!context.progress || context.progress == 1.0); |
| auto& blendingRenderStyle = context.progress ? to : from; |
| if (blendingRenderStyle.hasAutoAccentColor()) |
| destination.setHasAutoAccentColor(); |
| else |
| destination.setAccentColor(Color { blendingRenderStyle.accentColor() }); |
| } |
| }; |
| |
| class CaretColorWrapper final : public VisitedAffectedStyleTypeWrapper<Color> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(CaretColorWrapper, Animation); |
| public: |
| CaretColorWrapper() |
| : VisitedAffectedStyleTypeWrapper<Color>(CSSPropertyCaretColor, &RenderStyle::caretColor, &RenderStyle::setCaretColor, &RenderStyle::visitedLinkCaretColor, &RenderStyle::setVisitedLinkCaretColor) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return a.hasAutoCaretColor() == b.hasAutoCaretColor() |
| && a.hasVisitedLinkAutoCaretColor() == b.hasVisitedLinkAutoCaretColor() |
| && VisitedAffectedStyleTypeWrapper<Color>::equals(a, b); |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return canInterpolateCaretColor(from, to, false) || canInterpolateCaretColor(from, to, true); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| if (canInterpolateCaretColor(from, to, false)) |
| m_wrapper.interpolate(destination, from, to, context); |
| else { |
| auto& blendingRenderStyle = context.progress < 0.5 ? from : to; |
| if (blendingRenderStyle.hasAutoCaretColor()) |
| destination.setHasAutoCaretColor(); |
| else |
| destination.setCaretColor(Color { blendingRenderStyle.caretColor() }); |
| } |
| |
| if (canInterpolateCaretColor(from, to, true)) |
| m_visitedWrapper.interpolate(destination, from, to, context); |
| else { |
| auto& blendingRenderStyle = context.progress < 0.5 ? from : to; |
| if (blendingRenderStyle.hasVisitedLinkAutoCaretColor()) |
| destination.setHasVisitedLinkAutoCaretColor(); |
| else |
| destination.setVisitedLinkCaretColor(Color { blendingRenderStyle.visitedLinkCaretColor() }); |
| } |
| } |
| |
| private: |
| static bool canInterpolateCaretColor(const RenderStyle& from, const RenderStyle& to, bool visited) |
| { |
| if (visited) |
| return !from.hasVisitedLinkAutoCaretColor() && !to.hasVisitedLinkAutoCaretColor(); |
| return !from.hasAutoCaretColor() && !to.hasAutoCaretColor(); |
| } |
| }; |
| |
| class FontWeightWrapper final : public Wrapper<FontSelectionValue> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontWeightWrapper, Animation); |
| public: |
| FontWeightWrapper() |
| : Wrapper(CSSPropertyFontWeight, &RenderStyle::fontWeight, &RenderStyle::setFontWeight) |
| { |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| (destination.*m_setter)(FontSelectionValue(std::clamp(blendFunc(static_cast<float>(this->value(from)), static_cast<float>(this->value(to)), context), 1.0f, 1000.0f))); |
| } |
| }; |
| |
| class FontStyleWrapper final : public Wrapper<std::optional<FontSelectionValue>> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontStyleWrapper, Animation); |
| public: |
| FontStyleWrapper() |
| : Wrapper(CSSPropertyFontStyle, &RenderStyle::fontItalic, &RenderStyle::setFontItalic) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return from.fontDescription().fontStyleAxis() == FontStyleAxis::slnt && to.fontDescription().fontStyleAxis() == FontStyleAxis::slnt; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| auto blendedStyleAxis = FontStyleAxis::slnt; |
| if (context.isDiscrete) |
| blendedStyleAxis = (context.progress < 0.5 ? from : to).fontDescription().fontStyleAxis(); |
| |
| auto fromFontItalic = from.fontItalic(); |
| auto toFontItalic = to.fontItalic(); |
| auto blendedFontItalic = context.progress < 0.5 ? fromFontItalic : toFontItalic; |
| if (!context.isDiscrete) |
| blendedFontItalic = blendFunc(fromFontItalic, toFontItalic, context); |
| |
| auto description = destination.fontDescription(); |
| description.setItalic(blendedFontItalic); |
| description.setFontStyleAxis(blendedStyleAxis); |
| destination.setFontDescription(WTFMove(description)); |
| } |
| }; |
| |
| class FontSizeAdjustWrapper final : public WrapperWithGetter<FontSizeAdjust> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FontSizeAdjustWrapper, Animation); |
| public: |
| FontSizeAdjustWrapper() |
| : WrapperWithGetter(CSSPropertyFontSizeAdjust, &RenderStyle::fontSizeAdjust) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| auto fromFontSizeAdjust = from.fontSizeAdjust(); |
| auto toFontSizeAdjust = to.fontSizeAdjust(); |
| return fromFontSizeAdjust.metric == toFontSizeAdjust.metric |
| && fromFontSizeAdjust.value && toFontSizeAdjust.value; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| auto blendedFontSizeAdjust = [&]() -> FontSizeAdjust { |
| if (context.isDiscrete) |
| return (!context.progress ? from : to).fontSizeAdjust(); |
| |
| ASSERT(from.fontSizeAdjust().value && to.fontSizeAdjust().value); |
| auto blendedAdjust = blendFunc(*from.fontSizeAdjust().value, *to.fontSizeAdjust().value, context); |
| |
| ASSERT(from.fontSizeAdjust().metric == to.fontSizeAdjust().metric); |
| return { to.fontSizeAdjust().metric, FontSizeAdjust::ValueType::Number, std::max(blendedAdjust, 0.0f) }; |
| }; |
| |
| destination.setFontSizeAdjust(blendedFontSizeAdjust()); |
| } |
| }; |
| |
| class LineHeightWrapper final : public LengthWrapper { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(LineHeightWrapper, Animation); |
| public: |
| LineHeightWrapper() |
| : LengthWrapper(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation compositeOperation) const final |
| { |
| // We must account for how BuilderConverter::convertLineHeight() deals with line-height values: |
| // - "normal" is converted to LengthType::Percent with a -100 value |
| // - <number> values are converted to LengthType::Percent |
| // - <length-percentage> values are converted to LengthType::Fixed |
| // This means that animating between "normal" and a "<number>" would work with LengthWrapper::canInterpolate() |
| // since it would see two LengthType::Percent values. So if either value is "normal" we cannot interpolate since those |
| // values are either equal or of incompatible types. |
| auto normalLineHeight = RenderStyle::initialLineHeight(); |
| if (value(from) == normalLineHeight || value(to) == normalLineHeight) |
| return false; |
| |
| // The default logic will now apply since <number> and <length-percentage> values |
| // are converted to different LengthType values. |
| return LengthWrapper::canInterpolate(from, to, compositeOperation); |
| } |
| }; |
| |
| class TabSizeWrapper final : public Wrapper<const TabSize&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(TabSizeWrapper, Animation); |
| public: |
| TabSizeWrapper() |
| : Wrapper(CSSPropertyTabSize, &RenderStyle::tabSize, &RenderStyle::setTabSize) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| return value(from).isSpaces() == value(to).isSpaces(); |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| if (context.isDiscrete) |
| (destination.*m_setter)(context.progress ? value(to) : value(from)); |
| else |
| Wrapper::interpolate(destination, from, to, context); |
| } |
| }; |
| |
| class CounterWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(CounterWrapper, Animation); |
| public: |
| CounterWrapper(CSSPropertyID property) |
| : WrapperBase(property) |
| { |
| ASSERT(property == CSSPropertyCounterIncrement || property == CSSPropertyCounterReset || property == CSSPropertyCounterSet); |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const final |
| { |
| return false; |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| auto& mapA = a.counterDirectives().map; |
| auto& mapB = b.counterDirectives().map; |
| if (mapA.size() != mapB.size()) |
| return false; |
| for (auto& [key, aDirective] : mapA) { |
| auto it = mapB.find(key); |
| if (it == mapB.end()) |
| return false; |
| auto& bDirective = it->value; |
| if ((property() == CSSPropertyCounterIncrement && aDirective.incrementValue != bDirective.incrementValue) |
| || (property() == CSSPropertyCounterReset && aDirective.resetValue != bDirective.resetValue) |
| || (property() == CSSPropertyCounterSet && aDirective.setValue != bDirective.setValue)) |
| return false; |
| } |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| ASSERT(context.isDiscrete); |
| ASSERT(!context.progress || context.progress == 1); |
| |
| // Clear all existing values in the existing set of directives. |
| for (auto& [key, directive] : destination.accessCounterDirectives().map) { |
| if (property() == CSSPropertyCounterIncrement) |
| directive.incrementValue = std::nullopt; |
| else if (property() == CSSPropertyCounterReset) |
| directive.resetValue = std::nullopt; |
| else |
| directive.setValue = std::nullopt; |
| } |
| |
| auto& style = context.progress ? to : from; |
| auto& targetDirectives = destination.accessCounterDirectives().map; |
| for (auto& [key, directive] : style.counterDirectives().map) { |
| auto updateDirective = [&](CounterDirectives& target, const CounterDirectives& source) { |
| if (property() == CSSPropertyCounterIncrement) |
| target.incrementValue = source.incrementValue; |
| else if (property() == CSSPropertyCounterReset) |
| target.resetValue = source.resetValue; |
| else |
| target.setValue = source.setValue; |
| }; |
| auto it = targetDirectives.find(key); |
| if (it == targetDirectives.end()) |
| updateDirective(targetDirectives.add(key, CounterDirectives { }).iterator->value, directive); |
| else |
| updateDirective(it->value, directive); |
| } |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle&, const RenderStyle&, const RenderStyle&, double progress) const final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " at " << TextStream::FormatNumberRespectingIntegers(progress) << "."); |
| } |
| #endif |
| }; |
| |
| class VisibilityWrapper final : public Wrapper<Visibility> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(VisibilityWrapper, Animation); |
| public: |
| VisibilityWrapper() |
| : Wrapper(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| // https://drafts.csswg.org/web-animations-1/#animating-visibility |
| // If neither value is visible, then discrete animation is used. |
| return value(from) == Visibility::Visible || value(to) == Visibility::Visible; |
| } |
| }; |
| |
| template<typename T, typename GetterType = T, typename SetterType = T> |
| class DiscreteSVGWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DiscreteSVGWrapper, Animation); |
| public: |
| DiscreteSVGWrapper(CSSPropertyID property, GetterType (SVGRenderStyle::*getter)() const, void (SVGRenderStyle::*setter)(SetterType)) |
| : WrapperBase(property) |
| , m_getter(getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| return this->value(a) == this->value(b); |
| } |
| |
| bool canInterpolate(const RenderStyle&, const RenderStyle&, CompositeOperation) const final |
| { |
| return false; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| ASSERT(!context.progress || context.progress == 1.0); |
| (destination.accessSVGStyle().*this->m_setter)(T { this->value(context.progress ? to : from) }); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle&, const RenderStyle&, const RenderStyle&, double) const final |
| { |
| } |
| #endif |
| |
| private: |
| T value(const RenderStyle& style) const |
| { |
| return (style.svgStyle().*this->m_getter)(); |
| } |
| |
| GetterType (SVGRenderStyle::*m_getter)() const; |
| void (SVGRenderStyle::*m_setter)(SetterType); |
| }; |
| |
| // Deduction guide for getter/setters that return and take values. |
| template<typename T> |
| DiscreteSVGWrapper(CSSPropertyID, T (SVGRenderStyle::*getter)() const, void (SVGRenderStyle::*setter)(T)) -> DiscreteSVGWrapper<T, T, T>; |
| |
| // Deduction guide for getter/setters that return const references and take r-value references. |
| template<typename T> |
| DiscreteSVGWrapper(CSSPropertyID, const T& (SVGRenderStyle::*getter)() const, void (SVGRenderStyle::*setter)(T&&)) -> DiscreteSVGWrapper<T, const T&, T&&>; |
| |
| class DWrapper final : public RefCountedWrapper<StylePathData> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DWrapper, Animation); |
| public: |
| DWrapper() |
| : RefCountedWrapper(CSSPropertyD, &RenderStyle::d, &RenderStyle::setD) |
| { |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| auto* fromValue = value(from); |
| auto* toValue = value(to); |
| return fromValue && toValue && fromValue->canBlend(*toValue); |
| } |
| }; |
| |
| // MARK: - FillLayer Wrappers |
| |
| // Wrapper base class for an animatable property in a FillLayer |
| class FillLayerWrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerWrapperBase, Animation); |
| public: |
| FillLayerWrapperBase(CSSPropertyID property) |
| : m_property(property) |
| { |
| } |
| virtual ~FillLayerWrapperBase() = default; |
| |
| CSSPropertyID property() const { return m_property; } |
| |
| virtual bool equals(const FillLayer*, const FillLayer*) const = 0; |
| virtual void interpolate(FillLayer*, const FillLayer*, const FillLayer*, const Context&) const = 0; |
| virtual bool canInterpolate(const FillLayer*, const FillLayer*) const { return true; } |
| #if !LOG_DISABLED |
| virtual void log(const FillLayer* destination, const FillLayer*, const FillLayer*, double) const = 0; |
| #endif |
| |
| private: |
| CSSPropertyID m_property; |
| }; |
| |
| template<typename T> |
| class FillLayerWrapperWithGetter : public FillLayerWrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerWrapperWithGetter, Animation); |
| WTF_MAKE_NONCOPYABLE(FillLayerWrapperWithGetter); |
| public: |
| FillLayerWrapperWithGetter(CSSPropertyID property, T (FillLayer::*getter)() const) |
| : FillLayerWrapperBase(property) |
| , m_getter(getter) |
| { |
| } |
| |
| protected: |
| bool equals(const FillLayer* a, const FillLayer* b) const override |
| { |
| if (a == b) |
| return true; |
| if (!a || !b) |
| return false; |
| return value(a) == value(b); |
| } |
| |
| T value(const FillLayer* layer) const |
| { |
| return (layer->*m_getter)(); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const override |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << value(from) << " to " << value(to) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(destination)); |
| } |
| #endif |
| |
| private: |
| T (FillLayer::*m_getter)() const; |
| }; |
| |
| template<typename T> |
| class FillLayerWrapper final : public FillLayerWrapperWithGetter<const T&> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerWrapper, Animation); |
| public: |
| FillLayerWrapper(CSSPropertyID property, const T& (FillLayer::*getter)() const, void (FillLayer::*setter)(T)) |
| : FillLayerWrapperWithGetter<const T&>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| private: |
| void interpolate(FillLayer* destination, const FillLayer* from, const FillLayer* to, const Context& context) const final |
| { |
| (destination->*this->m_setter)(blendFunc(this->value(from), this->value(to), context)); |
| } |
| |
| bool canInterpolate(const FillLayer* from, const FillLayer* to) const final |
| { |
| return canInterpolateLengthVariants(this->value(from), this->value(to)); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << FillLayerWrapperWithGetter<const T&>::property() |
| << " from " << FillLayerWrapperWithGetter<const T&>::value(from) |
| << " to " << FillLayerWrapperWithGetter<const T&>::value(to) |
| << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << FillLayerWrapperWithGetter<const T&>::value(destination)); |
| } |
| #endif |
| |
| void (FillLayer::*m_setter)(T); |
| }; |
| |
| template<typename StyleType> |
| class FillLayerStyleTypeWrapper final : public FillLayerWrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerStyleTypeWrapper, Animation); |
| public: |
| FillLayerStyleTypeWrapper(CSSPropertyID property, const StyleType& (FillLayer::*getter)() const, void (FillLayer::*setter)(StyleType&&)) |
| : FillLayerWrapperBase(property) |
| , m_getter(getter) |
| , m_setter(setter) |
| { |
| } |
| |
| bool equals(const FillLayer* from, const FillLayer* to) const override |
| { |
| if (from == to) |
| return true; |
| if (!from || !to) |
| return false; |
| return Style::equalsForBlending(value(from), value(to)); |
| } |
| |
| bool canInterpolate(const FillLayer* from, const FillLayer* to) const override final |
| { |
| return Style::canBlend(value(from), value(to)); |
| } |
| |
| void interpolate(FillLayer* destination, const FillLayer* from, const FillLayer* to, const Context& context) const override final |
| { |
| (destination->*m_setter)(Style::blend(value(from), value(to), context)); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const override final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << value(from) << " to " << value(to) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(destination)); |
| } |
| #endif |
| |
| private: |
| const StyleType& value(const FillLayer* layer) const |
| { |
| return (layer->*m_getter)(); |
| } |
| |
| const StyleType& (FillLayer::*m_getter)() const; |
| void (FillLayer::*m_setter)(StyleType&&); |
| }; |
| |
| template<typename T> |
| class FillLayerRefCountedWrapper : public FillLayerWrapperWithGetter<T*> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerRefCountedWrapper, Animation); |
| public: |
| FillLayerRefCountedWrapper(CSSPropertyID property, T* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<T>&&)) |
| : FillLayerWrapperWithGetter<T*>(property, getter) |
| , m_setter(setter) |
| { |
| } |
| |
| private: |
| void interpolate(FillLayer* destination, const FillLayer* from, const FillLayer* to, const Context& context) const final |
| { |
| (destination->*this->m_setter)(blendFunc(this->value(from), this->value(to), context)); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const override |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << FillLayerWrapperWithGetter<T*>::property() |
| << " from " << FillLayerWrapperWithGetter<T*>::value(from) |
| << " to " << FillLayerWrapperWithGetter<T*>::value(to) |
| << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << FillLayerWrapperWithGetter<T*>::value(destination)); |
| } |
| #endif |
| |
| void (FillLayer::*m_setter)(RefPtr<T>&&); |
| }; |
| |
| class FillLayerStyleImageWrapper final : public FillLayerRefCountedWrapper<StyleImage> { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayerStyleImageWrapper, Animation); |
| public: |
| FillLayerStyleImageWrapper(CSSPropertyID property, StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<StyleImage>&&)) |
| : FillLayerRefCountedWrapper(property, getter, setter) |
| { |
| } |
| |
| private: |
| bool equals(const FillLayer* a, const FillLayer* b) const final |
| { |
| if (a == b) |
| return true; |
| if (!a || !b) |
| return false; |
| return arePointingToEqualData(value(a), value(b)); |
| } |
| |
| bool canInterpolate(const FillLayer* from, const FillLayer* to) const final |
| { |
| if (property() == CSSPropertyMaskImage) |
| return false; |
| return value(from) && value(to); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << this->value(from) << " to " << this->value(to) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(destination)); |
| } |
| #endif |
| }; |
| |
| template<typename T> |
| class DiscreteFillLayerWrapper final : public FillLayerWrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DiscreteFillLayerWrapper, Animation); |
| public: |
| DiscreteFillLayerWrapper(CSSPropertyID property, T (FillLayer::*getter)() const, void (FillLayer::*setter)(T)) |
| : FillLayerWrapperBase(property) |
| , m_getter(getter) |
| , m_setter(setter) |
| { |
| } |
| |
| private: |
| bool equals(const FillLayer* a, const FillLayer* b) const final |
| { |
| return (a->*m_getter)() == (b->*m_getter)(); |
| } |
| |
| bool canInterpolate(const FillLayer*, const FillLayer*) const final |
| { |
| return false; |
| } |
| |
| void interpolate(FillLayer* destination, const FillLayer* from, const FillLayer* to, const Context& context) const final |
| { |
| ASSERT(!context.progress || context.progress == 1.0); |
| (destination->*m_setter)(((context.progress ? to : from)->*m_getter)()); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const FillLayer* destination, const FillLayer* from, const FillLayer* to, double progress) const final |
| { |
| LOG_WITH_STREAM(Animations, stream << " blending " << property() << " from " << (from->*m_getter)() << " to " << (to->*m_getter)() << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << (destination->*m_getter)()); |
| } |
| #endif |
| |
| T (FillLayer::*m_getter)() const; |
| void (FillLayer::*m_setter)(T); |
| }; |
| |
| class FillLayersWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(FillLayersWrapper, Animation); |
| public: |
| typedef const FillLayer& (RenderStyle::*LayersGetter)() const; |
| typedef FillLayer& (RenderStyle::*LayersAccessor)(); |
| |
| FillLayersWrapper(CSSPropertyID property, LayersGetter getter, LayersAccessor accessor) |
| : WrapperBase(property) |
| , m_layersGetter(getter) |
| , m_layersAccessor(accessor) |
| { |
| switch (property) { |
| case CSSPropertyBackgroundPositionX: |
| case CSSPropertyWebkitMaskPositionX: |
| m_fillLayerWrapper = makeUnique<FillLayerStyleTypeWrapper<FillPositionX>>(property, &FillLayer::xPosition, &FillLayer::setXPosition); |
| break; |
| case CSSPropertyBackgroundPositionY: |
| case CSSPropertyWebkitMaskPositionY: |
| m_fillLayerWrapper = makeUnique<FillLayerStyleTypeWrapper<FillPositionY>>(property, &FillLayer::yPosition, &FillLayer::setYPosition); |
| break; |
| case CSSPropertyBackgroundSize: |
| case CSSPropertyWebkitBackgroundSize: |
| case CSSPropertyMaskSize: |
| m_fillLayerWrapper = makeUnique<FillLayerWrapper<LengthSize>>(property, &FillLayer::sizeLength, &FillLayer::setSizeLength); |
| break; |
| case CSSPropertyBackgroundImage: |
| case CSSPropertyMaskImage: |
| m_fillLayerWrapper = makeUnique<FillLayerStyleImageWrapper>(property, &FillLayer::image, &FillLayer::setImage); |
| break; |
| case CSSPropertyMaskClip: |
| m_fillLayerWrapper = makeUnique<DiscreteFillLayerWrapper<FillBox>>(property, &FillLayer::clip, &FillLayer::setClip); |
| break; |
| case CSSPropertyMaskOrigin: |
| m_fillLayerWrapper = makeUnique<DiscreteFillLayerWrapper<FillBox>>(property, &FillLayer::origin, &FillLayer::setOrigin); |
| break; |
| case CSSPropertyMaskComposite: |
| m_fillLayerWrapper = makeUnique<DiscreteFillLayerWrapper<CompositeOperator>>(property, &FillLayer::composite, &FillLayer::setComposite); |
| break; |
| case CSSPropertyMaskMode: |
| m_fillLayerWrapper = makeUnique<DiscreteFillLayerWrapper<MaskMode>>(property, &FillLayer::maskMode, &FillLayer::setMaskMode); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| if (&a == &b) |
| return true; |
| |
| auto* fromLayer = &(a.*m_layersGetter)(); |
| auto* toLayer = &(b.*m_layersGetter)(); |
| |
| while (fromLayer && toLayer) { |
| if (!m_fillLayerWrapper->equals(fromLayer, toLayer)) |
| return false; |
| |
| fromLayer = fromLayer->next(); |
| toLayer = toLayer->next(); |
| } |
| |
| return true; |
| } |
| |
| bool canInterpolate(const RenderStyle& from, const RenderStyle& to, CompositeOperation) const final |
| { |
| auto* fromLayer = &(from.*m_layersGetter)(); |
| auto* toLayer = &(to.*m_layersGetter)(); |
| |
| while (fromLayer && toLayer) { |
| if (fromLayer->sizeType() != toLayer->sizeType()) |
| return false; |
| |
| if (!m_fillLayerWrapper->canInterpolate(fromLayer, toLayer)) |
| return false; |
| |
| fromLayer = fromLayer->next(); |
| toLayer = toLayer->next(); |
| } |
| |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| auto* fromLayer = &(from.*m_layersGetter)(); |
| auto* toLayer = &(to.*m_layersGetter)(); |
| auto* dstLayer = &(destination.*m_layersAccessor)(); |
| |
| if (context.isDiscrete) { |
| ASSERT(!context.progress || context.progress == 1.0); |
| auto* layer = context.progress ? toLayer : fromLayer; |
| fromLayer = layer; |
| toLayer = layer; |
| } |
| |
| size_t layerCount = 0; |
| Vector<FillLayer*> previousDstLayers; |
| FillLayer* previousDstLayer = nullptr; |
| while (fromLayer && toLayer) { |
| if (dstLayer) |
| previousDstLayers.append(dstLayer); |
| else { |
| ASSERT(!previousDstLayers.isEmpty()); |
| auto* layerToCopy = previousDstLayers[layerCount % previousDstLayers.size()]; |
| previousDstLayer->setNext(layerToCopy->copy()); |
| dstLayer = previousDstLayer->next(); |
| } |
| |
| dstLayer->setSizeType((context.progress ? toLayer : fromLayer)->sizeType()); |
| m_fillLayerWrapper->interpolate(dstLayer, fromLayer, toLayer, context); |
| fromLayer = fromLayer->next(); |
| toLayer = toLayer->next(); |
| |
| previousDstLayer = dstLayer; |
| dstLayer = dstLayer->next(); |
| layerCount++; |
| } |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const final |
| { |
| auto* fromLayer = &(from.*m_layersGetter)(); |
| auto* toLayer = &(to.*m_layersGetter)(); |
| auto* dstLayer = &(destination.*m_layersGetter)(); |
| |
| while (fromLayer && toLayer && dstLayer) { |
| m_fillLayerWrapper->log(dstLayer, fromLayer, toLayer, progress); |
| fromLayer = fromLayer->next(); |
| toLayer = toLayer->next(); |
| dstLayer = dstLayer->next(); |
| } |
| } |
| #endif |
| |
| private: |
| std::unique_ptr<FillLayerWrapperBase> m_fillLayerWrapper; |
| LayersGetter m_layersGetter; |
| LayersAccessor m_layersAccessor; |
| }; |
| |
| // MARK: - Shorthand Wrapper |
| |
| class ShorthandWrapper final : public WrapperBase { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(ShorthandWrapper, Animation); |
| public: |
| ShorthandWrapper(CSSPropertyID property, Vector<WrapperBase*> longhandWrappers) |
| : WrapperBase(property) |
| , m_longhandWrappers(WTFMove(longhandWrappers)) |
| { |
| } |
| |
| bool equals(const RenderStyle& a, const RenderStyle& b) const final |
| { |
| if (&a == &b) |
| return true; |
| |
| for (auto& wrapper : m_longhandWrappers) { |
| if (!wrapper->equals(a, b)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void interpolate(RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, const Context& context) const final |
| { |
| for (auto& wrapper : m_longhandWrappers) |
| wrapper->interpolate(destination, from, to, context); |
| } |
| |
| #if !LOG_DISABLED |
| void log(const RenderStyle& from, const RenderStyle& to, const RenderStyle& destination, double progress) const final |
| { |
| for (auto& wrapper : m_longhandWrappers) |
| wrapper->log(from, to, destination, progress); |
| } |
| #endif |
| |
| private: |
| Vector<WrapperBase*> m_longhandWrappers; |
| }; |
| |
| } // namespace WebCore::Style::Interpolation |