Implement animation for color-filter
https://bugs.webkit.org/show_bug.cgi?id=185092
rdar://problem/39773810

Reviewed by Tim Horton.

Source/WebCore:

Implement animation of color-filter.

This requires tracking whether the color-filter function lists match for both old and new
animation code paths.

The filter-related ProperyWappers in CSSPropertyAnimation are cleaned up to use a single wrapper,
which has to pass the propertyID to the blend function so we know which "lists match" to check.
This wrapper reports that its accelerated for filter and backdrop-filter, but not color-filter.

Test: css3/color-filters/color-filter-animation.html

* animation/CSSPropertyBlendingClient.h:
* animation/KeyframeEffectReadOnly.cpp:
(WebCore::KeyframeEffectReadOnly::setBlendingKeyframes):
(WebCore::KeyframeEffectReadOnly::checkForMatchingColorFilterFunctionLists):
* animation/KeyframeEffectReadOnly.h:
* page/animation/AnimationBase.h:
* page/animation/CSSPropertyAnimation.cpp:
(WebCore::blendFunc):
(WebCore::PropertyWrapperFilter::PropertyWrapperFilter):
(WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap):
(WebCore::PropertyWrapperAcceleratedFilter::PropertyWrapperAcceleratedFilter): Deleted.
(WebCore::PropertyWrapperAcceleratedBackdropFilter::PropertyWrapperAcceleratedBackdropFilter): Deleted.
(WebCore::PropertyWrapperAcceleratedBackdropFilter::animationIsAccelerated const): Deleted.
(WebCore::PropertyWrapperAcceleratedBackdropFilter::blend const): Deleted.
* page/animation/ImplicitAnimation.cpp:
(WebCore::ImplicitAnimation::reset):
(WebCore::ImplicitAnimation::checkForMatchingColorFilterFunctionLists):
* page/animation/ImplicitAnimation.h:
* page/animation/KeyframeAnimation.cpp:
(WebCore::KeyframeAnimation::KeyframeAnimation):
(WebCore::KeyframeAnimation::checkForMatchingColorFilterFunctionLists):
* page/animation/KeyframeAnimation.h:

LayoutTests:

Fix the testing to recognize unprefixed filter, and color-filter.

Add a color-filter animation test.

* animations/resources/animation-test-helpers.js:
(getPropertyValue):
(comparePropertyValue):
* css3/color-filters/color-filter-animation-expected.txt: Added.
* css3/color-filters/color-filter-animation.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@231123 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 1197684..c44cf86 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,21 @@
+2018-04-27  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement animation for color-filter
+        https://bugs.webkit.org/show_bug.cgi?id=185092
+        rdar://problem/39773810
+
+        Reviewed by Tim Horton.
+        
+        Fix the testing to recognize unprefixed filter, and color-filter.
+        
+        Add a color-filter animation test.
+
+        * animations/resources/animation-test-helpers.js:
+        (getPropertyValue):
+        (comparePropertyValue):
+        * css3/color-filters/color-filter-animation-expected.txt: Added.
+        * css3/color-filters/color-filter-animation.html: Added.
+
 2018-04-27  Ryan Haddad  <ryanhaddad@apple.com>
 
         Update TestExpectations for fast/loader/submit-form-while-parsing-2.html.
diff --git a/LayoutTests/animations/resources/animation-test-helpers.js b/LayoutTests/animations/resources/animation-test-helpers.js
index 85c9828..aac905d 100644
--- a/LayoutTests/animations/resources/animation-test-helpers.js
+++ b/LayoutTests/animations/resources/animation-test-helpers.js
@@ -430,6 +430,8 @@
                || property == "listStyleImage"
                || property == "webkitMaskImage"
                || property == "webkitMaskBoxImage"
+               || property == "filter"
+               || property == "colorFilter"
                || property == "webkitFilter"
                || property == "webkitBackdropFilter"
                || property == "webkitClipPath"
@@ -469,7 +471,7 @@
                     break;
             }
         }
