[Web Animations] Implement "Starting of transitions" section from CSS Transitions
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Jun 2018 21:20:47 +0000 (21:20 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Jun 2018 21:20:47 +0000 (21:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186517
<rdar://problem/41000798>

Reviewed by Dean Jackson.

LayoutTests/imported/mozilla:

Mark some progressions in the Mozilla CSS Animations and CSS Transitions tests.

* css-transitions/test_animation-cancel-expected.txt:
* css-transitions/test_animation-ready-expected.txt:

Source/WebCore:

We implement the whole section at https://drafts.csswg.org/css-transitions-1/#starting so that we have spec-compliant starting of CSS Transitions.
To correctly implement this we now maintain two maps of transitions for a given property, a set of running transitions (m_elementToRunningCSSTransitionByCSSPropertyID)
and a set of completed transition (m_elementToCompletedCSSTransitionByCSSPropertyID).

* animation/AnimationTimeline.cpp:
(WebCore::removeCSSTransitionFromMap): Add a utility to remove a CSSTransition from one of the two HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>>
managed by AnimationTimeline (m_elementToRunningCSSTransitionByCSSPropertyID and m_elementToCompletedCSSTransitionByCSSPropertyID).
(WebCore::AnimationTimeline::animationWasRemovedFromElement): Use the new removeCSSTransitionFromMap() utility.
(WebCore::AnimationTimeline::updateCSSAnimationsForElement): We rename "oldStyle" to "currentStyle" to align with the naming used by updateCSSAnimationsForElement().
We also remove an initial check that has been pushed up to Style::TreeResolver::createAnimatedElementUpdate().
(WebCore::propertyInStyleMatchesValueForTransitionInMap): When running the steps mandated by the spec we often need to check whether a given property has a transition
in an AnimationList that matches the value used in a given RenderStyle.
(WebCore::transitionCombinedDuration): We need to compute the combined duration twice while running the steps mandated by the spec, so we have a dedicated utility.
(WebCore::transitionMatchesProperty): New utility that indicates whether a WebCore::Animation matches a given property, by virtue of targeting it directly, targeting
a shorthand for which this property is a longhand, or targeting "all".
(WebCore::AnimationTimeline::updateCSSTransitionsForElement): This is where all of the new implementation for starting transitions happens. We implement the steps
as they appear in the spec.
(WebCore::shouldBackingAnimationBeConsideredForCSSTransition): Deleted.
* animation/AnimationTimeline.h:
* animation/CSSAnimation.cpp:
(WebCore::CSSAnimation::syncPropertiesWithBackingAnimation): Since only CSS Animations respect the delay and duration values as parsed directly in a WebCore:Animation
object, we move code that was previously in DeclarativeAnimation::syncPropertiesWithBackingAnimation to this method. CSS Transitions set those values based on the
delay and duration parameters passed to CSSTransition::create() and computed in AnimationTimeline::updateCSSTransitionsForElement().
* animation/CSSTransition.cpp:
(WebCore::CSSTransition::create): Since the transition's delay and duration is computed in AnimationTimeline::updateCSSTransitionsForElement(), we now require them
to be passed when creating a CSSTransition and pass those to the new setTimingProperties() function. We also expect a target style and reversing-adjusted start style,
as well as a reversing-shortening factor.
(WebCore::CSSTransition::CSSTransition):
(WebCore::CSSTransition::resolve): We need to be able to query the last style computed by a call to resolve() from AnimationTimeline::updateCSSTransitionsForElement(),
we subclass this newly-virtual WebAnimation method and clone the style after we blended the property targeted by this transition.
(WebCore::CSSTransition::setTimingProperties): Set the transition delay and duration as provided in the call to create().
(WebCore::CSSTransition::canBeListed const): Fix a crash that was found while working on this bug where we could access a null effect.
(WebCore::CSSTransition::initialize): Deleted.
(WebCore::CSSTransition::matchesBackingAnimationAndStyles const): Deleted.
* animation/CSSTransition.h:
* animation/DeclarativeAnimation.cpp:
(WebCore::DeclarativeAnimation::syncPropertiesWithBackingAnimation): This virtual method now has an empty definition since the timing properties are now only set
for CSSAnimation.
* animation/KeyframeEffectReadOnly.cpp:
(WebCore::KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes const): Deleted.
* animation/KeyframeEffectReadOnly.h:
* animation/WebAnimation.h:
* page/animation/CSSPropertyAnimation.cpp:
(WebCore::AnimationPropertyWrapperBase::canInterpolate const): Since we need to be able to determine whether some property values can be interpolated, we add a new
canInterpolate() method to the base wrapper class so the cases where we can't interpolate values (length with an "auto" type) may return false.
(WebCore::LengthPropertyWrapper::LengthPropertyWrapper): Lengths can only be interpolated if both the start and end values are not "auto".
(WebCore::LengthVariantPropertyWrapper::LengthVariantPropertyWrapper):
(WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap):
(WebCore::CSSPropertyAnimation::canPropertyBeInterpolated):
* page/animation/CSSPropertyAnimation.h:
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::createAnimatedElementUpdate): Move a check found in both updateCSSAnimationsForElement() and updateCSSTransitionsForElement() to their
shared call site.

LayoutTests:

Implementing the CSS Transitions spec for starting transitions highlighted a couple of issues with existing tests.

* TestExpectations: The test imported/mozilla/css-transitions/test_animation-ready.html now passes reliably.
* animations/transition-and-animation-3-expected.txt:
* animations/transition-and-animation-3.html: This test was mistakenly expecting a retargeted transition to pick
up from the underlying value (100px) rather than the interrupted transition's value (~0px). We update the test
to be more obvious about what it is testing and with the correct behavior per the spec.
* transitions/background-position-transitions-expected.txt:
* transitions/background-position-transitions.html: Shorthand properties are expected to yield a transitiong for
each shorthand property, so we update this test to check the background-position shorthand properties.
* transitions/resources/transition-test-helpers.js: We uncovered a crash, which is not new to this patch, that forces
us to work around using the background-position longhand properties. So we work around this by reading from the shorthand
background-position property and manually parsing the value. Fixing this crash is tracked by webkit.org/b/186766.
* transitions/transition-to-from-auto-expected.txt:
* transitions/transition-to-from-auto.html: Update the test to check that we only yield transitions when interpolating
between non-auto values, as mandated by the spec.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@232946 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/animations/transition-and-animation-3-expected.txt
LayoutTests/animations/transition-and-animation-3.html
LayoutTests/imported/mozilla/ChangeLog
LayoutTests/imported/mozilla/css-transitions/test_animation-cancel-expected.txt
LayoutTests/imported/mozilla/css-transitions/test_animation-ready-expected.txt
LayoutTests/transitions/background-position-transitions-expected.txt
LayoutTests/transitions/background-position-transitions.html
LayoutTests/transitions/resources/transition-test-helpers.js
LayoutTests/transitions/transition-to-from-auto-expected.txt
LayoutTests/transitions/transition-to-from-auto.html
Source/WebCore/ChangeLog
Source/WebCore/animation/AnimationTimeline.cpp
Source/WebCore/animation/AnimationTimeline.h
Source/WebCore/animation/CSSAnimation.cpp
Source/WebCore/animation/CSSTransition.cpp
Source/WebCore/animation/CSSTransition.h
Source/WebCore/animation/DeclarativeAnimation.cpp
Source/WebCore/animation/KeyframeEffectReadOnly.cpp
Source/WebCore/animation/KeyframeEffectReadOnly.h
Source/WebCore/animation/WebAnimation.h
Source/WebCore/page/animation/CSSPropertyAnimation.cpp
Source/WebCore/page/animation/CSSPropertyAnimation.h
Source/WebCore/style/StyleTreeResolver.cpp

index 5ce4103..4c1565e 100644 (file)
@@ -1,3 +1,28 @@
+2018-06-18  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement "Starting of transitions" section from CSS Transitions
+        https://bugs.webkit.org/show_bug.cgi?id=186517
+        <rdar://problem/41000798>
+
+        Reviewed by Dean Jackson.
+
+        Implementing the CSS Transitions spec for starting transitions highlighted a couple of issues with existing tests.
+
+        * TestExpectations: The test imported/mozilla/css-transitions/test_animation-ready.html now passes reliably.
+        * animations/transition-and-animation-3-expected.txt:
+        * animations/transition-and-animation-3.html: This test was mistakenly expecting a retargeted transition to pick
+        up from the underlying value (100px) rather than the interrupted transition's value (~0px). We update the test
+        to be more obvious about what it is testing and with the correct behavior per the spec.
+        * transitions/background-position-transitions-expected.txt:
+        * transitions/background-position-transitions.html: Shorthand properties are expected to yield a transitiong for
+        each shorthand property, so we update this test to check the background-position shorthand properties.
+        * transitions/resources/transition-test-helpers.js: We uncovered a crash, which is not new to this patch, that forces
+        us to work around using the background-position longhand properties. So we work around this by reading from the shorthand
+        background-position property and manually parsing the value. Fixing this crash is tracked by webkit.org/b/186766.
+        * transitions/transition-to-from-auto-expected.txt:
+        * transitions/transition-to-from-auto.html: Update the test to check that we only yield transitions when interpolating
+        between non-auto values, as mandated by the spec.
+
 2018-06-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         Document should not be mutated under SMILTimeContainer::updateAnimations()
index 617e197..8962af5 100644 (file)
@@ -1932,7 +1932,6 @@ webkit.org/b/183821 imported/mozilla/css-transitions/test_animation-cancel.html
 webkit.org/b/183826 imported/mozilla/css-animations/test_animation-pausing.html [ Pass Failure Timeout ]
 webkit.org/b/183828 imported/mozilla/css-animations/test_animation-playstate.html [ Pass Failure Timeout ]
 webkit.org/b/183830 imported/mozilla/css-animations/test_animation-ready.html [ Pass Failure Timeout ]
-webkit.org/b/183831 imported/mozilla/css-transitions/test_animation-ready.html [ Pass Failure Timeout ]
 webkit.org/b/183834 imported/mozilla/css-animations/test_animation-starttime.html [ Pass Failure Timeout ]
 webkit.org/b/183836 imported/mozilla/css-animations/test_animations-dynamic-changes.html [ Pass Failure Timeout ]
 webkit.org/b/183837 imported/mozilla/css-transitions/test_document-get-animations.html [ Pass Failure Timeout ]
index 32234bc..12f06dc 100644 (file)
@@ -1,4 +1,13 @@
-Once animation has finished, box should be running left transition from 100px to 200px.
+Once animation has finished, box should be running left transition from 0px to 200px.
 
-PASS - "left" property for "test" element at 0.2s saw something close to: 100
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS animations.length is 1
+PASS animations[0] instanceof CSSTransition is true
+PASS animations[0].effect.getKeyframes()[0].left is not "100px"
+PASS animations[0].effect.getKeyframes()[1].left is "200px"
+PASS successfullyParsed is true
+
+TEST COMPLETE
 
index cf73d35..6020d7c 100644 (file)
             background-color: blue;
         }
 
