Part 5: Support transition-behavior when animation values fall back to discrete.

When transition-behavior is allow-discrete, the animation values are
transitionable even if they are not interpoltable, given that the
animation type of the CSS property is by computed value.

Also, we remove `animate()` check from `needs_transitions_update_per_property`.
This check was added for handling the transition between `auto` and
other values long time ago, but now it may be redundant (because we
still pass the tests without it) and we do the same things in
nsTransitionManager as well, so it should be fine to drop it, especially
after we support discrete transitions.

Differential Revision: https://phabricator.services.mozilla.com/D201865

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1805727
gecko-commit: 6203d25d0748a59455054f39056d4e182c4c83c9
gecko-reviewers: emilio
diff --git a/css/css-masking/clip-path/animations/clip-path-interpolation-discrete.html b/css/css-masking/clip-path/animations/clip-path-interpolation-discrete.html
new file mode 100644
index 0000000..9c14372
--- /dev/null
+++ b/css/css-masking/clip-path/animations/clip-path-interpolation-discrete.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>clip-path interpolation with allow-discrete</title>
+    <link rel="help" href="https://drafts.fxtf.org/css-masking-1/#the-clip-path">
+    <meta name="assert" content="clip-path supports animation with allow-discrete.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/css/support/interpolation-testcommon.js"></script>
+  </head>
+  <style>
+    .parent {
+      clip-path: circle(farthest-side);
+    }
+    .target {
+      clip-path: circle(10px);
+    }
+  </style>
+  <body>
+    <script>
+      'use strict';
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'circle(10px)',
+        to: 'inset(20px)',
+      }, [
+        {at: -1, expect: 'circle(10px)'},
+        {at: 0, expect: 'circle(10px)'},
+        {at: 0.4, expect: 'circle(10px)'},
+        {at: 0.5, expect: 'inset(20px)'},
+        {at: 1, expect: 'inset(20px)'},
+        {at: 1.5, expect: 'inset(20px)'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'ellipse()',
+        to: 'padding-box',
+      }, [
+        {at: -1, expect: 'ellipse()'},
+        {at: 0, expect: 'ellipse()'},
+        {at: 0.4, expect: 'ellipse()'},
+        {at: 0.5, expect: 'padding-box'},
+        {at: 1, expect: 'padding-box'},
+        {at: 1.5, expect: 'padding-box'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: neutralKeyframe,
+        to: 'none',
+      }, [
+        {at: -1, expect: 'circle(10px)'},
+        {at: 0, expect: 'circle(10px)'},
+        {at: 0.4, expect: 'circle(10px)'},
+        {at: 0.5, expect: 'none'},
+        {at: 1, expect: 'none'},
+        {at: 1.5, expect: 'none'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'initial',
+        to: 'circle()',
+      }, [
+        {at: -1, expect: 'none'},
+        {at: 0, expect: 'none'},
+        {at: 0.4, expect: 'none'},
+        {at: 0.5, expect: 'circle()'},
+        {at: 1, expect: 'circle()'},
+        {at: 1.5, expect: 'circle()'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'inherit',
+        to: 'circle()',
+      }, [
+        {at: -0.1, expect: 'circle(farthest-side)'},
+        {at: 0, expect: 'circle(farthest-side)'},
+        {at: 0.4, expect: 'circle(farthest-side)'},
+        {at: 0.5, expect: 'circle()'},
+        {at: 1, expect: 'circle()'},
+        {at: 1.5, expect: 'circle()'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'unset',
+        to: 'inset(10%)',
+      }, [
+        {at: -0.1, expect: 'none'},
+        {at: 0, expect: 'none'},
+        {at: 0.4, expect: 'none'},
+        {at: 0.5, expect: 'inset(10%)'},
+        {at: 1, expect: 'inset(10%)'},
+        {at: 1.5, expect: 'inset(10%)'},
+      ]);
+
+      test_interpolation({
+        property: 'clip-path',
+        behavior: 'allow-discrete',
+        from: 'stroke-box',
+        to: 'url("abc")',
+      }, [
+        {at: -0.1, expect: 'stroke-box'},
+        {at: 0, expect: 'stroke-box'},
+        {at: 0.4, expect: 'stroke-box'},
+        {at: 0.5, expect: 'url("abc")'},
+        {at: 1, expect: 'url("abc")'},
+        {at: 1.5, expect: 'url("abc")'},
+      ]);
+
+    </script>
+  </body>
+</html>
diff --git a/css/css-transitions/transition-behaivor.html b/css/css-transitions/transition-behaivor.html
index bc06cae..fa24509 100644
--- a/css/css-transitions/transition-behaivor.html
+++ b/css/css-transitions/transition-behaivor.html
@@ -81,4 +81,42 @@
 }, 'transition-behavior changed to normal should stop the running discrete ' +
    'transitions.');
 
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'transition: clip-path 5s normal; clip-path: circle(50px)',
+  });
+  getComputedStyle(div).transition;
+
+  div.style.clipPath = "circle(farthest-side)";
+  assert_equals(getComputedStyle(div).clipPath, "circle(farthest-side)");
+  assert_equals(div.getAnimations().length, 0,
+                "Should not start a transition of discrete animation values");
+
+  div.style.transitionBehavior = "allow-discrete";
+  div.style.clipPath = "circle(200px)";
+  assert_equals(getComputedStyle(div).clipPath, "circle(farthest-side)");
+  assert_equals(div.getAnimations().length, 1,
+                "Should start a transition for discrete animation values");
+}, 'transition-behavior:allow-discrete should animate for values fallback to ' +
+   'discrete animations.');
+
+promise_test(async t => {
+  const div = addDiv(t, {
+    style: 'transition: clip-path 5s allow-discrete; clip-path: circle(50px)',
+  });
+  getComputedStyle(div).transition;
+
+  div.style.clipPath = "circle(farthest-side)";
+  assert_equals(getComputedStyle(div).clipPath, "circle(50px)");
+  assert_equals(div.getAnimations().length, 1,
+                "Should start a transition for discrete animation values");
+
+  div.style.transitionBehavior = "normal";
+  div.style.clipPath = "ellipse()";
+  assert_equals(getComputedStyle(div).clipPath, "ellipse()");
+  assert_equals(div.getAnimations().length, 0,
+                "The running transition should be cancelled");
+}, 'transition-behavior:normal should cancel the running transitions whose ' +
+   'animation values are not interpolatable');
+
 </script>