-    } else if (property == "webkitFilter" || property == "webkitBackdropFilter") {
+    } else if (property == "webkitFilter" || property == "webkitBackdropFilter" || property == "filter" || property == "colorFilter") {
         var filterParameters = parseFilterFunctionList(computedValue);
         var filter2Parameters = parseFilterFunctionList(expectedValue);
         result = compareFilterFunctions(filterParameters, filter2Parameters, tolerance);
diff --git a/LayoutTests/css3/color-filters/color-filter-animation-expected.txt b/LayoutTests/css3/color-filters/color-filter-animation-expected.txt
new file mode 100644
index 0000000..7bb0c879
--- /dev/null
+++ b/LayoutTests/css3/color-filters/color-filter-animation-expected.txt
@@ -0,0 +1,10 @@
+       
+PASS - "colorFilter" property for "grayscale-box" element at 1s saw something close to: grayscale(0.5)
+PASS - "colorFilter" property for "sepia-box" element at 1s saw something close to: sepia(0.5)
+PASS - "colorFilter" property for "saturate-box" element at 1s saw something close to: saturate(0.5)
+PASS - "colorFilter" property for "huerotate-box" element at 1s saw something close to: hue-rotate(90deg)
+PASS - "colorFilter" property for "invert-box" element at 1s saw something close to: invert(0.5)
+PASS - "colorFilter" property for "opacity-box" element at 1s saw something close to: opacity(0.5)
+PASS - "colorFilter" property for "brightness-box" element at 1s saw something close to: brightness(0.5)
+PASS - "colorFilter" property for "contrast-box" element at 1s saw something close to: contrast(0.5)
+
diff --git a/LayoutTests/css3/color-filters/color-filter-animation.html b/LayoutTests/css3/color-filters/color-filter-animation.html
new file mode 100644
index 0000000..f1ed9fe
--- /dev/null
+++ b/LayoutTests/css3/color-filters/color-filter-animation.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    .box {
+        height: 100px;
+        width: 100px;
+        margin: 10px;
+        background-color: blue;
+        display: inline-block;
+    }
+
+    #grayscale-box {
+      animation: grayscale-anim 2s linear
+    }
+
+    #sepia-box {
+      animation: sepia-anim 2s linear
+    }
+
+    #saturate-box {
+      animation: saturate-anim 2s linear
+    }
+
+    #huerotate-box {
+      animation: huerotate-anim 2s linear
+    }
+
+    #invert-box {
+      animation: invert-anim 2s linear
+    }
+
+    #opacity-box {
+      animation: opacity-anim 2s linear
+    }
+
+    #brightness-box {
+      animation: brightness-anim 2s linear
+    }
+
+    #contrast-box {
+      animation: contrast-anim 2s linear
+    }
+
+
+    @keyframes grayscale-anim {
+        from { color-filter: grayscale(0); }
+        to   { color-filter: grayscale(1); }
+    }
+
+    @keyframes sepia-anim {
+        from { color-filter: sepia(0); }
+        to   { color-filter: sepia(1); }
+    }
+
+    @keyframes saturate-anim {
+        from { color-filter: saturate(0); }
+        to   { color-filter: saturate(1); }
+    }
+
+    @keyframes huerotate-anim {
+        from { color-filter: hue-rotate(0); }
+        to   { color-filter: hue-rotate(180deg); }
+    }
+
+    @keyframes invert-anim {
+        from { color-filter: invert(0); }
+        to   { color-filter: invert(1); }
+    }
+
+    @keyframes opacity-anim {
+        from { color-filter: opacity(1); }
+        to   { color-filter: opacity(0); }
+    }
+
+    @keyframes brightness-anim {
+        from { color-filter: brightness(1); }
+        to   { color-filter: brightness(0); }
+    }
+
+    @keyframes contrast-anim {
+        from { color-filter: contrast(1); }
+        to   { color-filter: contrast(0); }
+    }
+
+  </style>
+  <script src="../../animations/resources/animation-test-helpers.js"></script>
+  <script type="text/javascript">
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["grayscale-anim",  1, "grayscale-box", "colorFilter", 'grayscale(0.5)', 0.05],
+      ["sepia-anim",  1, "sepia-box", "colorFilter", 'sepia(0.5)', 0.05],
+      ["saturate-anim",  1, "saturate-box", "colorFilter", 'saturate(0.5)', 0.05],
+      ["huerotate-anim",  1, "huerotate-box", "colorFilter", 'hue-rotate(90deg)', 2],
+      ["invert-anim",  1, "invert-box", "colorFilter", 'invert(0.5)', 0.05],
+      ["opacity-anim",  1, "opacity-box", "colorFilter", 'opacity(0.5)', 0.05],
+      ["brightness-anim",  1, "brightness-box", "colorFilter", 'brightness(0.5)', 0.05],
+      ["contrast-anim",  1, "contrast-box", "colorFilter", 'contrast(0.5)', 0.05],
+    ];
+    
+    runAnimationTest(expectedValues);
+  </script>
+</head>
+<body>
+
+<div class="box" id="grayscale-box"></div>
+<div class="box" id="sepia-box"></div>
+<div class="box" id="saturate-box"></div>
+<div class="box" id="huerotate-box"></div>
+<div class="box" id="invert-box"></div>
+<div class="box" id="opacity-box"></div>
+<div class="box" id="brightness-box"></div>
+<div class="box" id="contrast-box"></div>
+
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 2f8f5a1..e3dd00d 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,45 @@
+2018-04-27  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement animation for color-filter
+        https://bugs.webkit.org/show_bug.cgi?id=185092
+        rdar://problem/39773810
+
+        Reviewed by Tim Horton.
+        
+        Implement animation of color-filter.
+        
+        This requires tracking whether the color-filter function lists match for both old and new
+        animation code paths.
+        
+        The filter-related ProperyWappers in CSSPropertyAnimation are cleaned up to use a single wrapper,
+        which has to pass the propertyID to the blend function so we know which "lists match" to check.
+        This wrapper reports that its accelerated for filter and backdrop-filter, but not color-filter.
+
+        Test: css3/color-filters/color-filter-animation.html
+
+        * animation/CSSPropertyBlendingClient.h:
+        * animation/KeyframeEffectReadOnly.cpp:
+        (WebCore::KeyframeEffectReadOnly::setBlendingKeyframes):
+        (WebCore::KeyframeEffectReadOnly::checkForMatchingColorFilterFunctionLists):
+        * animation/KeyframeEffectReadOnly.h:
+        * page/animation/AnimationBase.h:
+        * page/animation/CSSPropertyAnimation.cpp:
+        (WebCore::blendFunc):
+        (WebCore::PropertyWrapperFilter::PropertyWrapperFilter):
+        (WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap):
+        (WebCore::PropertyWrapperAcceleratedFilter::PropertyWrapperAcceleratedFilter): Deleted.
+        (WebCore::PropertyWrapperAcceleratedBackdropFilter::PropertyWrapperAcceleratedBackdropFilter): Deleted.
+        (WebCore::PropertyWrapperAcceleratedBackdropFilter::animationIsAccelerated const): Deleted.
+        (WebCore::PropertyWrapperAcceleratedBackdropFilter::blend const): Deleted.
+        * page/animation/ImplicitAnimation.cpp:
+        (WebCore::ImplicitAnimation::reset):
+        (WebCore::ImplicitAnimation::checkForMatchingColorFilterFunctionLists):
+        * page/animation/ImplicitAnimation.h:
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::KeyframeAnimation):
+        (WebCore::KeyframeAnimation::checkForMatchingColorFilterFunctionLists):
+        * page/animation/KeyframeAnimation.h:
+
 2018-04-27  Zalan Bujtas  <zalan@apple.com>
 
         [LFC] Add FormattingContext::computeWidth/computeHeight logic.