-        .box.animating {
-            -webkit-animation: move 0.1s linear;
+        .box.transitioning {
+            transition: left 10s linear;
             left: 100px;
-            -webkit-transition: left 10s linear;
+        }
+
+        .box.animating {
+            animation: move 0.1s linear;
         }
         
-        /* When the animation is done, the box should be running a transition from 100px to 200px */
-        .box.animating.moved {
+        .box.retargeted {
             left: 200px;
         }
         
-        @-webkit-keyframes move {
-            from {
-                left: 500px;
-            }
-            to   {
-                left: 501px;
-            }
+        @keyframes move {
+            from { left: 500px }
+            to   { left: 501px }
         }
         
-        #result {
-            margin-top: 130px;
-        }
     </style>
-    <script src="resources/animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+    <script src="../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <div id="test" class="box"></div>
     <script type="text/javascript" charset="utf-8">
 
-      const expectedValues = [
-        // [animation-name, time, element-id, property, expected-value, tolerance]
-        [null, 0.2, "test", "left", 100, 10],
-      ];
+    description("Once animation has finished, box should be running left transition from 0px to 200px.");
 
-      function animationStarted()
-      {
-          document.getElementById('test').className = 'animating moved box';
-      }
+    window.jsTestIsAsync = true;
 
-      function setupTest()
-      {
-        document.getElementById('test').className = 'animating box';
-        runAnimationTest(expectedValues, animationStarted);
-      }
-      
-      window.addEventListener('load', function() {
-          window.setTimeout(setupTest, 0);
-      }, false);
+    let animations;
+    const testElement = document.getElementById('test');
+
+    // Start the test by starting a transition from 0 to 100px.
+    window.setTimeout(() => testElement.classList.add("transitioning"));
+
+    // Then, once the transition has started, start an animation.
+    testElement.addEventListener("transitionstart", event => {
+        setTimeout(() => testElement.classList.add("animating"));
+    });
+
+    // Then wait for the animation to start and change the underlying left value.
+    testElement.addEventListener("animationstart", event => {
+        testElement.classList.add("retargeted");
+    });
+
+    // When the animation has ended, check that we're transitioning from ~0 > 200, and not 100 > 200.
+    testElement.addEventListener("animationend", event => {
+        animations = testElement.getAnimations();
+        shouldBe("animations.length", "1");
+        shouldBeTrue("animations[0] instanceof CSSTransition");
+        shouldNotBeEqualToString("animations[0].effect.getKeyframes()[0].left", "100px");
+        shouldBeEqualToString("animations[0].effect.getKeyframes()[1].left", "200px");
+        finishJSTest();
+    });
 
     </script>
-</head>
-<body>
-    <p>Once animation has finished, box should be running left transition from 100px to 200px.</p>
-    <div id="test" class="box"></div>
-    <div id="result"></div>
+    <script src="../resources/js-test-post.js"></script>
 </body>
 </html>
index 00e46f6..e860efd 100644 (file)
@@ -1,3 +1,16 @@
+2018-06-18  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement "Starting of transitions" section from CSS Transitions
+        https://bugs.webkit.org/show_bug.cgi?id=186517
+        <rdar://problem/41000798>
+
+        Reviewed by Dean Jackson.
+
+        Mark some progressions in the Mozilla CSS Animations and CSS Transitions tests.
+
+        * css-transitions/test_animation-cancel-expected.txt:
+        * css-transitions/test_animation-ready-expected.txt:
+
 2018-06-15  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] CSS Animations should take precedence over CSS Transitions
index 167e08e..003935e 100644 (file)
@@ -4,10 +4,10 @@ PASS After canceling a transition, it can still be re-used
 PASS After cancelling a finished transition, it can still be re-used 
 PASS After cancelling a transition, updating transition properties doesn't make it live again 
 PASS Setting display:none on an element cancels its transitions 
-PASS Setting display:none cancels transitions on a child element 
+FAIL Setting display:none cancels transitions on a child element assert_equals: expected "idle" but got "running"
 PASS Removing a property from transition-property cancels transitions on that property 
 PASS Setting zero combined duration 
-FAIL Changing style to another interpolable value cancels the original transition assert_equals: expected "idle" but got "finished"
-FAIL An after-change style value can't be interpolated assert_equals: There should be no transitions expected 0 but got 1
-FAIL Reversing a running transition cancels the original transition assert_equals: expected "idle" but got "running"
+PASS Changing style to another interpolable value cancels the original transition 
+PASS An after-change style value can't be interpolated 
+PASS Reversing a running transition cancels the original transition 
 
index 1724f91..fd3ad85 100644 (file)
@@ -1,5 +1,5 @@
 
 PASS A new ready promise is created each time play() is called the animation property 
 PASS ready promise is rejected when a transition is cancelled by updating transition-property 
-FAIL ready promise is rejected when a transition is cancelled by changing the transition property to something not interpolable assert_equals: Animation is idle after transition was cancelled expected "idle" but got "paused"
+PASS ready promise is rejected when a transition is cancelled by changing the transition property to something not interpolable 
 
index ac9ba48..8e213e9 100644 (file)
@@ -1,4 +1,7 @@
-PASS - "background-position" property for "box1" element at 0.5s saw something close to: 45,60
-PASS - "background-position" property for "box2" element at 0.5s saw something close to: 45,60
-PASS - "background-position" property for "box3" element at 0.5s saw something close to: 10,0.5,100,80,0,0.5,20,0.5,100,100,0,0.5
+PASS - "background-position-x" property for "box1" element at 0.5s saw something close to: 45px
+PASS - "background-position-y" property for "box1" element at 0.5s saw something close to: 60px
+PASS - "background-position-x" property for "box2" element at 0.5s saw something close to: 45px
+PASS - "background-position-y" property for "box2" element at 0.5s saw something close to: 60px
+PASS - "background-position-x" property for "box3" element at 0.5s saw something close to: calc((10px * 0.5) + ((100% - 80px) * 0.5))
+PASS - "background-position-y" property for "box3" element at 0.5s saw something close to: calc((20px * 0.5) + ((100% - 100px) * 0.5))
 
index 2bbd06a..2563277 100644 (file)
     <script>
         const expectedValues = [
         // [time, element-id, property, expected-value, tolerance]
-        [0.5, 'box1', 'background-position', [45, 60], 2],
-        [0.5, 'box2', 'background-position', [45, 60], 2],
-        [0.5, 'box3', 'background-position', [10,0.5,100,80,0,0.5,20,0.5,100,100,0,0.5], 2], // Numbers extracted from a calc() expression.
+        [0.5, 'box1', 'background-position-x', "45px"],
+        [0.5, 'box1', 'background-position-y', "60px"],
+        [0.5, 'box2', 'background-position-x', "45px"],
+        [0.5, 'box2', 'background-position-y', "60px"],
+        [0.5, 'box3', 'background-position-x', "calc((10px * 0.5) + ((100% - 80px) * 0.5))"],
+        [0.5, 'box3', 'background-position-y', "calc((20px * 0.5) + ((100% - 100px) * 0.5))"],
         ];
 
         function setupTest()
index ed39e25..ebda795 100644 (file)
@@ -213,6 +213,30 @@ function checkExpectedValue(expected, index)
                     break;
             }
         }
