[CompositeClipPathAnimations] Fix behavior for custom timing functions

Previously, custom timing functions affected the float animation used
as input to the clip-path paint worklet. This caused a situation where the clip path paint worklet would be confused as to which keyframe the animation was on.

This change forces the float animation to use linear timing
functions, and applies the timing functions within the paint worklet.

Bug: 1374390

Change-Id: Idede83e0b75c84690a0b6a6d3cee4fc94a8771dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3975531
Commit-Queue: Claire Chambers <clchambers@microsoft.com>
Reviewed-by: Robert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084492}
diff --git a/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-ref.html b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-ref.html
new file mode 100644
index 0000000..829055a
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<style>
+  .green {
+    background-color: green;
+    position: fixed;
+    left: 0px;
+    top: 0px;
+    width: 200px;
+    height: 200px;
+  }
+
+</style>
+
+<body>
+  <div class="green"></div>
+</body>
+
+</html>
diff --git a/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-reverse.html b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-reverse.html
new file mode 100644
index 0000000..7525f67
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function-reverse.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-custom-timing-function-ref.html">
+<!--
+  Test that ensures that the bounding rect for a clip path animation is not
+  limited to the size of the largest keyframe.
+
+  Test is done by occulsion to prevent flakes. Test succeeds if the extrapolated
+  clip area (green) is large enough to occlude the entire red area.
+
+  This is the reverse of clip-path-animation-custom-timing-fumction.html,
+  and tests extrapolation in the negative direction
+-->
+<style>
+  @keyframes clippath {
+    0% {
+      clip-path: inset(45% 45%);
+    }
+
+    25% {
+      clip-path: inset(49% 459);
+    }
+
+    50% {
+      clip-path: inset(45% 45%);
+    }
+
+    75% {
+      clip-path: inset(40% 40%);
+      animation-timing-function: cubic-bezier(0, -9, 1, -9);
+      /* Test that the correct keyframe's timing function is being used. */
+    }
+
+    100% {
+      clip-path: inset(45% 45%);
+    }
+  }
+
+  .green {
+    background-color: green;
+    position: fixed;
+    left: 0px;
+    top: 0px;
+    width: 200px;
+    height: 200px;
+  }
+
+  /* for this test to succeed, the red rect needs to be entirely
+  occluded by the inner green rect, requiring extrapolation
+  beyond the largest keyframe. */
+  .red {
+    background-color: red;
+    position: fixed;
+    left: 50px;
+    top: 50px;
+    width: 100px;
+    height: 100px;
+  }
+
+  .anim {
+    animation: clippath 10000000s -8750000s
+      /* halfway between the second to last and last keyframes.*/
+    ;
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="green">
+    <div class="red">
+      <div class="green anim"></div>
+    </div>
+  </div>
+
+  <script>
+    document.getAnimations()[0].ready.then(takeScreenshot);
+  </script>
+</body>
+
+</html>
diff --git a/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function.html b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function.html
new file mode 100644
index 0000000..e73f01b
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-animation-custom-timing-function.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-custom-timing-function-ref.html">
+<!--
+  Test that ensures that the bounding rect for a clip path animation is not
+  limited to the size of the largest keyframe.
+
+  Test is done by occulsion to prevent flakes. Test succeeds if the extrapolated
+  clip area (green) is large enough to occlude the entire red area.
+-->
+<style>
+  @keyframes clippath {
+    0% {
+      clip-path: inset(45% 45%);
+    }
+
+    25% {
+      clip-path: inset(40% 40%);
+    }
+
+    50% {
+      clip-path: inset(45% 45%);
+    }
+
+    75% {
+      clip-path: inset(40% 40%);
+    }
+
+    100% {
+      clip-path: inset(45% 45%);
+    }
+  }
+
+  .green {
+    background-color: green;
+    position: fixed;
+    left: 0px;
+    top: 0px;
+    width: 200px;
+    height: 200px;
+  }
+
+  /* for this test to succeed, the red rect needs to be entirely
+  occluded by the inner green rect, requiring extrapolation
+  beyond the largest keyframe. */
+  .red {
+    background-color: red;
+    position: fixed;
+    left: 50px;
+    top: 50px;
+    width: 100px;
+    height: 100px;
+  }
+
+  .anim {
+    animation: clippath 10000000s -1250000s
+      /* halfway between the first and second keyframes. */
+    ;
+    animation-timing-function: cubic-bezier(0, 9, 1, 9);
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="green">
+    <div class="red">
+      <div class="green anim"></div>
+    </div>
+  </div>
+
+  <script>
+    document.getAnimations()[0].ready.then(takeScreenshot);
+  </script>
+</body>
+
+</html>
diff --git a/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function-ref.html b/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function-ref.html
new file mode 100644
index 0000000..d351f80
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+  .container {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    clip-path: circle(50% at 50% 50%);
+  }
+
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
+
+</html>
diff --git a/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function.html b/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function.html
new file mode 100644
index 0000000..9e4eb01
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-animation-non-keyframe-timing-function.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta name=fuzzy content="0-5;0-5">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-non-keyframe-timing-function-ref.html">
+<style>
+  .container {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    document.querySelector('.container').animate([
+      // 1st keyframe must have a non-linear easing function or the animation
+      // will extrapolate based on the animation-wide timing function
+      { clipPath: 'circle(20% at 20% 20%)', easing: 'ease' },
+      { clipPath: 'circle(50% at 50% 50%)' }
+    ], {
+      easing: 'cubic-bezier(0, 2, 1, 2)',
+      duration: 1000000,
+      delay: -500000
+    });
+    document.getAnimations()[0].ready.then(() => {
+      takeScreenshot();
+    });
+  </script>
+</body>
+
+</html>
diff --git a/css/css-masking/clip-path/animations/clip-path-transition-custom-timing-function.html b/css/css-masking/clip-path/animations/clip-path-transition-custom-timing-function.html
new file mode 100644
index 0000000..022b1f0
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-transition-custom-timing-function.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-custom-timing-function-ref.html">
+<style>
+  .transition {
+    clip-path: inset(45%);
+    transition-property: clip-path;
+    transition-duration: 1000000s;
+    transition-timing-function: cubic-bezier(0, 9, 1, 9);
+    transition-delay: -500000s;
+  }
+
+  .green {
+    background-color: green;
+    position: fixed;
+    left: 0px;
+    top: 0px;
+    width: 200px;
+    height: 200px;
+  }
+
+  /* for this test to succeed, the red rect needs to be entirely
+  occluded by the inner green rect, requiring extrapolation
+  beyond the largest keyframe. */
+  .red {
+    background-color: red;
+    position: fixed;
+    left: 50px;
+    top: 50px;
+    width: 100px;
+    height: 100px;
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="green">
+    <div class="red">
+      <div class="green transition" id="target"></div>
+    </div>
+  </div>
+
+  <script>
+    function update() {
+      document.getElementById('target').style.clipPath = "inset(40%)";
+      requestAnimationFrame(() => {
+        takeScreenshot();
+      });
+    }
+    requestAnimationFrame(function () {
+      requestAnimationFrame(update);
+    });
+  </script>
+</body>
+
+</html>