diff --git a/Source/WebCore/animation/CSSPropertyBlendingClient.h b/Source/WebCore/animation/CSSPropertyBlendingClient.h
index 429c3bb..0d7747e 100644
--- a/Source/WebCore/animation/CSSPropertyBlendingClient.h
+++ b/Source/WebCore/animation/CSSPropertyBlendingClient.h
@@ -41,6 +41,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     virtual bool backdropFilterFunctionListsMatch() const = 0;
 #endif
+    virtual bool colorFilterFunctionListsMatch() const = 0;
 
     virtual ~CSSPropertyBlendingClient() = default;
 };
diff --git a/Source/WebCore/animation/KeyframeEffectReadOnly.cpp b/Source/WebCore/animation/KeyframeEffectReadOnly.cpp
index efad647..54fad57 100644
--- a/Source/WebCore/animation/KeyframeEffectReadOnly.cpp
+++ b/Source/WebCore/animation/KeyframeEffectReadOnly.cpp
@@ -724,6 +724,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     checkForMatchingBackdropFilterFunctionLists();
 #endif
+    checkForMatchingColorFilterFunctionLists();
 }
 
 void KeyframeEffectReadOnly::checkForMatchingTransformFunctionLists()
@@ -814,6 +815,13 @@
 }
 #endif
 