+    } else if (property == "background-position-x" || property == "background-position-y") {
+        computedValue = window.getComputedStyle(document.getElementById(elementId)).backgroundPosition;
+
+        const leftCharIndex = computedValue.indexOf("left");
+        const rightCharIndex = computedValue.indexOf("right");
+        const topCharIndex = computedValue.indexOf("top");
+        const bottomCharIndex = computedValue.indexOf("bottom");
+
+        let firstCharIndex, lastCharIndex;
+        if (property == "background-position-x") {
+            if (computedValue.startsWith("left"))
+                firstCharIndex = 4;
+            else if (computedValue.startsWith("right"))
+                firstCharIndex = 5;
+            lastCharIndex = Math.max(topCharIndex, bottomCharIndex);
+        } else {
+            if (topCharIndex > -1)
+                firstCharIndex = topCharIndex + 3;
+            else
+                firstCharIndex = bottomCharIndex + 6;
+            lastCharIndex = computedValue.length;
+        }
+
+        pass = computedValue.substring(firstCharIndex, lastCharIndex).trim() == expectedValue;
     } else if (property == "fill" || property == "stroke") {
         computedValue = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property).rgbColor;
         if (compareRGB([computedValue.red.cssText, computedValue.green.cssText, computedValue.blue.cssText], expectedValue, tolerance))
index 529dcfa..2902d01 100644 (file)
@@ -1,4 +1,3 @@
-PASS - "left" property for "test1" element at 1s saw something close to: 0
-PASS - "left" property for "test2" element at 1s saw something close to: 100
+Total number of animations: 1 
 PASS - "left" property for "test3" element at 1s saw something close to: 50
 
index 9610323..3eaaa16 100644 (file)
 
     const expectedValues = [
       // [time, element-id, property, expected-value, tolerance]
-      [1, 'test1', 'left', '0', 2],
-      [1, 'test2', 'left', '100', 2],
       [1, 'test3', 'left', '50', 2],
     ];
   
     function setupTest()
     {
         document.body.classList.add('final');
+        result += `Total number of animations: ${document.getAnimations().length} <br>`;
     }
     
     runTransitionTest(expectedValues, setupTest, usePauseAPI);
index 7c5d676..9c30134 100644 (file)
@@ -1,3 +1,65 @@
+2018-06-18  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement "Starting of transitions" section from CSS Transitions
+        https://bugs.webkit.org/show_bug.cgi?id=186517
+        <rdar://problem/41000798>
+
+        Reviewed by Dean Jackson.
+
+        We implement the whole section at https://drafts.csswg.org/css-transitions-1/#starting so that we have spec-compliant starting of CSS Transitions.
+        To correctly implement this we now maintain two maps of transitions for a given property, a set of running transitions (m_elementToRunningCSSTransitionByCSSPropertyID)
+        and a set of completed transition (m_elementToCompletedCSSTransitionByCSSPropertyID).
+
+        * animation/AnimationTimeline.cpp:
+        (WebCore::removeCSSTransitionFromMap): Add a utility to remove a CSSTransition from one of the two HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>>
+        managed by AnimationTimeline (m_elementToRunningCSSTransitionByCSSPropertyID and m_elementToCompletedCSSTransitionByCSSPropertyID).
+        (WebCore::AnimationTimeline::animationWasRemovedFromElement): Use the new removeCSSTransitionFromMap() utility.
+        (WebCore::AnimationTimeline::updateCSSAnimationsForElement): We rename "oldStyle" to "currentStyle" to align with the naming used by updateCSSAnimationsForElement().
+        We also remove an initial check that has been pushed up to Style::TreeResolver::createAnimatedElementUpdate().
+        (WebCore::propertyInStyleMatchesValueForTransitionInMap): When running the steps mandated by the spec we often need to check whether a given property has a transition
+        in an AnimationList that matches the value used in a given RenderStyle.
+        (WebCore::transitionCombinedDuration): We need to compute the combined duration twice while running the steps mandated by the spec, so we have a dedicated utility.
+        (WebCore::transitionMatchesProperty): New utility that indicates whether a WebCore::Animation matches a given property, by virtue of targeting it directly, targeting
+        a shorthand for which this property is a longhand, or targeting "all".
+        (WebCore::AnimationTimeline::updateCSSTransitionsForElement): This is where all of the new implementation for starting transitions happens. We implement the steps
+        as they appear in the spec.
+        (WebCore::shouldBackingAnimationBeConsideredForCSSTransition): Deleted.
+        * animation/AnimationTimeline.h:
+        * animation/CSSAnimation.cpp:
+        (WebCore::CSSAnimation::syncPropertiesWithBackingAnimation): Since only CSS Animations respect the delay and duration values as parsed directly in a WebCore:Animation
+        object, we move code that was previously in DeclarativeAnimation::syncPropertiesWithBackingAnimation to this method. CSS Transitions set those values based on the
+        delay and duration parameters passed to CSSTransition::create() and computed in AnimationTimeline::updateCSSTransitionsForElement().
+        * animation/CSSTransition.cpp:
+        (WebCore::CSSTransition::create): Since the transition's delay and duration is computed in AnimationTimeline::updateCSSTransitionsForElement(), we now require them
+        to be passed when creating a CSSTransition and pass those to the new setTimingProperties() function. We also expect a target style and reversing-adjusted start style,
+        as well as a reversing-shortening factor.
+        (WebCore::CSSTransition::CSSTransition): 
+        (WebCore::CSSTransition::resolve): We need to be able to query the last style computed by a call to resolve() from AnimationTimeline::updateCSSTransitionsForElement(),
+        we subclass this newly-virtual WebAnimation method and clone the style after we blended the property targeted by this transition.
+        (WebCore::CSSTransition::setTimingProperties): Set the transition delay and duration as provided in the call to create().
+        (WebCore::CSSTransition::canBeListed const): Fix a crash that was found while working on this bug where we could access a null effect.
+        (WebCore::CSSTransition::initialize): Deleted.
+        (WebCore::CSSTransition::matchesBackingAnimationAndStyles const): Deleted.
+        * animation/CSSTransition.h:
+        * animation/DeclarativeAnimation.cpp:
+        (WebCore::DeclarativeAnimation::syncPropertiesWithBackingAnimation): This virtual method now has an empty definition since the timing properties are now only set
+        for CSSAnimation.
+        * animation/KeyframeEffectReadOnly.cpp:
+        (WebCore::KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes const): Deleted.
+        * animation/KeyframeEffectReadOnly.h:
+        * animation/WebAnimation.h:
+        * page/animation/CSSPropertyAnimation.cpp:
+        (WebCore::AnimationPropertyWrapperBase::canInterpolate const): Since we need to be able to determine whether some property values can be interpolated, we add a new
+        canInterpolate() method to the base wrapper class so the cases where we can't interpolate values (length with an "auto" type) may return false.
+        (WebCore::LengthPropertyWrapper::LengthPropertyWrapper): Lengths can only be interpolated if both the start and end values are not "auto".
+        (WebCore::LengthVariantPropertyWrapper::LengthVariantPropertyWrapper):
+        (WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap):
+        (WebCore::CSSPropertyAnimation::canPropertyBeInterpolated):
+        * page/animation/CSSPropertyAnimation.h:
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::createAnimatedElementUpdate): Move a check found in both updateCSSAnimationsForElement() and updateCSSTransitionsForElement() to their
+        shared call site.
+
 2018-06-18  Nan Wang  <n_wang@apple.com>
 
         AX: [macOS] When zoom is enabled, focus doesn't follow text cursor
