[Web Animations] Implement basic to-from animations
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Nov 2017 21:35:38 +0000 (21:35 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Nov 2017 21:35:38 +0000 (21:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179707
<rdar://problem/34932456>

Source/WebCore:

We can now actually perform an animation, in software only, when provided two keyframes on an AnimationEffect.
To parse a keyframes object from JS, we use the StyleResolver to create RenderStyle objects based on the strings
provided for the property names and values. Then, when the DocumentTimeline indicates that animations are ready
to be updated, we invalidate the style of elements with animations, so that during style resolution we can perform
blending between the RenderStyles for each keyframe.

Reviewed by Dean Jackson.

* animation/AnimationEffect.h:
(WebCore::AnimationEffect::~AnimationEffect): Deleted.
* animation/AnimationTimeline.h:
(WebCore::AnimationTimeline::elementToAnimationsMap const):
* animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::animationResolutionTimerFired):
(WebCore::DocumentTimeline::updateAnimations): Renamed from resolveAnimations() since we're not
actually resolving animations, merely invalidating styles in preparation for resolution.
(WebCore::DocumentTimeline::resolveAnimations): Deleted.
* animation/DocumentTimeline.h:
* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::create): Pass the keyframes argument to setKeyframes() and forward the
exception in case one was raised in the setter.
(WebCore::KeyframeEffect::setKeyframes): Pass the keyframes argument to processKeyframes() and
forward the exception in case one was raised during processing.
(WebCore::KeyframeEffect::processKeyframes): Deal with exactly two keyframes, set at 0 and 1 offsets,
specified in array forms for the keyframes object. As we parse the content of the provided JS object,
we create RenderStyle objects using the element's StyleResolver from a CSS text string we create
based on the property and values as strings.
(WebCore::KeyframeEffect::applyAtLocalTime): Compute the progress based on the local time and duration,
using the existing CSSPropertyAnimation::blendProperties() mechanics to perform the blend between the
from and to keyframes.
* animation/KeyframeEffect.h:
* animation/KeyframeEffect.idl:
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::resolve):
* animation/WebAnimation.h:
* bindings/IDLTypes.h:
(WebCore::IDLObject::nullValue): Make JSC::Strong an optional type.
* dom/Document.h:
(WebCore::Document::existingTimeline const): Provide an explicit method for call sites to check existence
of a timeline before forcing one to be created if missing by calling timeline().
* dom/Element.cpp:
(WebCore::Element::getAnimations): Do not force the creation of a timeline if one isn't already created.
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::createAnimatedElementUpdate): When applying styles, account for any Web
Animation applied to the provided element.

LayoutTests:

Reviewed by Dean Jackson.

Update existing tests to explicitly pass null for keyframes and update WPT expectations.

* http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt:
* http/wpt/web-animations/interfaces/KeyframeEffect/constructor-expected.txt:
* http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001-expected.txt:
* http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-002-expected.txt:
* http/wpt/web-animations/interfaces/KeyframeEffect/setKeyframes-expected.txt:
* http/wpt/wk-web-animations/interfaces/element-get-animations.html:
* http/wpt/wk-web-animations/timing-model/animation-creation-basic.html:
* http/wpt/wk-web-animations/timing-model/animation-effect-unique-relationship.html:
* http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html:
* http/wpt/wk-web-animations/timing-model/keyframe-effect-expected.txt:
* http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html:
* http/wpt/wk-web-animations/timing-model/keyframe-effect.html:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/KeyframeEffect/constructor-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-002-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/KeyframeEffect/setKeyframes-expected.txt
LayoutTests/http/wpt/wk-web-animations/interfaces/element-get-animations.html
LayoutTests/http/wpt/wk-web-animations/timing-model/animation-creation-basic.html
LayoutTests/http/wpt/wk-web-animations/timing-model/animation-effect-unique-relationship.html
LayoutTests/http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html
LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect-expected.txt
LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html
LayoutTests/http/wpt/wk-web-animations/timing-model/keyframe-effect.html
Source/WebCore/ChangeLog
Source/WebCore/animation/AnimationEffect.h
Source/WebCore/animation/AnimationTimeline.h
Source/WebCore/animation/DocumentTimeline.cpp
Source/WebCore/animation/DocumentTimeline.h
Source/WebCore/animation/KeyframeEffect.cpp
Source/WebCore/animation/KeyframeEffect.h
Source/WebCore/animation/KeyframeEffect.idl
Source/WebCore/animation/WebAnimation.cpp
Source/WebCore/animation/WebAnimation.h
Source/WebCore/bindings/IDLTypes.h
Source/WebCore/dom/Document.h
Source/WebCore/dom/Element.cpp
Source/WebCore/style/StyleTreeResolver.cpp