+void KeyframeEffectReadOnly::checkForMatchingColorFilterFunctionLists()
+{
+    m_colorFilterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyColorFilter, [] (const RenderStyle& style) -> const FilterOperations& {
+        return style.colorFilter();
+    });
+}
+
 void KeyframeEffectReadOnly::computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle)
 {
     ASSERT(is<DeclarativeAnimation>(animation()));
diff --git a/Source/WebCore/animation/KeyframeEffectReadOnly.h b/Source/WebCore/animation/KeyframeEffectReadOnly.h
index 59d2229..4cf8353 100644
--- a/Source/WebCore/animation/KeyframeEffectReadOnly.h
+++ b/Source/WebCore/animation/KeyframeEffectReadOnly.h
@@ -111,6 +111,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     bool backdropFilterFunctionListsMatch() const override { return m_backdropFilterFunctionListsMatch; }
 #endif
+    bool colorFilterFunctionListsMatch() const override { return m_colorFilterFunctionListsMatch; }
 
     void computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
     bool stylesWouldYieldNewCSSTransitionsBlendingKeyframes(const RenderStyle& oldStyle, const RenderStyle& newStyle) const;
@@ -149,6 +150,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     void checkForMatchingBackdropFilterFunctionLists();
 #endif
+    void checkForMatchingColorFilterFunctionLists();
 
     bool checkForMatchingFilterFunctionLists(CSSPropertyID, const std::function<const FilterOperations& (const RenderStyle&)>&) const;
 
@@ -162,6 +164,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     bool m_backdropFilterFunctionListsMatch { false };
 #endif
+    bool m_colorFilterFunctionListsMatch { false };
 
     RefPtr<Element> m_target;
     KeyframeList m_blendingKeyframes;
diff --git a/Source/WebCore/page/animation/AnimationBase.h b/Source/WebCore/page/animation/AnimationBase.h
index 4c733f1..b4d3743 100644
--- a/Source/WebCore/page/animation/AnimationBase.h
+++ b/Source/WebCore/page/animation/AnimationBase.h
@@ -186,6 +186,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     bool backdropFilterFunctionListsMatch() const override { return m_backdropFilterFunctionListsMatch; }
 #endif
+    bool colorFilterFunctionListsMatch() const override { return m_colorFilterFunctionListsMatch; }
 
     // Freeze the animation; used by DumpRenderTree.
     void freezeAtTime(double t);
@@ -260,6 +261,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     bool m_backdropFilterFunctionListsMatch { false };
 #endif
+    bool m_colorFilterFunctionListsMatch { false };
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
index 7efcb54..8820d38 100644
--- a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
+++ b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
@@ -200,18 +200,30 @@
     return result;
 }
 