index 7ed02ce..0c0e10d 100644 (file)
@@ -38,6 +38,7 @@
 #include "KeyframeEffectReadOnly.h"
 #include "RenderStyle.h"
 #include "RenderView.h"
+#include "StylePropertyShorthand.h"
 #include "WebAnimationUtilities.h"
 #include <wtf/text/TextStream.h>
 #include <wtf/text/WTFString.h>
@@ -95,10 +96,24 @@ void AnimationTimeline::animationWasAddedToElement(WebAnimation& animation, Elem
     }).iterator->value.add(&animation);
 }
 
+static inline bool removeCSSTransitionFromMap(CSSTransition& transition, Element& element, HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>>& map)
+{
+    auto iterator = map.find(&element);
+    if (iterator == map.end())
+        return false;
+
+    auto& cssTransitionsByProperty = iterator->value;
+    cssTransitionsByProperty.remove(transition.property());
+    if (cssTransitionsByProperty.isEmpty())
+        map.remove(&element);
+    return true;
+}
+
 void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation, Element& element)
 {
-    // First, we clear this animation from one of the m_elementToCSSAnimationsMap, m_elementToCSSTransitionsMap
-    // or m_elementToAnimationsMap map, whichever is relevant to this type of animation.
+    // First, we clear this animation from one of the m_elementToCSSAnimationsMap, m_elementToCSSTransitionsMap,
+    // m_elementToAnimationsMap or m_elementToCompletedCSSTransitionByCSSPropertyID map, whichever is relevant to
+    // this type of animation.
     auto& map = relevantMapForAnimation(animation);
     auto iterator = map.find(&element);
     if (iterator == map.end())
@@ -110,7 +125,7 @@ void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation,
         map.remove(iterator);
 
     // Now, if we're dealing with a declarative animation, we remove it from either the m_elementToCSSAnimationByName
-    // or the m_elementToCSSTransitionByCSSPropertyID map, whichever is relevant to this type of animation.
+    // or the m_elementToRunningCSSTransitionByCSSPropertyID map, whichever is relevant to this type of animation.
     if (is<CSSAnimation>(animation)) {
         auto iterator = m_elementToCSSAnimationByName.find(&element);
         if (iterator != m_elementToCSSAnimationByName.end()) {
@@ -121,14 +136,9 @@ void AnimationTimeline::animationWasRemovedFromElement(WebAnimation& animation,
                 m_elementToCSSAnimationByName.remove(&element);
         }
     } else if (is<CSSTransition>(animation)) {
-        auto iterator = m_elementToCSSTransitionByCSSPropertyID.find(&element);
-        if (iterator != m_elementToCSSTransitionByCSSPropertyID.end()) {
-            auto& cssTransitionsByProperty = iterator->value;
-            auto property = downcast<CSSTransition>(animation).property();
-            cssTransitionsByProperty.remove(property);
-            if (cssTransitionsByProperty.isEmpty())
-                m_elementToCSSTransitionByCSSPropertyID.remove(&element);
-        }
+        auto& transition = downcast<CSSTransition>(animation);
+        if (!removeCSSTransitionFromMap(transition, element, m_elementToRunningCSSTransitionByCSSPropertyID))
+            removeCSSTransitionFromMap(transition, element, m_elementToCompletedCSSTransitionByCSSPropertyID);
     }
 }
 
@@ -158,16 +168,10 @@ void AnimationTimeline::removeAnimationsForElement(Element& element)
     }
 }
 