index 8929065..d669200 100644 (file)
@@ -1,3 +1,26 @@
+2017-11-15  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement basic to-from animations
+        https://bugs.webkit.org/show_bug.cgi?id=179707
+        <rdar://problem/34932456>
+
+        Reviewed by Dean Jackson.
+
+        Update existing tests to explicitly pass null for keyframes and update WPT expectations.
+
+        * http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline-expected.txt:
+        * http/wpt/web-animations/interfaces/KeyframeEffect/constructor-expected.txt:
+        * http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001-expected.txt:
+        * http/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-002-expected.txt:
+        * http/wpt/web-animations/interfaces/KeyframeEffect/setKeyframes-expected.txt:
+        * http/wpt/wk-web-animations/interfaces/element-get-animations.html:
+        * http/wpt/wk-web-animations/timing-model/animation-creation-basic.html:
+        * http/wpt/wk-web-animations/timing-model/animation-effect-unique-relationship.html:
+        * http/wpt/wk-web-animations/timing-model/animation-interface-effect-property.html:
+        * http/wpt/wk-web-animations/timing-model/keyframe-effect-expected.txt:
+        * http/wpt/wk-web-animations/timing-model/keyframe-effect-interface-timing-duration.html:
+        * http/wpt/wk-web-animations/timing-model/keyframe-effect.html:
+
 2017-11-15  Frederic Wang  <fwang@igalia.com>
 
         ASSERTION FAILED: !renderer->needsLayout() in WebCore::RenderBlock::checkPositionedObjectsNeedLayout with MathML
index f18dfab..8820dec 100644 (file)
@@ -1,6 +1,6 @@
 
 PASS document.timeline identity tests 
-FAIL document.timeline.currentTime value tests assert_equals: document.timeline.currentTime matches requestAnimationFrame time expected 552.3000000000001 but got 0.5538000000000001
+FAIL document.timeline.currentTime value tests assert_equals: document.timeline.currentTime matches requestAnimationFrame time expected 852.9 but got 0.8540000000000001
 PASS document.timeline.currentTime liveness tests 
 PASS iframe document.timeline.currentTime liveness tests 
 PASS document.timeline.currentTime time should be the same for all RAF callbacks in a frame 
index 3e0f3f3..57b6fee 100644 (file)
@@ -199,5 +199,5 @@ FAIL a KeyframeEffectReadOnly constructed with null target Can't find variable:
 PASS KeyframeEffect constructor creates an AnimationEffectTiming timing object 
 FAIL KeyframeEffect constructor propagates exceptions generated by accessing the options object assert_throws: function "function () {
     new KeyframeEffect(target, { get left() { throw test_error }})
-  }" did not throw
+  }" threw object "TypeError: Type error" ("TypeError") expected object "[object Object]" ("test")
 
index d13e544..c0bb772 100644 (file)
@@ -55,5 +55,5 @@ FAIL Only enumerable properties on keyframes are read effect.getKeyframes is not
 FAIL Only properties defined directly on keyframes are read effect.getKeyframes is not a function. (In 'effect.getKeyframes()', 'effect.getKeyframes' is undefined)
 FAIL Only enumerable properties on property-indexed keyframes are read effect.getKeyframes is not a function. (In 'effect.getKeyframes()', 'effect.getKeyframes' is undefined)
 FAIL Only properties defined directly on property-indexed keyframes are read effect.getKeyframes is not a function. (In 'effect.getKeyframes()', 'effect.getKeyframes' is undefined)
-FAIL Properties are read in ascending order by Unicode codepoint assert_array_equals: property access order lengths differ, expected 5 got 0
+FAIL Properties are read in ascending order by Unicode codepoint assert_array_equals: property access order property 0, expected "composite" but got "marginLeft"
 
index d8a16cb..30b3933 100644 (file)
@@ -1,15 +1,9 @@
 
-FAIL easing values are parsed correctly when set on a property-indexed keyframe effect.getKeyframes is not a function. (In 'effect.getKeyframes()', 'effect.getKeyframes' is undefined)
+FAIL easing values are parsed correctly when set on a property-indexed keyframe Type error
 FAIL easing values are parsed correctly when using a keyframe sequence effect.getKeyframes is not a function. (In 'effect.getKeyframes()', 'effect.getKeyframes' is undefined)
-FAIL Invalid easing values are correctly rejected when set on a property-indexed keyframe assert_throws: TypeError is thrown for easing '' function "() => {
-      new KeyframeEffect(target, { easing: invalidEasing });
-    }" did not throw
-FAIL Invalid easing values are correctly rejected when using a keyframe sequence assert_throws: TypeError is thrown for easing '' function "() => {
-      new KeyframeEffect(target, [{ easing: invalidEasing }]);
-    }" did not throw
-FAIL Errors from invalid easings on a property-indexed keyframe are thrown after reading all properties assert_throws: function "() => {
-    new KeyframeEffect(target, keyframe);
-  }" did not throw
+PASS Invalid easing values are correctly rejected when set on a property-indexed keyframe 
+PASS Invalid easing values are correctly rejected when using a keyframe sequence 
+FAIL Errors from invalid easings on a property-indexed keyframe are thrown after reading all properties assert_equals: All properties were read before throwing the easing error expected 2 but got 0
 FAIL Errors from invalid easings on a keyframe sequence are thrown after reading all properties assert_throws: function "() => {
     new KeyframeEffect(target, [ kf1, kf2 ]);
   }" did not throw
index 0b36792..2f0f7f3 100644 (file)
@@ -1,49 +1,49 @@
 
-FAIL Keyframes can be replaced with an empty keyframe effect.setKeyframes is not a function. (In 'effect.setKeyframes(frame)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one shorthand property two value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property two value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property property-indexed keyframes specification with different numbers of values effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a property-indexed keyframes specification with an invalid value effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification that needs to stringify its values effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference in a shorthand property effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property one value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property one non-array value property-indexed keyframes specification effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification where the first value is invalid effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification where the second value is invalid effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property one keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property two keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one shorthand property two keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property (a shorthand and one of its component longhands) two keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with duplicate values for a given interior offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with duplicate values for offsets 0 and 1 effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property four keyframe sequence effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a single keyframe sequence with omitted offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a single keyframe sequence with null offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a single keyframe sequence with string offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property keyframe sequence with some omitted offsets effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property keyframe sequence with some null offsets effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property keyframe sequence with some omitted offsets effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property keyframe sequence with all omitted offsets effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with different easing values, but the same easing value for a given offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with different composite values, but the same composite value for a given offset effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a one property two keyframe sequence that needs to stringify its values effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with a CSS variable reference effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with a CSS variable reference in a shorthand property effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence where shorthand precedes longhand effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence where longhand precedes shorthand effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence where lesser shorthand precedes greater shorthand effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence where greater shorthand precedes lesser shorthand effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property keyframe sequence where one property is missing from the first keyframe effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a two property keyframe sequence where one property is missing from the last keyframe effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-FAIL Keyframes can be replaced with a keyframe sequence with repeated values at offset 1 with different easings effect.setKeyframes is not a function. (In 'effect.setKeyframes(subtest.input)', 'effect.setKeyframes' is undefined)
-PASS KeyframeEffect constructor throws with keyframes with an out-of-bounded positive offset 
-PASS KeyframeEffect constructor throws with keyframes with an out-of-bounded negative offset 
-PASS KeyframeEffect constructor throws with keyframes not loosely sorted by offset 
-PASS KeyframeEffect constructor throws with property-indexed keyframes with an invalid easing value 
-PASS KeyframeEffect constructor throws with a keyframe sequence with an invalid easing value 
-PASS KeyframeEffect constructor throws with keyframes with an invalid composite value 
+FAIL Keyframes can be replaced with an empty keyframe Type error
+FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a one shorthand property two value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a two property two value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a two property property-indexed keyframes specification with different numbers of values Type error
+FAIL Keyframes can be replaced with a property-indexed keyframes specification with an invalid value Type error
+FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification that needs to stringify its values Type error
+FAIL Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference Type error
+FAIL Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference in a shorthand property Type error
+FAIL Keyframes can be replaced with a one property one value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a one property one non-array value property-indexed keyframes specification Type error
+FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification where the first value is invalid Type error
+FAIL Keyframes can be replaced with a one property two value property-indexed keyframes specification where the second value is invalid Type error
+FAIL Keyframes can be replaced with a one property one keyframe sequence Type error
+FAIL Keyframes can be replaced with a one property two keyframe sequence Type error
+FAIL Keyframes can be replaced with a two property two keyframe sequence Type error
+FAIL Keyframes can be replaced with a one shorthand property two keyframe sequence Type error
+FAIL Keyframes can be replaced with a two property (a shorthand and one of its component longhands) two keyframe sequence Type error
+FAIL Keyframes can be replaced with a keyframe sequence with duplicate values for a given interior offset Type error
+FAIL Keyframes can be replaced with a keyframe sequence with duplicate values for offsets 0 and 1 Type error
+FAIL Keyframes can be replaced with a two property four keyframe sequence Type error
+FAIL Keyframes can be replaced with a single keyframe sequence with omitted offset Type error
+FAIL Keyframes can be replaced with a single keyframe sequence with null offset Type error
+FAIL Keyframes can be replaced with a single keyframe sequence with string offset Type error
+FAIL Keyframes can be replaced with a one property keyframe sequence with some omitted offsets Type error
+FAIL Keyframes can be replaced with a one property keyframe sequence with some null offsets Type error
+FAIL Keyframes can be replaced with a two property keyframe sequence with some omitted offsets Type error
+FAIL Keyframes can be replaced with a one property keyframe sequence with all omitted offsets Type error
+FAIL Keyframes can be replaced with a keyframe sequence with different easing values, but the same easing value for a given offset Type error
+FAIL Keyframes can be replaced with a keyframe sequence with different composite values, but the same composite value for a given offset Type error
+FAIL Keyframes can be replaced with a one property two keyframe sequence that needs to stringify its values Type error
+FAIL Keyframes can be replaced with a keyframe sequence with a CSS variable reference Type error
+FAIL Keyframes can be replaced with a keyframe sequence with a CSS variable reference in a shorthand property Type error
+FAIL Keyframes can be replaced with a keyframe sequence where shorthand precedes longhand Type error
+FAIL Keyframes can be replaced with a keyframe sequence where longhand precedes shorthand Type error
+FAIL Keyframes can be replaced with a keyframe sequence where lesser shorthand precedes greater shorthand Type error
+FAIL Keyframes can be replaced with a keyframe sequence where greater shorthand precedes lesser shorthand Type error
+FAIL Keyframes can be replaced with a two property keyframe sequence where one property is missing from the first keyframe Type error
+FAIL Keyframes can be replaced with a two property keyframe sequence where one property is missing from the last keyframe Type error
+FAIL Keyframes can be replaced with a keyframe sequence with repeated values at offset 1 with different easings Type error
+FAIL KeyframeEffect constructor throws with keyframes with an out-of-bounded positive offset Type error
+FAIL KeyframeEffect constructor throws with keyframes with an out-of-bounded negative offset Type error
+FAIL KeyframeEffect constructor throws with keyframes not loosely sorted by offset Type error
+FAIL KeyframeEffect constructor throws with property-indexed keyframes with an invalid easing value Type error
+FAIL KeyframeEffect constructor throws with a keyframe sequence with an invalid easing value Type error
+FAIL KeyframeEffect constructor throws with keyframes with an invalid composite value Type error
 
index b5d3e6d..4c01ddd 100644 (file)
@@ -13,9 +13,9 @@ let a, b, c;
 const targetA = document.createElement("a");
 const targetB = document.createElement("b");
 
-const effectAa = new KeyframeEffect(targetA);
-const effectAb = new KeyframeEffect(targetA);
-const effectB = new KeyframeEffect(targetB);
+const effectAa = new KeyframeEffect(targetA, null);
+const effectAb = new KeyframeEffect(targetA, null);
+const effectB = new KeyframeEffect(targetB, null);
 
 test(t => {
     assert_true(typeof targetA.getAnimations === "function");
index 55f5ea0..ef7a45d 100644 (file)
@@ -33,7 +33,7 @@ test(t => {
 }, "Creating Animation with a null argument should have a null effect and the document timeline.");
 
 test(t => {
-    const effect = new KeyframeEffect(document.body);
+    const effect = new KeyframeEffect(document.body, null);
     const animation = new Animation(effect);
     assert_equals(animation.effect, effect);
     assert_equals(animation.timeline, document.timeline);
index eb57140..eb10463 100644 (file)
@@ -9,8 +9,8 @@
 'use strict';
 
 test(t => {
-    const effectA = new KeyframeEffect(document.createElement("div"));
-    const effectB = new KeyframeEffect(document.createElement("div"));
+    const effectA = new KeyframeEffect(document.createElement("div"), null);
+    const effectB = new KeyframeEffect(document.createElement("div"), null);
 
     const a = new Animation(effectA);
     const b = new Animation(effectB);
index d4a3873..498c911 100644 (file)
@@ -17,24 +17,24 @@ test(t => {
 
 test(t => {
     const animation = new Animation;
-    const effect = new KeyframeEffect(document.body);
+    const effect = new KeyframeEffect(document.body, null);
     animation.effect = effect;
     assert_equals(animation.effect, effect);
 }, "The animation.effect property can be set to a KeyframeEffect object.");
 
 test(t => {
     const animation = new Animation;
-    const effectA = new KeyframeEffect(document.body);
+    const effectA = new KeyframeEffect(document.body, null);
     animation.effect = effectA;
     assert_equals(animation.effect, effectA);
-    const effectB = new KeyframeEffect(document.body);
+    const effectB = new KeyframeEffect(document.body, null);
     animation.effect = effectB;
     assert_equals(animation.effect, effectB);
 }, "The animation.effect property can be set to another KeyframeEffect object.");
 
 test(t => {
     const animation = new Animation;
-    const effect = new KeyframeEffect(document.body);
+    const effect = new KeyframeEffect(document.body, null);
     animation.effect = effect;
     assert_equals(animation.effect, effect);
     animation.effect = null;
index c6ccd9e..c7caa30 100644 (file)
@@ -1,7 +1,7 @@
 
 PASS The KeyframeEffect interface is defined. 
-PASS The KeyframeEffect interface cannot be constructed without parameters. 
-PASS The KeyframeEffect interface constructor throws if the parameter has the wrong type. 
+PASS The KeyframeEffect interface cannot be constructed without enough parameters. 
+PASS The KeyframeEffect interface constructor throws if the first parameter has the wrong type. 
 PASS The KeyframeEffect interface target is set to the provided Element. 
 PASS A KeyframeEffect object target is read-only. 
 
index 88b6845..32b6a12 100644 (file)
 internals.pauseTimeline(document.timeline);
 
 test(t => {
-    const keyframeEffect = new KeyframeEffect(document.body);
+    const keyframeEffect = new KeyframeEffect(document.body, null);
     assert_inherits(keyframeEffect, "timing");
     assert_inherits(keyframeEffect.timing, "duration");
     assert_equals(keyframeEffect.timing.duration, 0);
 }, "Check the default state of the timing and duration properties.");
 
 test(t => {
-    const keyframeEffect = new KeyframeEffect(document.body);
+    const keyframeEffect = new KeyframeEffect(document.body, null);
     keyframeEffect.timing.duration = 2;
     assert_equals(keyframeEffect.timing.duration, 2);
 }, "Check that we can update the timing duration.");
index ce0c6f7..a9746bd 100644 (file)
@@ -16,22 +16,23 @@ test(t => {
 
 test(t => {
     assert_throws(new TypeError, () => new KeyframeEffect);
-}, "The KeyframeEffect interface cannot be constructed without parameters.");
+    assert_throws(new TypeError, () => new KeyframeEffect(document.createElement("div")));
+}, "The KeyframeEffect interface cannot be constructed without enough parameters.");
 
 test(t => {
-    assert_throws(new TypeError, () => new KeyframeEffect(0));
-    assert_throws(new TypeError, () => new KeyframeEffect({}));
-    assert_throws(new TypeError, () => new KeyframeEffect([]));
-    assert_throws(new TypeError, () => new KeyframeEffect(false));
-    assert_throws(new TypeError, () => new KeyframeEffect('element'));
-}, "The KeyframeEffect interface constructor throws if the parameter has the wrong type.");
+    assert_throws(new TypeError, () => new KeyframeEffect(0, null));
+    assert_throws(new TypeError, () => new KeyframeEffect({}, null));
+    assert_throws(new TypeError, () => new KeyframeEffect([], null));
+    assert_throws(new TypeError, () => new KeyframeEffect(false, null));
+    assert_throws(new TypeError, () => new KeyframeEffect('element', null));
+}, "The KeyframeEffect interface constructor throws if the first parameter has the wrong type.");
 
 test(t => {
-    assert_equals((new KeyframeEffect(document.body)).target, document.body);
+    assert_equals((new KeyframeEffect(document.body, null)).target, document.body);
 }, "The KeyframeEffect interface target is set to the provided Element.");
 
 test(t => {
-    const effect = new KeyframeEffect(document.body);
+    const effect = new KeyframeEffect(document.body, null);
     assert_readonly(effect, "target");
 }, "A KeyframeEffect object target is read-only.");
 
index a713bf7..4e9dcda 100644 (file)
@@ -1,3 +1,55 @@
+2017-11-15  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement basic to-from animations
+        https://bugs.webkit.org/show_bug.cgi?id=179707
+        <rdar://problem/34932456>
+
+        We can now actually perform an animation, in software only, when provided two keyframes on an AnimationEffect.
+        To parse a keyframes object from JS, we use the StyleResolver to create RenderStyle objects based on the strings
+        provided for the property names and values. Then, when the DocumentTimeline indicates that animations are ready
+        to be updated, we invalidate the style of elements with animations, so that during style resolution we can perform
+        blending between the RenderStyles for each keyframe.
+
+        Reviewed by Dean Jackson.
+
+        * animation/AnimationEffect.h:
+        (WebCore::AnimationEffect::~AnimationEffect): Deleted.
+        * animation/AnimationTimeline.h:
+        (WebCore::AnimationTimeline::elementToAnimationsMap const):
+        * animation/DocumentTimeline.cpp:
+        (WebCore::DocumentTimeline::animationResolutionTimerFired):
+        (WebCore::DocumentTimeline::updateAnimations): Renamed from resolveAnimations() since we're not
+        actually resolving animations, merely invalidating styles in preparation for resolution.
+        (WebCore::DocumentTimeline::resolveAnimations): Deleted.
+        * animation/DocumentTimeline.h:
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::create): Pass the keyframes argument to setKeyframes() and forward the
+        exception in case one was raised in the setter.
+        (WebCore::KeyframeEffect::setKeyframes): Pass the keyframes argument to processKeyframes() and
+        forward the exception in case one was raised during processing.
+        (WebCore::KeyframeEffect::processKeyframes): Deal with exactly two keyframes, set at 0 and 1 offsets,
+        specified in array forms for the keyframes object. As we parse the content of the provided JS object,
+        we create RenderStyle objects using the element's StyleResolver from a CSS text string we create
+        based on the property and values as strings.
+        (WebCore::KeyframeEffect::applyAtLocalTime): Compute the progress based on the local time and duration,
+        using the existing CSSPropertyAnimation::blendProperties() mechanics to perform the blend between the
+        from and to keyframes.
+        * animation/KeyframeEffect.h:
+        * animation/KeyframeEffect.idl:
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::resolve):
+        * animation/WebAnimation.h:
+        * bindings/IDLTypes.h:
+        (WebCore::IDLObject::nullValue): Make JSC::Strong an optional type.
+        * dom/Document.h:
+        (WebCore::Document::existingTimeline const): Provide an explicit method for call sites to check existence
+        of a timeline before forcing one to be created if missing by calling timeline().
+        * dom/Element.cpp:
+        (WebCore::Element::getAnimations): Do not force the creation of a timeline if one isn't already created.
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::createAnimatedElementUpdate): When applying styles, account for any Web
+        Animation applied to the provided element.
+
 2017-11-15  Frederic Wang  <fwang@igalia.com>
 
         ASSERTION FAILED: !renderer->needsLayout() in WebCore::RenderBlock::checkPositionedObjectsNeedLayout with MathML
index e456479..13a2db5 100644 (file)
@@ -36,10 +36,11 @@ namespace WebCore {
 
 class AnimationEffect : public RefCounted<AnimationEffect> {
 public:
+    virtual ~AnimationEffect() = default;
+
     bool isKeyframeEffect() const { return m_classType == KeyframeEffectClass; }
     AnimationEffectTiming* timing() const { return m_timing.get(); }
-
-    virtual ~AnimationEffect() { }
+    virtual void applyAtLocalTime(Seconds, RenderStyle&) = 0;
 
     WebAnimation* animation() const { return m_animation.get(); }
     void setAnimation(RefPtr<WebAnimation>&& animation) { m_animation = animation; }
index 74da5db..841ee5c 100644 (file)
@@ -70,6 +70,8 @@ protected:
 
     explicit AnimationTimeline(ClassType);
 
+    const HashMap<RefPtr<Element>, Vector<RefPtr<WebAnimation>>>& elementToAnimationsMap() { return m_elementToAnimationsMap; }
+
 private:
     ClassType m_classType;
     std::optional<Seconds> m_currentTime;
index 85c11e0..b0fd6d7 100644 (file)
@@ -154,11 +154,17 @@ void DocumentTimeline::displayRefreshFired()
 void DocumentTimeline::animationResolutionTimerFired()
 #endif
 {
-    resolveAnimations();
+    updateAnimations();
 }
 
-void DocumentTimeline::resolveAnimations()
+void DocumentTimeline::updateAnimations()
 {
+    if (m_document && !elementToAnimationsMap().isEmpty()) {
+        for (const auto& elementToAnimationsMapItem : elementToAnimationsMap())
+            elementToAnimationsMapItem.key->invalidateStyleAndLayerComposition();
+        m_document->updateStyleIfNeeded();
+    }
+
     // Time has advanced, the timing model requires invalidation now.
     animationTimingModelDidChange();
 }
index 6f3df1a..30060f6 100644 (file)
@@ -62,7 +62,7 @@ private:
     void updateAnimationSchedule();
     void animationScheduleTimerFired();
     void scheduleAnimationResolution();
-    void resolveAnimations();
+    void updateAnimations();
 
     RefPtr<Document> m_document;
     bool m_paused { false };
index cb7bb22..15513b6 100644 (file)
 #include "config.h"
 #include "KeyframeEffect.h"
 
+#include "CSSPropertyAnimation.h"
 #include "Element.h"
+#include "RenderStyle.h"
+#include "StyleProperties.h"
+#include "StyleResolver.h"
 
 namespace WebCore {
+using namespace JSC;
 
-Ref<KeyframeEffect> KeyframeEffect::create(Element* target)
+ExceptionOr<Ref<KeyframeEffect>> KeyframeEffect::create(ExecState& state, Element* target, Strong<JSObject>&& keyframes)
 {
-    return adoptRef(*new KeyframeEffect(target));
+    auto result = adoptRef(*new KeyframeEffect(target));
+
+    auto setKeyframesResult = result->setKeyframes(state, WTFMove(keyframes));
+    if (setKeyframesResult.hasException())
+        return setKeyframesResult.releaseException();
+
+    return WTFMove(result);
 }
 
 KeyframeEffect::KeyframeEffect(Element* target)
@@ -41,4 +52,93 @@ KeyframeEffect::KeyframeEffect(Element* target)
 {
 }
 
+ExceptionOr<void> KeyframeEffect::setKeyframes(ExecState& state, Strong<JSObject>&& keyframes)
+{
+    auto processKeyframesResult = processKeyframes(state, WTFMove(keyframes));
+    if (processKeyframesResult.hasException())
+        return processKeyframesResult.releaseException();
+    return { };
+}
+
+ExceptionOr<void> KeyframeEffect::processKeyframes(ExecState& state, Strong<JSObject>&& keyframes)
+{
+    // FIXME: We only have primitive to-from parsing, for full support see webkit.org/b/179708.
+    // Full specification is at https://w3c.github.io/web-animations/#processing-a-keyframes-argument.
+
+    if (!m_target || !keyframes)
+        return { };
+
+    if (!isJSArray(keyframes.get()))
+        return Exception { TypeError };
+
+    Vector<Keyframe> newKeyframes { };
+
+    VM& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    StyleResolver& styleResolver = m_target->styleResolver();
+    auto parserContext = CSSParserContext(HTMLStandardMode);
+
+    const auto* array = jsCast<const JSArray*>(keyframes.get());
+    auto length = array->length();
+    if (length != 2)
+        return Exception { TypeError };
+
+    for (unsigned i = 0; i < length; ++i) {
+        const JSValue value = array->getIndex(&state, i);
+        if (scope.exception() || !value || !value.isObject())
+            return Exception { TypeError };
+        JSObject* keyframe = value.toObject(&state);
+        PropertyNameArray ownPropertyNames(&vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
+        JSObject::getOwnPropertyNames(keyframe, &state, ownPropertyNames, EnumerationMode());
+        size_t numberOfProperties = ownPropertyNames.size();
+
+        StringBuilder cssText;
+        for (unsigned j = 0; j < numberOfProperties; ++j) {
+            cssText.append(ownPropertyNames[j].string());
+            cssText.appendLiteral(": ");
+            cssText.append(keyframe->get(&state, ownPropertyNames[j]).toWTFString(&state));
+            cssText.appendLiteral("; ");
+        }
+
+        auto renderStyle = RenderStyle::createPtr();
+        auto styleProperties = MutableStyleProperties::create();
+        styleProperties->parseDeclaration(cssText.toString(), parserContext);
+        unsigned numberOfCSSProperties = styleProperties->propertyCount();
+
+        Vector<CSSPropertyID> properties;
+        for (unsigned k = 0; k < numberOfCSSProperties; ++k) {
+            properties.append(styleProperties->propertyAt(k).id());
+            styleResolver.applyPropertyToStyle(styleProperties->propertyAt(k).id(), styleProperties->propertyAt(k).value(), WTFMove(renderStyle));
+            renderStyle = styleResolver.state().takeStyle();
+        }
+
+        newKeyframes.append({ RenderStyle::clone(*renderStyle), properties });
+    }
+
+    m_keyframes = WTFMove(newKeyframes);
+
+    return { };
+}
+
+void KeyframeEffect::applyAtLocalTime(Seconds localTime, RenderStyle& targetStyle)
+{
+    if (!m_target)
+        return;
+
+    // FIXME: Assume animations only apply in the range [0, duration[
+    // until we support fill modes, delays and iterations.
+    if (localTime < 0_s || localTime >= timing()->duration())
+        return;
+
+    if (!timing()->duration())
+        return;
+
+    float progress = localTime / timing()->duration();
+
+    // FIXME: This will crash if we attempt to animate properties that require an AnimationBase.
+    for (auto propertyId : m_keyframes[0].properties)
+        CSSPropertyAnimation::blendProperties(nullptr, propertyId, &targetStyle, &m_keyframes[0].style, &m_keyframes[1].style, progress);
+}
+
 } // namespace WebCore
index d5e4f36..60e9c23 100644 (file)
 #pragma once
 
 #include "AnimationEffect.h"
+#include "RenderStyle.h"
 #include <wtf/Ref.h>
 
 namespace WebCore {
 
 class Element;
 
+struct Keyframe {
+    RenderStyle style;
+    Vector<CSSPropertyID> properties;
+};
+
 class KeyframeEffect final : public AnimationEffect {
 public:
-    static Ref<KeyframeEffect> create(Element*);
+    static ExceptionOr<Ref<KeyframeEffect>> create(JSC::ExecState&, Element*, JSC::Strong<JSC::JSObject>&&);
     ~KeyframeEffect() { }
 
     Element* target() const { return m_target.get(); }
+    ExceptionOr<void> setKeyframes(JSC::ExecState&, JSC::Strong<JSC::JSObject>&&);
+    void applyAtLocalTime(Seconds, RenderStyle&) override;
 
 private:
     KeyframeEffect(Element*);
+    ExceptionOr<void> processKeyframes(JSC::ExecState&, JSC::Strong<JSC::JSObject>&&);
     RefPtr<Element> m_target;
+    Vector<Keyframe> m_keyframes;
 
 };
 
index 538e831..519a0b6 100644 (file)
 
 [
     EnabledAtRuntime=WebAnimations,
-    Constructor(Element? target)
+    ConstructorMayThrowException,
+    ConstructorCallWith=ScriptState,
+    Constructor(Element? target, object? keyframes)
 ] interface KeyframeEffect : AnimationEffect {
     readonly attribute Element? target;
+    [MayThrowException, CallWith=ScriptState] void setKeyframes(object? keyframes);
 };
index aec979e..0afbef5 100644 (file)
@@ -237,6 +237,12 @@ Seconds WebAnimation::timeToNextRequiredTick(Seconds timelineTime) const
     return Seconds::infinity();
 }
 
+void WebAnimation::resolve(RenderStyle& targetStyle)
+{
+    if (m_effect && currentTime())
+        m_effect->applyAtLocalTime(currentTime().value(), targetStyle);
+}
+
 String WebAnimation::description()
 {
     return "Animation";
index b4b5246..0eb236f 100644 (file)
@@ -38,6 +38,7 @@ namespace WebCore {
 class AnimationEffect;
 class AnimationTimeline;
 class Document;
+class RenderStyle;
 
 class WebAnimation final : public RefCounted<WebAnimation> {
 public:
@@ -63,6 +64,7 @@ public:
     void setPlaybackRate(double);
 
     Seconds timeToNextRequiredTick(Seconds) const;
+    void resolve(RenderStyle&);
 
     String description();
 
index 6b7bb22..4cb5e25 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "StringAdaptors.h"
 #include <heap/HandleTypes.h>
+#include <heap/Strong.h>
 #include <wtf/Brigand.h>
 #include <wtf/HashMap.h>
 #include <wtf/StdLibExtras.h>
@@ -150,7 +151,7 @@ template<typename T> struct IDLRequiresExistingAtomicStringAdaptor : IDLString<A
 struct IDLObject : IDLType<JSC::Strong<JSC::JSObject>> {
     using NullableType = JSC::Strong<JSC::JSObject>;
 
-    static inline std::nullptr_t nullValue() { return nullptr; }
+    static inline NullableType nullValue() { return { }; }
     template<typename U> static inline bool isNullValue(U&& value) { return !value; }
     template<typename U> static inline U&& extractValueFromNullable(U&& value) { return std::forward<U>(value); }
 };
index 6b4674c..d8975bc 100644 (file)
@@ -1377,6 +1377,7 @@ public:
     WEBCORE_EXPORT void setConsoleMessageListener(RefPtr<StringCallback>&&); // For testing.
 
     DocumentTimeline& timeline();
+    DocumentTimeline* existingTimeline() const { return m_timeline.get(); }
     Vector<RefPtr<WebAnimation>> getAnimations();
         
 #if ENABLE(ATTACHMENT_ELEMENT)
index 4cd18b4..2002c37 100644 (file)
@@ -3706,7 +3706,9 @@ Element* Element::findAnchorElementForLink(String& outAnchorName)
 Vector<RefPtr<WebAnimation>> Element::getAnimations()
 {
     // FIXME: Filter and order the list as specified (webkit.org/b/179535).
-    return document().timeline().animationsForElement(*this);
+    if (auto timeline = document().existingTimeline())
+        return timeline->animationsForElement(*this);
+    return Vector<RefPtr<WebAnimation>> { };
 }
 
 AccessibleNode* Element::accessibleNode()
index 213467c..c34de97 100644 (file)
@@ -30,6 +30,7 @@
 #include "CSSFontSelector.h"
 #include "ComposedTreeAncestorIterator.h"
 #include "ComposedTreeIterator.h"
+#include "DocumentTimeline.h"
 #include "ElementIterator.h"
 #include "HTMLBodyElement.h"
 #include "HTMLMeterElement.h"
@@ -274,9 +275,20 @@ const RenderStyle* TreeResolver::parentBoxStyle() const
 
 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange)
 {
+    auto* oldStyle = renderOrDisplayContentsStyle(element);
+
+    if (auto timeline = element.document().existingTimeline()) {
+        auto webAnimations = timeline->animationsForElement(element);
+        if (!webAnimations.isEmpty()) {
+            auto animatedStyle = RenderStyle::clonePtr(*newStyle);
+            for (const auto& animation : webAnimations)
+                animation->resolve(*animatedStyle);
+            newStyle = WTFMove(animatedStyle);
+        }
+    }
+
     auto& animationController = m_document.frame()->animation();
 
-    auto* oldStyle = renderOrDisplayContentsStyle(element);
     auto animationUpdate = animationController.updateAnimations(element, *newStyle, oldStyle);
 
     if (animationUpdate.style)