diff --git a/css/motion/animation/offset-path-interpolation-007.html b/css/motion/animation/offset-path-interpolation-007.html
new file mode 100644
index 0000000..6629f28
--- /dev/null
+++ b/css/motion/animation/offset-path-interpolation-007.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>offset-path interpolation with allow-discrete</title>
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-path-property">
+    <meta name="assert" content="offset-path supports animation with allow-discrete.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/css/support/interpolation-testcommon.js"></script>
+  </head>
+  <style>
+    .parent {
+      offset-path: circle(farthest-side);
+    }
+    .target {
+      offset-path: circle(10px);
+    }
+  </style>
+  <body>
+    <script>
+      'use strict';
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'circle(10px)',
+        to: 'inset(20px)',
+      }, [
+        {at: -1, expect: 'circle(10px)'},
+        {at: 0, expect: 'circle(10px)'},
+        {at: 0.4, expect: 'circle(10px)'},
+        {at: 0.5, expect: 'inset(20px)'},
+        {at: 1, expect: 'inset(20px)'},
+        {at: 1.5, expect: 'inset(20px)'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'ellipse(at center)',
+        to: 'none',
+      }, [
+        {at: -1, expect: 'ellipse(at center)'},
+        {at: 0, expect: 'ellipse(at center)'},
+        {at: 0.4, expect: 'ellipse(at center)'},
+        {at: 0.5, expect: 'none'},
+        {at: 1, expect: 'none'},
+        {at: 1.5, expect: 'none'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: neutralKeyframe,
+        to: 'none',
+      }, [
+        {at: -1, expect: 'circle(10px)'},
+        {at: 0, expect: 'circle(10px)'},
+        {at: 0.4, expect: 'circle(10px)'},
+        {at: 0.5, expect: 'none'},
+        {at: 1, expect: 'none'},
+        {at: 1.5, expect: 'none'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'initial',
+        to: 'circle()',
+      }, [
+        {at: -1, expect: 'none'},
+        {at: 0, expect: 'none'},
+        {at: 0.4, expect: 'none'},
+        {at: 0.5, expect: 'circle()'},
+        {at: 1, expect: 'circle()'},
+        {at: 1.5, expect: 'circle()'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'inherit',
+        to: 'circle()',
+      }, [
+        {at: -0.1, expect: 'circle(farthest-side)'},
+        {at: 0, expect: 'circle(farthest-side)'},
+        {at: 0.4, expect: 'circle(farthest-side)'},
+        {at: 0.5, expect: 'circle()'},
+        {at: 1, expect: 'circle()'},
+        {at: 1.5, expect: 'circle()'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'unset',
+        to: 'inset(10%)',
+      }, [
+        {at: -0.1, expect: 'none'},
+        {at: 0, expect: 'none'},
+        {at: 0.4, expect: 'none'},
+        {at: 0.5, expect: 'inset(10%)'},
+        {at: 1, expect: 'inset(10%)'},
+        {at: 1.5, expect: 'inset(10%)'},
+      ]);
+
+      test_interpolation({
+        property: 'offset-path',
+        behavior: 'allow-discrete',
+        from: 'ray(0deg)',
+        to: 'url("abc")',
+      }, [
+        {at: -0.1, expect: 'ray(0deg)'},
+        {at: 0, expect: 'ray(0deg)'},
+        {at: 0.4, expect: 'ray(0deg)'},
+        {at: 0.5, expect: 'url("abc")'},
+        {at: 1, expect: 'url("abc")'},
+        {at: 1.5, expect: 'url("abc")'},
+      ]);
+
+    </script>
+  </body>
+</html>