-void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const RenderStyle& newStyle, const RenderStyle* oldStyle)
+void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const RenderStyle* currentStyle, const RenderStyle& afterChangeStyle)
 {
-    if (element.document().pageCacheState() != Document::NotInPageCache)
-        return;
-
-    if (element.document().renderView()->printing())
-        return;
-
     // In case this element is newly getting a "display: none" we need to cancel all of its animations and disregard new ones.
-    if (oldStyle && oldStyle->hasAnimations() && oldStyle->display() != DisplayType::None && newStyle.display() == DisplayType::None) {
+    if (currentStyle && currentStyle->hasAnimations() && currentStyle->display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
         if (m_elementToCSSAnimationByName.contains(&element)) {
             for (const auto& cssAnimationsByNameMapItem : m_elementToCSSAnimationByName.take(&element))
                 cancelOrRemoveDeclarativeAnimation(cssAnimationsByNameMapItem.value);
@@ -175,13 +179,13 @@ void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const Re
         return;
     }
 
-    if (oldStyle && oldStyle->hasAnimations() && newStyle.hasAnimations() && *(oldStyle->animations()) == *(newStyle.animations()))
+    if (currentStyle && currentStyle->hasAnimations() && afterChangeStyle.hasAnimations() && *(currentStyle->animations()) == *(afterChangeStyle.animations()))
         return;
 
     // First, compile the list of animation names that were applied to this element up to this point.
     HashSet<String> namesOfPreviousAnimations;
-    if (oldStyle && oldStyle->hasAnimations()) {
-        auto* previousAnimations = oldStyle->animations();
+    if (currentStyle && currentStyle->hasAnimations()) {
+        auto* previousAnimations = currentStyle->animations();
         for (size_t i = 0; i < previousAnimations->size(); ++i) {
             auto& previousAnimation = previousAnimations->animation(i);
             if (previousAnimation.isValidAnimation())
@@ -194,7 +198,7 @@ void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const Re
         return HashMap<String, RefPtr<CSSAnimation>> { };
     }).iterator->value;
 
-    if (auto* currentAnimations = newStyle.animations()) {
+    if (auto* currentAnimations = afterChangeStyle.animations()) {
         for (size_t i = 0; i < currentAnimations->size(); ++i) {
             auto& currentAnimation = currentAnimations->animation(i);
             auto& name = currentAnimation.name();
@@ -205,7 +209,7 @@ void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const Re
                 cssAnimationsByName.get(name)->setBackingAnimation(currentAnimation);
             } else if (currentAnimation.isValidAnimation()) {
                 // Otherwise we are dealing with a new animation name and must create a CSSAnimation for it.
-                cssAnimationsByName.set(name, CSSAnimation::create(element, currentAnimation, oldStyle, newStyle));
+                cssAnimationsByName.set(name, CSSAnimation::create(element, currentAnimation, currentStyle, afterChangeStyle));
             }
             // Remove the name of this animation from our list since it's now known to be current.
             namesOfPreviousAnimations.remove(name);
@@ -233,109 +237,183 @@ RefPtr<WebAnimation> AnimationTimeline::cssAnimationForElementAndProperty(Elemen
     return matchingAnimation;
 }
 
-static bool shouldBackingAnimationBeConsideredForCSSTransition(const Animation& backingAnimation)
+static bool propertyInStyleMatchesValueForTransitionInMap(CSSPropertyID property, const RenderStyle& style, HashMap<CSSPropertyID, RefPtr<CSSTransition>>& transitions)
 {
-    auto mode = backingAnimation.animationMode();
+    if (auto* transition = transitions.get(property)) {
+        if (CSSPropertyAnimation::propertiesEqual(property, &style, &transition->targetStyle()))
+            return true;
+    }
+    return false;
+}
+
+static double transitionCombinedDuration(const Animation* transition)
+{
+    return std::max(0.0, transition->duration()) + transition->delay();
+}
+
+static bool transitionMatchesProperty(const Animation& transition, CSSPropertyID property)
+{
+    auto mode = transition.animationMode();
     if (mode == Animation::AnimateNone || mode == Animation::AnimateUnknownProperty)
         return false;
-    if (mode == Animation::AnimateSingleProperty && backingAnimation.property() == CSSPropertyInvalid)
-        return false;
+    if (mode == Animation::AnimateSingleProperty) {
+        auto transitionProperty = transition.property();
+        if (transitionProperty != property) {
+            auto shorthand = shorthandForProperty(transitionProperty);
+            for (size_t i = 0; i < shorthand.length(); ++i) {
+                if (shorthand.properties()[i] == property)
+                    return true;
+            }
+            return false;
+        }
+    }
     return true;
 }
 
-void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& newStyle, const RenderStyle* oldStyle)
+void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle)
 {
-    if (element.document().pageCacheState() != Document::NotInPageCache)
-        return;
-
-    if (element.document().renderView()->printing())
-        return;
-
-    // In case this element is newly getting a "display: none" we need to cancel all of its animations and disregard new ones.
-    if (oldStyle && oldStyle->hasTransitions() && oldStyle->display() != DisplayType::None && newStyle.display() == DisplayType::None) {
-        if (m_elementToCSSTransitionByCSSPropertyID.contains(&element)) {
-            for (const auto& cssTransitionsByCSSPropertyIDMapItem : m_elementToCSSTransitionByCSSPropertyID.take(&element))
+    // In case this element is newly getting a "display: none" we need to cancel all of its transitions and disregard new ones.
+    if (currentStyle.hasTransitions() && currentStyle.display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
+        if (m_elementToRunningCSSTransitionByCSSPropertyID.contains(&element)) {
+            for (const auto& cssTransitionsByCSSPropertyIDMapItem : m_elementToRunningCSSTransitionByCSSPropertyID.take(&element))
                 cancelOrRemoveDeclarativeAnimation(cssTransitionsByCSSPropertyIDMapItem.value);
         }
         return;
     }
 
-    // Create or get the CSSTransitions by CSS property name map for this element.
-    auto& cssTransitionsByProperty = m_elementToCSSTransitionByCSSPropertyID.ensure(&element, [] {
+    // Section 3 "Starting of transitions" from the CSS Transitions Level 1 specification.
+    // https://drafts.csswg.org/css-transitions-1/#starting
+
+    auto& runningTransitionsByProperty = m_elementToRunningCSSTransitionByCSSPropertyID.ensure(&element, [] {
         return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { };
     }).iterator->value;
 
-    // First, compile the list of backing animations and properties that were applied to this element up to this point.
-    auto previousProperties = copyToVector(cssTransitionsByProperty.keys());
-    HashSet<const Animation*> previousBackingAnimations;
-    if (oldStyle && oldStyle->hasTransitions()) {
-        auto* previousTransitions = oldStyle->transitions();
-        for (size_t i = 0; i < previousTransitions->size(); ++i) {
-            auto& backingAnimation = previousTransitions->animation(i);
-            if (shouldBackingAnimationBeConsideredForCSSTransition(backingAnimation))
-                previousBackingAnimations.add(&backingAnimation);
-        }
-    }
+    auto& completedTransitionsByProperty = m_elementToCompletedCSSTransitionByCSSPropertyID.ensure(&element, [] {
+        return HashMap<CSSPropertyID, RefPtr<CSSTransition>> { };
+    }).iterator->value;
 
-    if (auto* currentTransitions = newStyle.transitions()) {
-        for (size_t i = 0; i < currentTransitions->size(); ++i) {
-            auto& backingAnimation = currentTransitions->animation(i);
-            if (!shouldBackingAnimationBeConsideredForCSSTransition(backingAnimation))
-                continue;
-            auto property = backingAnimation.property();
-            bool transitionsAllProperties = backingAnimation.animationMode() == Animation::AnimateAll;
-            auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
-            // In the "transition-property: all" case, where the animation's mode is set to AnimateAll,
-            // the property will be set to CSSPropertyInvalid and we need to iterate over all known
-            // CSS properties and see if they have mis-matching values in the old and new styles, which
-            // means they should have a CSSTransition created for them.
-            // We implement a single loop which handles the "all" case and the specified property case
-            // by using the pre-set property above in the specified property case and breaking out of
-            // the loop after the first complete iteration.
-            for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
-                if (transitionsAllProperties) {
-                    bool isShorthand;
-                    property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
-                    if (isShorthand)
-                        continue;
-                } else if (propertyIndex) {
-                    // We only go once through this loop if we are transitioning a single property.
-                    break;
-                }
-
-                previousProperties.removeFirst(property);
-                // We've found a backing animation that we didn't know about for a valid property.
-                if (!previousBackingAnimations.contains(&backingAnimation)) {
-                    // If we already had a CSSTransition for this property, check whether its timing properties match the current backing
-                    // animation's properties and whether its blending keyframes match the old and new styles. If they do, move on to the
-                    // next transition, otherwise delete the previous CSSTransition object, and create a new one.
-                    if (cssTransitionsByProperty.contains(property)) {
-                        if (cssTransitionsByProperty.get(property)->matchesBackingAnimationAndStyles(backingAnimation, oldStyle, newStyle))
-                            continue;
-                        removeAnimation(cssTransitionsByProperty.take(property).releaseNonNull());
-                    }
-                    // Now we can create a new CSSTransition with the new backing animation provided it has a valid
-                    // duration and the from and to values are distinct.
-                    if ((backingAnimation.duration() || backingAnimation.delay() > 0) && oldStyle) {
-                        auto existingAnimation = cssAnimationForElementAndProperty(element, property);
-                        const auto* fromStyle = existingAnimation ? &downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : oldStyle;
-                        if (!CSSPropertyAnimation::propertiesEqual(property, fromStyle, &newStyle))
-                            cssTransitionsByProperty.set(property, CSSTransition::create(element, property, backingAnimation, fromStyle, newStyle));
-                    }
-                }
+    auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
+    for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
+        bool isShorthand;
+        auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
+        if (isShorthand)
+            continue;
+
+        const Animation* matchingBackingAnimation = nullptr;
+        if (auto* transitions = afterChangeStyle.transitions()) {
+            for (size_t i = 0; i < transitions->size(); ++i) {
+                auto& backingAnimation = transitions->animation(i);
+                if (transitionMatchesProperty(backingAnimation, property))
+                    matchingBackingAnimation = &backingAnimation;
             }
         }
-    }
 
-    // Remaining properties are no longer current and must be removed.
-    for (const auto transitionPropertyToRemove : previousProperties) {
-        if (cssTransitionsByProperty.contains(transitionPropertyToRemove))
-            cancelOrRemoveDeclarativeAnimation(cssTransitionsByProperty.take(transitionPropertyToRemove));
+        // https://drafts.csswg.org/css-transitions-1/#before-change-style
+        // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with
+        // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time.
+        auto existingAnimation = cssAnimationForElementAndProperty(element, property);
+        const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : currentStyle;
+
+        if (!runningTransitionsByProperty.contains(property)
+            && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle)
+            && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle)
+            && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)
+            && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) {
+            // 1. If all of the following are true:
+            //   - the element does not have a running transition for the property,
+            //   - the before-change style is different from and can be interpolated with the after-change style for that property,
+            //   - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property,
+            //   - there is a matching transition-property value, and
+            //   - the combined duration is greater than 0s,
+
+            // then implementations must remove the completed transition (if present) from the set of completed transitions
+            completedTransitionsByProperty.remove(property);
+
+            // and start a transition whose:
+            //   - start time is the time of the style change event plus the matching transition delay,
+            //   - end time is the start time plus the matching transition duration,
+            //   - start value is the value of the transitioning property in the before-change style,
+            //   - end value is the value of the transitioning property in the after-change style,
+            //   - reversing-adjusted start value is the same as the start value, and
+            //   - reversing shortening factor is 1.
+            auto delay = Seconds(matchingBackingAnimation->delay());
+            auto duration = Seconds(matchingBackingAnimation->duration());
+            auto& reversingAdjustedStartStyle = beforeChangeStyle;
+            auto reversingShorteningFactor = 1;
+            runningTransitionsByProperty.set(property, CSSTransition::create(element, property, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
+        } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) {
+            // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from
+            //    the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions.
+            completedTransitionsByProperty.remove(property);
+        }
+
+        bool hasRunningTransition = runningTransitionsByProperty.contains(property);
+        if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) {
+            // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property
+            //    value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions.
+            if (hasRunningTransition)
+                runningTransitionsByProperty.take(property)->cancel();
+            else
+                completedTransitionsByProperty.remove(property);
+        }
+
+        if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) {
+            auto previouslyRunningTransition = runningTransitionsByProperty.take(property);
+            auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle();
+            // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running
+            //    transition is not equal to the value of the property in the after-change style, then:
+            if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &currentStyle, &afterChangeStyle)) {
+                // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style,
+                //    or if these two values cannot be interpolated, then implementations must cancel the running transition.
+                previouslyRunningTransition->cancel();
+            } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) {
+                // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition
+                //    cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition.
+                previouslyRunningTransition->cancel();
+            } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) {
+                // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change
+                //    style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition
+                previouslyRunningTransition->cancel();
+
+                // and start a new transition whose:
+                //   - reversing-adjusted start value is the end value of the running transition,
+                //   - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of:
+                //       1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition
+                //       2. 1 minus the reversing shortening factor of the old transition.
+                //   - start time is the time of the style change event plus:
+                //       1. if the matching transition delay is nonnegative, the matching transition delay, or
+                //       2. if the matching transition delay is negative, the product of the new transition’s reversing shortening factor and the matching transition delay,
+                //   - end time is the start time plus the product of the matching transition duration and the new transition’s reversing shortening factor,
+                //   - start value is the current value of the property in the running transition,
+                //   - end value is the value of the property in the after-change style
+                auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle();
+                double transformedProgress = 1;
+                if (auto* effect = previouslyRunningTransition->effect())
+                    transformedProgress = effect->iterationProgress().value_or(transformedProgress);
+                auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0);
+                auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay());
+                auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor;
+                runningTransitionsByProperty.set(property, CSSTransition::create(element, property, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
+            } else {
+                // 4. Otherwise, implementations must cancel the running transition
+                previouslyRunningTransition->cancel();
+
+                // and start a new transition whose:
+                //   - start time is the time of the style change event plus the matching transition delay,
+                //   - end time is the start time plus the matching transition duration,
+                //   - start value is the current value of the property in the running transition,
+                //   - end value is the value of the property in the after-change style,
+                //   - reversing-adjusted start value is the same as the start value, and
+                //   - reversing shortening factor is 1.
+                auto delay = Seconds(matchingBackingAnimation->delay());
+                auto duration = Seconds(matchingBackingAnimation->duration());
+                auto& reversingAdjustedStartStyle = currentStyle;
+                auto reversingShorteningFactor = 1;
+                runningTransitionsByProperty.set(property, CSSTransition::create(element, property, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
+            }
+        }
     }
 
-    // Remove the map of CSSTransitions by property for this element if it's now empty.
-    if (cssTransitionsByProperty.isEmpty())
-        m_elementToCSSTransitionByCSSPropertyID.remove(&element);
 }
 
 void AnimationTimeline::cancelOrRemoveDeclarativeAnimation(RefPtr<DeclarativeAnimation> animation)
index 3680cb0..fe9a7c6 100644 (file)
@@ -63,8 +63,8 @@ public:
     void animationWasAddedToElement(WebAnimation&, Element&);
     void animationWasRemovedFromElement(WebAnimation&, Element&);
 
-    void updateCSSAnimationsForElement(Element&, const RenderStyle& newStyle, const RenderStyle* oldStyle);
-    void updateCSSTransitionsForElement(Element&, const RenderStyle& newStyle, const RenderStyle* oldStyle);
+    void updateCSSAnimationsForElement(Element&, const RenderStyle* currentStyle, const RenderStyle& afterChangeStyle);
+    void updateCSSTransitionsForElement(Element&, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle);
 
     virtual ~AnimationTimeline();
 
@@ -96,7 +96,8 @@ private:
     ListHashSet<RefPtr<WebAnimation>> m_animations;
 
     HashMap<Element*, HashMap<String, RefPtr<CSSAnimation>>> m_elementToCSSAnimationByName;
-    HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>> m_elementToCSSTransitionByCSSPropertyID;
+    HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>> m_elementToRunningCSSTransitionByCSSPropertyID;
+    HashMap<Element*, HashMap<CSSPropertyID, RefPtr<CSSTransition>>> m_elementToCompletedCSSTransitionByCSSPropertyID;
 };
 
 } // namespace WebCore
index 2f8cdc4..1b65f51 100644 (file)
@@ -87,10 +87,13 @@ void CSSAnimation::syncPropertiesWithBackingAnimation()
     auto iterationCount = animation.iterationCount();
     timing->setIterations(iterationCount == Animation::IterationCountInfinite ? std::numeric_limits<double>::infinity() : iterationCount);
 
+    timing->setDelay(Seconds(animation.delay()));
+    timing->setIterationDuration(Seconds(animation.duration()));
+
     // Synchronize the play state
-    if (backingAnimation().playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused)
+    if (animation.playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused)
         play();
-    else if (backingAnimation().playState() == AnimationPlayState::Paused && playState() == WebAnimation::PlayState::Running)
+    else if (animation.playState() == AnimationPlayState::Paused && playState() == WebAnimation::PlayState::Running)
         pause();
 
     unsuspendEffectInvalidation();
index d93e6ff..30f2262 100644 (file)
 
 namespace WebCore {
 
-Ref<CSSTransition> CSSTransition::create(Element& target, CSSPropertyID property, const Animation& backingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle)
+Ref<CSSTransition> CSSTransition::create(Element& target, CSSPropertyID property, const Animation& backingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle, Seconds delay, Seconds duration, const RenderStyle& reversingAdjustedStartStyle, double reversingShorteningFactor)
 {
-    auto result = adoptRef(*new CSSTransition(target, property, backingAnimation));
+    auto result = adoptRef(*new CSSTransition(target, property, backingAnimation, newStyle, reversingAdjustedStartStyle, reversingShorteningFactor));
     result->initialize(target, oldStyle, newStyle);
+    result->setTimingProperties(delay, duration);
     return result;
 }
 
-CSSTransition::CSSTransition(Element& element, CSSPropertyID property, const Animation& backingAnimation)
+CSSTransition::CSSTransition(Element& element, CSSPropertyID property, const Animation& backingAnimation, const RenderStyle& targetStyle, const RenderStyle& reversingAdjustedStartStyle, double reversingShorteningFactor)
     : DeclarativeAnimation(element, backingAnimation)
     , m_property(property)
+    , m_targetStyle(RenderStyle::clonePtr(targetStyle))
+    , m_reversingAdjustedStartStyle(RenderStyle::clonePtr(reversingAdjustedStartStyle))
+    , m_reversingShorteningFactor(reversingShorteningFactor)
 {
 }
 
-void CSSTransition::initialize(const Element& target, const RenderStyle* oldStyle, const RenderStyle& newStyle)
+void CSSTransition::resolve(RenderStyle& targetStyle)
 {
-    DeclarativeAnimation::initialize(target, oldStyle, newStyle);
+    DeclarativeAnimation::resolve(targetStyle);
+    m_currentStyle = RenderStyle::clonePtr(targetStyle);
+}
 
+void CSSTransition::setTimingProperties(Seconds delay, Seconds duration)
+{
     suspendEffectInvalidation();
 
+    auto* timing = effect()->timing();
     // In order for CSS Transitions to be seeked backwards, they need to have their fill mode set to backwards
     // such that the original CSS value applied prior to the transition is used for a negative current time.
-    effect()->timing()->setFill(FillMode::Backwards);
+    
+    timing->setFill(FillMode::Backwards);
+    timing->setDelay(delay);
+    timing->setIterationDuration(duration);
 
     unsuspendEffectInvalidation();
 }
 
-bool CSSTransition::matchesBackingAnimationAndStyles(const Animation& newBackingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle) const
-{
-    // See if the animations match, excluding the property since we can move from an "all" transition to an explicit property transition.
-    bool backingAnimationsMatch = backingAnimation().animationsMatch(newBackingAnimation, false);
-    if (!oldStyle || !effect())
-        return backingAnimationsMatch;
-    return backingAnimationsMatch && !downcast<KeyframeEffectReadOnly>(effect())->stylesWouldYieldNewCSSTransitionsBlendingKeyframes(*oldStyle, newStyle);
-}
-
 bool CSSTransition::canBeListed() const
 {
-    if (!downcast<KeyframeEffectReadOnly>(effect())->hasBlendingKeyframes())
-        return false;
+    if (auto* transitionEffect = effect()) {
+        if (is<KeyframeEffectReadOnly>(transitionEffect)) {
+            if (!downcast<KeyframeEffectReadOnly>(effect())->hasBlendingKeyframes())
+                return false;
+        }
+    }
     return WebAnimation::canBeListed();
 }
 
index 50dda8b..a933313 100644 (file)
@@ -37,23 +37,29 @@ class RenderStyle;
 
 class CSSTransition final : public DeclarativeAnimation {
 public:
-    static Ref<CSSTransition> create(Element&, CSSPropertyID, const Animation&, const RenderStyle* oldStyle, const RenderStyle& newStyle);
+    static Ref<CSSTransition> create(Element&, CSSPropertyID, const Animation&, const RenderStyle* oldStyle, const RenderStyle& newStyle, Seconds delay, Seconds duration, const RenderStyle& reversingAdjustedStartStyle, double);
     ~CSSTransition() = default;
 
     bool isCSSTransition() const override { return true; }
     String transitionProperty() const { return getPropertyNameString(m_property); }
     CSSPropertyID property() const { return m_property; }
+    const RenderStyle& targetStyle() const { return *m_targetStyle; }
+    const RenderStyle& currentStyle() const { return *m_currentStyle; }
+    const RenderStyle& reversingAdjustedStartStyle() const { return *m_reversingAdjustedStartStyle; }
+    double reversingShorteningFactor() const { return m_reversingShorteningFactor; }
 
-    bool matchesBackingAnimationAndStyles(const Animation&, const RenderStyle* oldStyle, const RenderStyle& newStyle) const;
     bool canBeListed() const final;
-
-protected:
-    void initialize(const Element&, const RenderStyle* oldStyle, const RenderStyle& newStyle) final;
+    void resolve(RenderStyle&) final;
 
 private:
-    CSSTransition(Element&, CSSPropertyID, const Animation&);
+    CSSTransition(Element&, CSSPropertyID, const Animation&, const RenderStyle& targetStyle, const RenderStyle& reversingAdjustedStartStyle, double);
+    void setTimingProperties(Seconds delay, Seconds duration);
 
     CSSPropertyID m_property;
+    std::unique_ptr<RenderStyle> m_targetStyle;
+    std::unique_ptr<RenderStyle> m_currentStyle;
+    std::unique_ptr<RenderStyle> m_reversingAdjustedStartStyle;
+    double m_reversingShorteningFactor;
 
 };
 
index 646b0eb..b944a2a 100644 (file)
@@ -82,13 +82,6 @@ void DeclarativeAnimation::initialize(const Element& target, const RenderStyle*
 
 void DeclarativeAnimation::syncPropertiesWithBackingAnimation()
 {
-    suspendEffectInvalidation();
-
-    auto* timing = effect()->timing();
-    timing->setDelay(Seconds(m_backingAnimation->delay()));
-    timing->setIterationDuration(Seconds(m_backingAnimation->duration()));
-
-    unsuspendEffectInvalidation();
 }
 
 void DeclarativeAnimation::setTimeline(RefPtr<AnimationTimeline>&& newTimeline)
index 8ba7b6c..9639695 100644 (file)
@@ -877,25 +877,6 @@ void KeyframeEffectReadOnly::computeCSSTransitionBlendingKeyframes(const RenderS
     setBlendingKeyframes(keyframeList);
 }
 
-bool KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes(const RenderStyle& oldStyle, const RenderStyle& newStyle) const
-{
-    ASSERT(is<CSSTransition>(animation()));
-
-    // We don't yet have blending keyframes to compare with, so these wouldn't be new keyframes, but the fisrt ones.
-    if (!hasBlendingKeyframes())
-        return false;
-
-    auto property = downcast<CSSTransition>(animation())->property();
-
-    // There cannot be new keyframes if the start and to values are the same.
-    if (CSSPropertyAnimation::propertiesEqual(property, &oldStyle, &newStyle))
-        return false;
-
-    // Otherwise, we would create new blending keyframes provided the current end keyframe holds a different
-    // value than the new end style for this property.
-    return !CSSPropertyAnimation::propertiesEqual(property, m_blendingKeyframes[1].style(), &newStyle);
-}
-
 void KeyframeEffectReadOnly::computedNeedsForcedLayout()
 {
     m_needsForcedLayout = false;
index 8acba01..17c3964 100644 (file)
@@ -114,7 +114,6 @@ public:
     bool colorFilterFunctionListsMatch() const override { return m_colorFilterFunctionListsMatch; }
 
     void computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
-    bool stylesWouldYieldNewCSSTransitionsBlendingKeyframes(const RenderStyle& oldStyle, const RenderStyle& newStyle) const;
     bool hasBlendingKeyframes() const { return m_blendingKeyframes.size(); }
     const HashSet<CSSPropertyID>& animatedProperties() const { return m_blendingKeyframes.properties(); }
 
index baa42f2..cb8db1d 100644 (file)
@@ -99,7 +99,7 @@ public:
     ExceptionOr<void> reverse();
 
     Seconds timeToNextRequiredTick() const;
-    void resolve(RenderStyle&);
+    virtual void resolve(RenderStyle&);
     void effectTargetDidChange(Element* previousTarget, Element* newTarget);
     void acceleratedStateDidChange();
     void applyPendingAcceleratedActions();
index 3fb9fe6..b3f0c5a 100644 (file)
@@ -426,6 +426,7 @@ public:
 
     virtual bool isShorthandWrapper() const { return false; }
     virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0;
+    virtual bool canInterpolate(const RenderStyle*, const RenderStyle*) const { return true; }
     virtual void blend(const CSSPropertyBlendingClient*, RenderStyle*, const RenderStyle*, const RenderStyle*, double) const = 0;
     
 #if !LOG_DISABLED
@@ -513,11 +514,34 @@ protected:
     void (RenderStyle::*m_setter)(RefPtr<T>&&);
 };
 
+class LengthPropertyWrapper : public PropertyWrapperGetter<const Length&> {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    LengthPropertyWrapper(CSSPropertyID prop, const Length& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(Length&&))
+        : PropertyWrapperGetter<const Length&>(prop, getter)
+        , m_setter(setter)
+    {
+    }
+
+    bool canInterpolate(const RenderStyle* a, const RenderStyle* b) const override
+    {
+        return !(a->*PropertyWrapperGetter<const Length&>::m_getter)().isAuto() && !(b->*PropertyWrapperGetter<const Length&>::m_getter)().isAuto();
+    }
+
+    void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
+    {
+        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const Length&>::m_getter)(), (b->*PropertyWrapperGetter<const Length&>::m_getter)(), progress));
+    }
+
+protected:
+    void (RenderStyle::*m_setter)(Length&&);
+};
+
 template <typename T>
-class LengthPropertyWrapper : public PropertyWrapperGetter<const T&> {
+class LengthVariantPropertyWrapper : public PropertyWrapperGetter<const T&> {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&))
+    LengthVariantPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&))
         : PropertyWrapperGetter<const T&>(prop, getter)
         , m_setter(setter)
     {
@@ -1469,18 +1493,18 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
 {
     // build the list of property wrappers to do the comparisons and blends
     AnimationPropertyWrapperBase* animatableLonghandPropertyWrappers[] = {
-        new LengthPropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft),
-        new LengthPropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight),
-        new LengthPropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop),
-        new LengthPropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom),
+        new LengthPropertyWrapper(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft),
+        new LengthPropertyWrapper(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight),
+        new LengthPropertyWrapper(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop),
+        new LengthPropertyWrapper(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom),
 
-        new LengthPropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth),
-        new LengthPropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth),
-        new LengthPropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth),
+        new LengthPropertyWrapper(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth),
+        new LengthPropertyWrapper(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth),
+        new LengthPropertyWrapper(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth),
 
-        new LengthPropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight),
-        new LengthPropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight),
-        new LengthPropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight),
+        new LengthPropertyWrapper(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight),
+        new LengthPropertyWrapper(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight),
+        new LengthPropertyWrapper(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight),
 
         new PropertyWrapperFlex(),
 