-static inline FilterOperations blendFunc(const CSSPropertyBlendingClient* anim, const FilterOperations& from, const FilterOperations& to, double progress, bool animatingBackdropFilter = false)
+static inline FilterOperations blendFunc(const CSSPropertyBlendingClient* anim, const FilterOperations& from, const FilterOperations& to, double progress, CSSPropertyID propertyID = CSSPropertyFilter)
 {
     FilterOperations result;
 
     // If we have a filter function list, use that to do a per-function animation.
+    
+    bool listsMatch = false;
+    switch (propertyID) {
+    case CSSPropertyFilter:
+        listsMatch = anim->filterFunctionListsMatch();
+        break;
 #if ENABLE(FILTERS_LEVEL_2)
-    if ((!animatingBackdropFilter && anim->filterFunctionListsMatch()) || (animatingBackdropFilter && anim->backdropFilterFunctionListsMatch()))
-#else
-    UNUSED_PARAM(animatingBackdropFilter);
-    if (anim->filterFunctionListsMatch())
+    case CSSPropertyWebkitBackdropFilter:
+        listsMatch = anim->backdropFilterFunctionListsMatch();
+        break;
 #endif
-
+    case CSSPropertyColorFilter:
+        listsMatch = anim->colorFilterFunctionListsMatch();
+        break;
+    default:
+        break;
+    }
+    
+    if (listsMatch)
         result = blendFilterOperations(anim, from, to, progress);
     else {
         // If the filter function lists don't match, we could try to cross-fade, but don't yet have a way to represent that in CSS.
@@ -670,40 +682,29 @@
     }
 };
 
-class PropertyWrapperAcceleratedFilter : public PropertyWrapper<const FilterOperations&> {
+class PropertyWrapperFilter : public PropertyWrapper<const FilterOperations&> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    PropertyWrapperAcceleratedFilter()
-        : PropertyWrapper<const FilterOperations&>(CSSPropertyFilter, &RenderStyle::filter, &RenderStyle::setFilter)
+    PropertyWrapperFilter(CSSPropertyID propertyID, const FilterOperations& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const FilterOperations&))
+        : PropertyWrapper<const FilterOperations&>(propertyID, getter, setter)
     {
     }
 
-    bool animationIsAccelerated() const override { return true; }
+    bool animationIsAccelerated() const override
+    {
+        return property() == CSSPropertyFilter
+#if ENABLE(FILTERS_LEVEL_2)
+            || property() == CSSPropertyWebkitBackdropFilter
+#endif
+            ;
+    }
 
     void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
     {
-        dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress));
+        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const FilterOperations&>::m_getter)(), (b->*PropertyWrapperGetter<const FilterOperations&>::m_getter)(), progress, property()));
     }
 };
 
-#if ENABLE(FILTERS_LEVEL_2)
-class PropertyWrapperAcceleratedBackdropFilter : public PropertyWrapper<const FilterOperations&> {
-    WTF_MAKE_FAST_ALLOCATED;
-public:
-    PropertyWrapperAcceleratedBackdropFilter()
-        : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitBackdropFilter, &RenderStyle::backdropFilter, &RenderStyle::setBackdropFilter)
-    {
-    }
-
-    virtual bool animationIsAccelerated() const { return true; }
-
-    virtual void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
-    {
-        dst->setBackdropFilter(blendFunc(anim, a->backdropFilter(), b->backdropFilter(), progress, true));
-    }
-};
-#endif
-
 static inline size_t shadowListLength(const ShadowData* shadow)
 {
     size_t count;
@@ -1558,10 +1559,13 @@
 
         new PropertyWrapperAcceleratedOpacity(),
         new PropertyWrapperAcceleratedTransform(),
-        new PropertyWrapperAcceleratedFilter(),
+        
+        new PropertyWrapperFilter(CSSPropertyFilter, &RenderStyle::filter, &RenderStyle::setFilter),
 #if ENABLE(FILTERS_LEVEL_2)
-        new PropertyWrapperAcceleratedBackdropFilter(),
+        new PropertyWrapperFilter(CSSPropertyWebkitBackdropFilter, &RenderStyle::backdropFilter, &RenderStyle::setBackdropFilter),
 #endif
+        new PropertyWrapperFilter(CSSPropertyColorFilter, &RenderStyle::colorFilter, &RenderStyle::setColorFilter),
+
         new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath),
 
         new PropertyWrapperShape(CSSPropertyShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside),