@@ -1488,14 +1512,14 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
         new PropertyWrapper<float>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth),
         new PropertyWrapper<float>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth),
         new PropertyWrapper<float>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth),
-        new LengthPropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft),
-        new LengthPropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight),
-        new LengthPropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop),
-        new LengthPropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom),
-        new LengthPropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft),
-        new LengthPropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight),
-        new LengthPropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop),
-        new LengthPropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom),
+        new LengthPropertyWrapper(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft),
+        new LengthPropertyWrapper(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight),
+        new LengthPropertyWrapper(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop),
+        new LengthPropertyWrapper(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom),
+        new LengthPropertyWrapper(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft),
+        new LengthPropertyWrapper(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight),
+        new LengthPropertyWrapper(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop),
+        new LengthPropertyWrapper(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom),
 
         new PropertyWrapperVisitedAffectedColor(CSSPropertyCaretColor, &RenderStyle::caretColor, &RenderStyle::setCaretColor, &RenderStyle::visitedLinkCaretColor, &RenderStyle::setVisitedLinkCaretColor),
 
@@ -1508,9 +1532,9 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
         new StyleImagePropertyWrapper(CSSPropertyWebkitMaskImage, &RenderStyle::maskImage, &RenderStyle::setMaskImage),
 
         new StyleImagePropertyWrapper(CSSPropertyBorderImageSource, &RenderStyle::borderImageSource, &RenderStyle::setBorderImageSource),
-        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageSlice, &RenderStyle::borderImageSlices, &RenderStyle::setBorderImageSlices),
-        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageWidth, &RenderStyle::borderImageWidth, &RenderStyle::setBorderImageWidth),
-        new LengthPropertyWrapper<LengthBox>(CSSPropertyBorderImageOutset, &RenderStyle::borderImageOutset, &RenderStyle::setBorderImageOutset),
+        new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageSlice, &RenderStyle::borderImageSlices, &RenderStyle::setBorderImageSlices),
+        new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageWidth, &RenderStyle::borderImageWidth, &RenderStyle::setBorderImageWidth),
+        new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyBorderImageOutset, &RenderStyle::borderImageOutset, &RenderStyle::setBorderImageOutset),
 
         new StyleImagePropertyWrapper(CSSPropertyWebkitMaskBoxImageSource, &RenderStyle::maskBoxImageSource, &RenderStyle::setMaskBoxImageSource),
         new PropertyWrapper<const NinePieceImage&>(CSSPropertyWebkitMaskBoxImage, &RenderStyle::maskBoxImage, &RenderStyle::setMaskBoxImage),
@@ -1526,8 +1550,8 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
 
         new PropertyWrapper<float>(CSSPropertyFontSize, &RenderStyle::computedFontSize, &RenderStyle::setFontSize),
         new PropertyWrapper<unsigned short>(CSSPropertyColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth),
-        new LengthPropertyWrapper<GapLength>(CSSPropertyColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap),
-        new LengthPropertyWrapper<GapLength>(CSSPropertyRowGap, &RenderStyle::rowGap, &RenderStyle::setRowGap),
+        new LengthVariantPropertyWrapper<GapLength>(CSSPropertyColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap),
+        new LengthVariantPropertyWrapper<GapLength>(CSSPropertyRowGap, &RenderStyle::rowGap, &RenderStyle::setRowGap),
         new PropertyWrapper<unsigned short>(CSSPropertyColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount),
         new PropertyWrapper<float>(CSSPropertyColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth),
         new PropertyWrapper<float>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing),
@@ -1535,27 +1559,27 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
         new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex),
         new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans),
         new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows),
-        new LengthPropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight),
+        new LengthPropertyWrapper(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight),
         new PropertyWrapper<float>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset),
         new PropertyWrapper<float>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth),
         new PropertyWrapper<float>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing),
-        new LengthPropertyWrapper<Length>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing),
-        new LengthPropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent),
+        new LengthPropertyWrapper(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing),
+        new LengthPropertyWrapper(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent),
 
         new PropertyWrapper<float>(CSSPropertyPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective),
-        new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX),
-        new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY),
-        new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX),
-        new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY),
+        new LengthPropertyWrapper(CSSPropertyPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX),
+        new LengthPropertyWrapper(CSSPropertyPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY),
+        new LengthPropertyWrapper(CSSPropertyTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX),
+        new LengthPropertyWrapper(CSSPropertyTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY),
         new PropertyWrapper<float>(CSSPropertyTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ),
-        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius),
-        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius),
-        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius),
-        new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius),
+        new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius),
+        new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius),
+        new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius),
+        new LengthVariantPropertyWrapper<LengthSize>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius),
         new PropertyWrapper<Visibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility),
         new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoomWithoutReturnValue),
 
-        new LengthPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip),
+        new LengthVariantPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip),
 
         new PropertyWrapperAcceleratedOpacity(),
         new PropertyWrapperAcceleratedTransform(),
@@ -1569,7 +1593,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
         new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath),
 
         new PropertyWrapperShape(CSSPropertyShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside),
-        new LengthPropertyWrapper<Length>(CSSPropertyShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin),
+        new LengthPropertyWrapper(CSSPropertyShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin),
         new PropertyWrapper<float>(CSSPropertyShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold),
 
         new PropertyWrapperVisitedAffectedColor(CSSPropertyColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor),
@@ -1593,15 +1617,15 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap()
         new PropertyWrapper<Vector<SVGLengthValue>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray),
         new PropertyWrapper<float>(CSSPropertyStrokeMiterlimit, &RenderStyle::strokeMiterLimit, &RenderStyle::setStrokeMiterLimit),
 
-        new LengthPropertyWrapper<Length>(CSSPropertyCx, &RenderStyle::cx, &RenderStyle::setCx),
-        new LengthPropertyWrapper<Length>(CSSPropertyCy, &RenderStyle::cy, &RenderStyle::setCy),
-        new LengthPropertyWrapper<Length>(CSSPropertyR, &RenderStyle::r, &RenderStyle::setR),
-        new LengthPropertyWrapper<Length>(CSSPropertyRx, &RenderStyle::rx, &RenderStyle::setRx),
-        new LengthPropertyWrapper<Length>(CSSPropertyRy, &RenderStyle::ry, &RenderStyle::setRy),
-        new LengthPropertyWrapper<Length>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset),
-        new LengthPropertyWrapper<Length>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth),
-        new LengthPropertyWrapper<Length>(CSSPropertyX, &RenderStyle::x, &RenderStyle::setX),
-        new LengthPropertyWrapper<Length>(CSSPropertyY, &RenderStyle::y, &RenderStyle::setY),
+        new LengthPropertyWrapper(CSSPropertyCx, &RenderStyle::cx, &RenderStyle::setCx),
+        new LengthPropertyWrapper(CSSPropertyCy, &RenderStyle::cy, &RenderStyle::setCy),
+        new LengthPropertyWrapper(CSSPropertyR, &RenderStyle::r, &RenderStyle::setR),
+        new LengthPropertyWrapper(CSSPropertyRx, &RenderStyle::rx, &RenderStyle::setRx),
+        new LengthPropertyWrapper(CSSPropertyRy, &RenderStyle::ry, &RenderStyle::setRy),
+        new LengthPropertyWrapper(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset),
+        new LengthPropertyWrapper(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth),
+        new LengthPropertyWrapper(CSSPropertyX, &RenderStyle::x, &RenderStyle::setX),
+        new LengthPropertyWrapper(CSSPropertyY, &RenderStyle::y, &RenderStyle::setY),
 
         new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity),
         new PropertyWrapperMaybeInvalidColor(CSSPropertyFloodColor, &RenderStyle::floodColor, &RenderStyle::setFloodColor),
@@ -1758,6 +1782,14 @@ bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle
     return true;
 }
 
+bool CSSPropertyAnimation::canPropertyBeInterpolated(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b)
+{
+    AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop);
+    if (wrapper)
+        return wrapper->canInterpolate(a, b);
+    return false;
+}
+
 CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand)
 {
     CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton();
index e9fc7ff..d3bd9c4 100644 (file)
@@ -41,6 +41,7 @@ public:
     static bool isPropertyAnimatable(CSSPropertyID);
     static bool animationOfPropertyIsAccelerated(CSSPropertyID);
     static bool propertiesEqual(CSSPropertyID, const RenderStyle* a, const RenderStyle* b);
+    static bool canPropertyBeInterpolated(CSSPropertyID, const RenderStyle* a, const RenderStyle* b);
     static CSSPropertyID getPropertyAtIndex(int, bool& isShorthand);
     static int getNumProperties();
 
index 49e4d45..e54ce52 100644 (file)
@@ -292,11 +292,13 @@ ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderSt
         // First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation
         // on the document timeline. Note that we get timeline() on the Document here because we need a timeline created
         // in case no Web Animations have been created through the JS API.
-        if ((oldStyle && oldStyle->hasTransitions()) || newStyle->hasTransitions())
-            m_document.timeline().updateCSSTransitionsForElement(element, *newStyle, oldStyle);
+        if (element.document().pageCacheState() == Document::NotInPageCache && !element.document().renderView()->printing()) {
+            if (oldStyle && (oldStyle->hasTransitions() || newStyle->hasTransitions()))
+                m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle);
 
-        if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
-            m_document.timeline().updateCSSAnimationsForElement(element, *newStyle, oldStyle);
+            if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
+                m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle);
+        }
     }
 
     if (auto timeline = m_document.existingTimeline()) {