diff --git a/Source/WebCore/page/animation/ImplicitAnimation.cpp b/Source/WebCore/page/animation/ImplicitAnimation.cpp
index c6c7d18..080cfcc 100644
--- a/Source/WebCore/page/animation/ImplicitAnimation.cpp
+++ b/Source/WebCore/page/animation/ImplicitAnimation.cpp
@@ -215,6 +215,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     checkForMatchingBackdropFilterFunctionLists();
 #endif
+    checkForMatchingColorFilterFunctionLists();
 }
 
 void ImplicitAnimation::setOverridden(bool b)
@@ -311,6 +312,16 @@
 }
 #endif
 
+void ImplicitAnimation::checkForMatchingColorFilterFunctionLists()
+{
+    m_filterFunctionListsMatch = false;
+
+    if (!m_fromStyle || !m_toStyle)
+        return;
+
+    m_colorFilterFunctionListsMatch = filterOperationsMatch(&m_fromStyle->colorFilter(), m_toStyle->colorFilter());
+}
+
 std::optional<Seconds> ImplicitAnimation::timeToNextService()
 {
     std::optional<Seconds> t = AnimationBase::timeToNextService();
diff --git a/Source/WebCore/page/animation/ImplicitAnimation.h b/Source/WebCore/page/animation/ImplicitAnimation.h
index b28115c..921ef78 100644
--- a/Source/WebCore/page/animation/ImplicitAnimation.h
+++ b/Source/WebCore/page/animation/ImplicitAnimation.h
@@ -86,6 +86,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     void checkForMatchingBackdropFilterFunctionLists();
 #endif
+    void checkForMatchingColorFilterFunctionLists();
 
 private:
     ImplicitAnimation(const Animation&, CSSPropertyID, Element&, CompositeAnimation&, const RenderStyle&);
diff --git a/Source/WebCore/page/animation/KeyframeAnimation.cpp b/Source/WebCore/page/animation/KeyframeAnimation.cpp
index 564f489..73a6f38 100644
--- a/Source/WebCore/page/animation/KeyframeAnimation.cpp
+++ b/Source/WebCore/page/animation/KeyframeAnimation.cpp
@@ -58,6 +58,8 @@
 #if ENABLE(FILTERS_LEVEL_2)
     checkForMatchingBackdropFilterFunctionLists();
 #endif
+    checkForMatchingColorFilterFunctionLists();
+
     computeStackingContextImpact();
     computeLayoutDependency();
 }
@@ -493,6 +495,13 @@
 }
 #endif
 
+void KeyframeAnimation::checkForMatchingColorFilterFunctionLists()
+{
+    m_colorFilterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyColorFilter, [] (const RenderStyle& style) -> const FilterOperations& {
+        return style.colorFilter();
+    });
+}
+
 std::optional<Seconds> KeyframeAnimation::timeToNextService()
 {
     std::optional<Seconds> t = AnimationBase::timeToNextService();
diff --git a/Source/WebCore/page/animation/KeyframeAnimation.h b/Source/WebCore/page/animation/KeyframeAnimation.h
index 208aa6f..d180c0e 100644
--- a/Source/WebCore/page/animation/KeyframeAnimation.h
+++ b/Source/WebCore/page/animation/KeyframeAnimation.h
@@ -91,6 +91,7 @@
 #if ENABLE(FILTERS_LEVEL_2)
     void checkForMatchingBackdropFilterFunctionLists();
 #endif
+    void checkForMatchingColorFilterFunctionLists();
     bool checkForMatchingFilterFunctionLists(CSSPropertyID, const std::function<const FilterOperations& (const RenderStyle&)>&) const;
 
 private: