12012-03-10 Nikolas Zimmermann <nzimmermann@rim.com>
2
3 animations update baseVal instead of animVal
4 https://bugs.webkit.org/show_bug.cgi?id=12437
5
6 Reviewed by NOBODY (OOPS!).
7
8 Begin implementing the last missing core piece of the SVG DOM: proper animVal support.
9 Most SVG DOM interfaces exposing eg. lengths use SVGAnimatedLength. eg. from SVGRectElement:
10 "readonly attribute SVGAnimatedLength x;" SVGAnimatedXXX contains following methods:
11 "readonly attribute SVGLength baseVal; readonly attribute SVGLength animVal;"
12 From SVG DOM perspective, animVal and baseVal are two distinctive objects, animVal != baseVal.
13 Its underlying value is the same though, if no animation is running on that attribute.
14
15 As soon as a SMIL animation starts animating an SVGAnimated* target attribute, its
16 baseVal and animVal may begin to differ. The animVal always reflect the current animated
17 value (including all effects of additive/accumulated animations) which is shown on screen
18 when eg animating the width of a <rect>. The baseVal is is equal to the underlying XML
19 property value / SVG DOM value, but may be influenced through dynamic changes.
20 (Consider changing rect1.width.baseVal.value while 'width' is animated)
21
22 During the last year we prepared our animation code to turn on animVal support.
23 This patch adds the last missing pieces to turn on animVal support for the SVGLength.
24 SVGLengthList and all other types will follow, one after the other.
25
26 I've decided to write an exhaustive ChangeLog, as this as the base for any future
27 work in this area - hopefully making this more reviewable.
28
29 Tests: svg/animations/additive-from-to-width-animation.html
30 svg/animations/additive-values-width-animation.html
31 svg/animations/change-baseVal-while-animating-fill-freeze-2.html
32 svg/animations/change-baseVal-while-animating-fill-freeze.html
33 svg/animations/change-baseVal-while-animating-fill-remove-2.html
34 svg/animations/change-baseVal-while-animating-fill-remove.html
35 svg/animations/change-target-while-animating-SVG-property.html
36 svg/animations/multiple-animations-fill-freeze.html
37 svg/animations/remove-animation-element-while-animation-is-running.html
38 svg/repaint/repainting-after-animation-element-removal.svg
39
40
41 * svg/SVGAnimateElement.cpp: Remove unnecessary std namespace inclusion.
42 (WebCore::SVGAnimateElement::SVGAnimateElement): Remove now-obsolete m_aboutToStopAnimation.
43 (WebCore::SVGAnimateElement::calculateAnimatedValue): Swap assertion order, to test hasTagName() _before_ casting.
44 (WebCore::SVGAnimateElement::resetToBaseValue):
45 Stop relying on the cached baseValue (breaking additive="sum"+values animation) for SVG DOM primitive animations.
46 Avoid any string roundtrips previously needed to reset the SVGAnimatedType to the base value. Just grab the
47 currentBaseValue() from the associated SVGAnimatedProperty, which includes all dynamic changes to the baseVal
48 either by SVG DOM or setAttribute() calls - this way we don't need to utilize the buggy cache in SMILTimeContainer,
49 which can be removed once all SVG DOM primitive types switched to the new animVal concept.
50
51 NOTE: When multiple animations of the same attribute are applied to a target element, resetToBaseValue() will be called
52 for the highest priority SVGSMILElement, on every animation step! Consider two <animate> elements, applied to a target
53 <rect> which both animate the 'x' attribute, one from 0s to 2s, the other from 4s to 6s. The last <animate> element
54 will reuse the SVGAnimatedType m_animatedType from the first <animate> element, and never create an own m_animatedType.
55 When the animation starts the first time at 0s, we update the rect.x.animVals SVGLength* pointer, to point to the
56 SVGAnimatedType of the first <animate> element, owning the m_animatedType. From that point on each call to rect.x.animVal
57 will always return the same value as the SVGAnimatedType of the first <animate> element holds. Now after 2s the first
58 <animate> element becomes inactive, but its m_animatedType remains alive. The bindings don't notice this change at all.
59 Now at 4s, the second animation element gets active. It reuses the SVGAnimatedType of the first <animate> element, and
60 applies its animation changes to that SVGAnimatedType, which is immediately reflected in the bindings w/o any additional
61 work. It's very important for the understanding when animationStarted/animationEnded need to be called.
62
63 (WebCore::SVGAnimateElement::applyResultsToTarget): Remove now-obsolete m_aboutToStopAnimation logic. No need to know it at this point.
64 (WebCore::SVGAnimateElement::targetElementWillChange):
65 Renamed from targetElementDidChange(). This method is called from SVGSMILElement for following conditions:
66 - animation element is destructed
67 - animation element is removed from document
68 - target element of animation is destructed
69 - target element of animation is removed from document
70 - target element of animation changes id
71
72 Whenever any of this happens, we need to reset the animVal. Resetting the animVal involves resetting the PropertyType* pointer,
73 eg. SVGLength*, from the animVal property tear off, belonging to a certain SVGAnimatedProperty (eg. rect.x) to the initial
74 value again, which is the 'm_x' of the SVGRectElement. This is needed as the SVGAnimatedType the animVal currently points to,
75 if an animation is/was running, is destructed in targetElementWillChange(), to reset the SVGAnimateElement to the initial
76 state before it received a target. This is the only place which destructed the m_animatedType, and thus the only place that
77 needs to take care of resetting the animVal pointers.
78
79 * svg/SVGAnimatedLength.cpp:
80 (WebCore::SVGAnimatedLengthAnimator::constructFromCopy):
81 Add a new constructFromCopy(SVGGenericAnimatedType) function to SVGAnimatedLengthAnimator.
82 It takes a type-unsafe SVGGenericAnimatedType - the caller has to guarantee the type matches.
83 This is strictly enforced for the single caller of constructFromCopy, and guaranteed to be safe.
84
85 * svg/SVGAnimatedLength.h: Add new constructFromCopy method, which is used to avoid string-roundtrips when resetting to base values.
86 * svg/SVGAnimatedType.cpp:
87 (WebCore::SVGAnimatedType::supportsAnimVal): Only returns true for AnimatedLength, for now.
88 (WebCore::SVGAnimatedType::setValue): Takes a SVGGenericAnimatedType, assuming the type matches. Callers have to guarantee type-safety!
89 * svg/SVGAnimatedType.h:
90 (SVGAnimatedType): Add new static supportsAnimVal(AnimatedPropertyType) function.
91 (WebCore::SVGAnimatedType::variant): Add a generic accessor for all animated types, called variant(). Only one place uses this.
92 * svg/SVGAnimatedTypeAnimator.h:
93 (WebCore::SVGAnimatedTypeAnimator::constructFromCopy):
94 New method to construct an eg. SVGAnimatedLengthAnimator right from a SVGLength, instead of a String.
95 In that case the SVGAnimatedType just stores a pointer to the underlying SVGLength, no copying and or other roundtrips involved.
96
97 * svg/SVGAnimationElement.cpp:
98 (WebCore::SVGAnimationElement::svgAttributeChanged):
99 Implement this instead of attributeChanged. The previous implementation reset the animation state to Inactive, causing a full
100 rebuild, whenever any attribute changes, even though it might not be related for the animation element, eg.
101 animate.setAttribute("stdDeviationX", "foobar"). Fix that by checking if we support the attribute (keyTimes/keySplines/etc..)
102 , if not pass it on to SVGSMILElement (which supports begin/end/etc..) to check if it can handle that.
103
104 (WebCore::SVGAnimationElement::animationAttributeChanged):
105 Called from our svgAttributeChanged, and/or from SVGSMILElement::svgAttributeChanged, whenever a _known_ attribute has changed.
106 This sledgehammer should be used with care, instead of each time attributeChanged() is called :-)
107
108 (WebCore::setTargetAttributeAnimatedCSSValue):
109 Remove support for removing properties from the override style sheet. I've added this optimization too early, we should reevaluate
110 this once more types support animVal. It currently complexifies the logic too much, requiring setAttributeAnimatedValue to know
111 if the animation ends (and that's not easy to figure out, at least not using started/endedActiveInterval, as I anticipated).
112
113 (WebCore::findMatchingAnimatedProperty):
114 Add helper functions which retrieves a SVGAnimatedProperty* for a given SVGElement* targetElement, an attributeName, and an attribute
115 type. eg. findMatchingAnimatedProperty(myRectElement, SVGNames::xAttr, AnimatedLength) returns the SVGAnimatedProperty which is
116 exposed to JS, that holds: SVGProperty* baseVal, and SVGProperty* animVal. (Lazily created if they got accessed from JS.). This is
117 used to update the animVal pointing to a new eg. SVGLength* value, once animation has started, to make rect->x() return that new
118 SVGLength* value (internally), and to reflect the current animated value in rect.x.animVal.value from JS.
119
120 (WebCore::SVGAnimationElement::applyAnimatedValue): Refactored from setTargetAttributeAnimatedValue, to simplify the code.
121 (WebCore::notifyAnimatedPropertyAboutAnimationBeginEnd):
122 Helper function to share code betweeen animationStarted/animationEnded.
123 It takes a SVGAnimatedProperty* and a SVGAnimatedType* which may be zero, indicating that the animation ended.
124 It calls animationStarted/animationEnded on the given SVGAnimatedProperty, to update the animVal state.
125 It also figures out all instances of the target element, and their SVGAnimatedProperties that may need updating.
126
127 (WebCore::SVGAnimationElement::animationStarted): Uses the helper above, passing on the given animatedType.
128 (WebCore::SVGAnimationElement::animationEnded): Uses the helper above, passing 0 as animatedType.
129 (WebCore::InstanceUpdateBlocker::InstanceUpdateBlocker):
130 Added new helper struct, doing element->setInstancesUpdatedBlock(true) on construction and setInstancesUpdatesBlocked(false) on
131 destruction, making it impossible to forget one. If we ever rewrite svgAttributeChanged & co to auto-update the cloned instances,
132 this can go away.
133
134 (WebCore::SVGAnimationElement::setTargetAttributeAnimatedValue):
135 Now takes an SVGAnimatedType* instead of a String parameter. In order to avoid string-roundtrips for animVal support, let us
136 decide if we need to construct a String out of it, or not. For animations supporting animVal (only SVGLength) we don't need
137 to update any attribute or animVal pointer here, that happens automatically! We only need to notify the targetElement eg,
138 that its xAttr changed! Previously we had to call targetElement->setAttribute("x", "...") on every animation step for
139 SVGLength animations - that's gone now! The SVGAnimatedType pointers remains the same during the whole animation, so there's
140 no need to call animationStarted() at each animated step!
141
142 (WebCore::SVGAnimationElement::animatedPropertyForType):
143 Helper function returning a SVGAnimatedProperty* for the current target element & current target attribute, if the
144 current animation is running on a type supporting animVal (SVGLength), or returning 0. This is needed for SVGAnimateElement.
145 Reuses the existing findMatchingAnimatedProperty code.
146
147 * svg/SVGAnimationElement.h:
148 * svg/animation/SMILTimeContainer.cpp:
149 (WebCore::SMILTimeContainer::updateAnimations):
150 Add comment to clarify why caching baseValues is just wrong. For SVGLength animations the problem is now gone.
151 This is exercised using the new additive-from-to-width-animation.html & additive-values-width-animation.html tests.
152
153 * svg/animation/SVGSMILElement.cpp:
154 (WebCore::SVGSMILElement::removedFromDocument):
155 Since animVal requires that the SVGAnimatedProperties are correctly reset if an animation element is removed from the document,
156 we have to call targetElementWillChange(0) from here. That requires to move the "m_attributeName = anyQName()" line down,
157 otherwise targetElementWillChange() would early exit, as no valid attributeName was specified.
158
159 This is verified using the new svg/animations/remove-animation-element-while-animation-is-running.html
160 and svg/repaint/repainting-after-animation-element-removal.svg tests.
161
162 (WebCore::SVGSMILElement::isSupportedAttribute): Add function like all SVG*Elements have identifying their supported attributes.
163 (WebCore::SVGSMILElement::svgAttributeChanged):
164 Implement svgAttributeChanged instead of attributeChanged. Only take action if the attribute is actually supported.
165 If one of the common attributes like begin/end/etc. changed, be sure to call animationAttributeChanged() so that our
166 ancestor-classes get notified about this and can take action as well.
167
168 (WebCore::SVGSMILElement::targetElement): Adapt logic to targetElementDidChange -> targetElementWillChange change.
169 (WebCore::SVGSMILElement::targetElementWillChange):
170 Renamed from targetElementDidChange. Added "oldTarget" as parameter as well. Our ancestor-classes like SVGAnimateElement
171 use this to properly deregister the animVal in the old target, before resetting the SVGAnimatedType, otherwise we'd leave
172 dangling pointers around (verified manually by guard malloc runs, that none of this happens).
173
174 Also add a default implementation here in targetElementWillChange, that ancestor classes have to call.
175 We now properly call endedActiveInterval() if the m_activeState is currently Active, so that animations are shut-down
176 just like if the animation properly ends (use the same cleanup routines, etc.). Not doing that now leads to assertions.
177
178 (WebCore::SVGSMILElement::resetTargetElement):
179 Instead of forcing m_activeState to be inactive, use the standard methods to end the animation.
180 targetElementWillChange(m_targetElement, 0) and animationAttributeChanged().
181
182 resetTargetElement() is only called by SVGDocumentExtensions::removeAllAnimationElementsFromTarget() for following conditions:
183 - targetElement gets destructed
184 - targetElement gets removed from the document
185 - targetElement id changes
186
187 If the targetElement gets destructed or removed, no actions need to be taken, as the SVGAnimatedPropertys are teared down
188 as well. But if only the id changes, we still have to properly disconnect the animVals - this is all handled through
189 targetElementWillChange now - that's why this has to be called from here as well.
190 That explains why targetElementWillChange() now needs to check if the targetElement is destructing or not.
191
192 * svg/properties/SVGAnimatedEnumerationPropertyTearOff.h:
193 Pass the AnimatedPropertyType from the SVGPropertyInfo to the SVGAnimatedProperties.
194 Requires mechanic changes in all SVGAnimated* classes. We need acccess to the AnimatedPropertyType
195 to verify the SVGAnimatedType objects, passed to animationStarted, match our type. This is to enforce
196 strict type-checking, whenever SVGGenericAnimatedTypes are passed around.
197
198 (WebCore::SVGAnimatedEnumerationPropertyTearOff::create):
199 (WebCore::SVGAnimatedEnumerationPropertyTearOff::SVGAnimatedEnumerationPropertyTearOff):
200 * svg/properties/SVGAnimatedListPropertyTearOff.h: Ditto.
201 (WebCore::SVGAnimatedListPropertyTearOff::create):
202 (WebCore::SVGAnimatedListPropertyTearOff::SVGAnimatedListPropertyTearOff):
203 * svg/properties/SVGAnimatedPathSegListPropertyTearOff.h: Ditto.
204 (WebCore::SVGAnimatedPathSegListPropertyTearOff::create):
205 (WebCore::SVGAnimatedPathSegListPropertyTearOff::SVGAnimatedPathSegListPropertyTearOff):
206 * svg/properties/SVGAnimatedProperty.h: Store AnimatedPropertyType, add accessors.
207 (WebCore::SVGAnimatedProperty::animatedPropertyType): Add accessor.
208 (WebCore::SVGAnimatedProperty::animationValueChanged): New animVal related functions to be implemented in the animated tear offs.
209 (WebCore::SVGAnimatedProperty::animationStarted): Ditto.
210 (WebCore::SVGAnimatedProperty::animationEnded): Ditto.
211 (WebCore::SVGAnimatedProperty::currentBaseValue):
212 Generic accessor for the baseVal: returns a SVGGenericAnimatedType.
213 It takes an AnimatedPropertyType as input, that's only needed to verify that the type we're returning matches
214 the expectation of the caller. If not, return 0 to avoid any potential casting mistakes, which would lead to crashes.
215
216 (WebCore::SVGAnimatedProperty::SVGAnimatedProperty): Store m_animatedPropertyType.
217 * svg/properties/SVGAnimatedPropertyTearOff.h:
218 (WebCore::SVGAnimatedPropertyTearOff::create): Same changes as in the other tear offs: pass around AnimatedPropertyType.
219 (WebCore::SVGAnimatedPropertyTearOff::currentBaseValue): Returns &m_property, if the type matches (see above).
220 (SVGAnimatedPropertyTearOff): Pass around AnimatedPropertyType.
221 (WebCore::SVGAnimatedPropertyTearOff::animationValueChanged): No-op for non list types, don't need to do anything here.
222 (WebCore::SVGAnimatedPropertyTearOff::animationStarted):
223 (WebCore::SVGAnimatedPropertyTearOff::animationEnded):
224 Store the currently animated value in the animVal() property tear off, that's also re-used as-is for the JS bindings.
225 As this is important, here's an example of how this affects methods like rect->x() used in the renderers.
226
227 Setting m_isAnimating to true, redirects any rect->x() calls that previously returned rect->m_x, to
228 rect->xAnimated()->animVal()->propertyReference() (which returns the same SVGLength& that the SVGAnimatedElement
229 m_animatedType contains). Calling rect->setXBaseValue() still modifies rect->m_x, and is used by all parseAttribute()
230 methods in svg/ as setAttribute() calls only ever modify the "baseValue", never the current animated value.
231 rect.x.baseVal will return a SVGLength object corresponding to rect->m_x.
232 rect.x.animVal will return a SVGLength object corresponding to rect->xAnimated()->animVal()->propertyReference().
233
234 These implementation details are all hidden in the SVGAnimatedPropertyMacros. Here's an example from SVGRectElement:
235 DECLARE_ANIMATED_LENGTH(X, x) -> Replace PropertyType with 'SVGLength', LowerProperty with 'x', and UpperProperty with 'X'.
236
237 PropertyType& LowerProperty() const
238 {
239 if (TearOffType* wrapper = SVGAnimatedProperty::lookupWrapper<UseOwnerType, TearOffType, IsDerivedFromSVGElement<UseOwnerType>::value>(this, LowerProperty##PropertyInfo())) {
240 if (wrapper->isAnimating())
241 return wrapper->currentAnimatedValue();
242 }
243 return m_##LowerProperty.value;
244 }
245
246 PropertyType& LowerProperty##BaseValue() const
247 {
248 return m_##LowerProperty.value;
249 }
250
251 void set##UpperProperty##BaseValue(const PropertyType& type)
252 {
253 m_##LowerProperty.value = type;
254 }
255
256 Any code outside of svg/, eg. in rendering/svg, does not need to care about any baseVal/animVal differences.
257 During layout eg. RenderSVGRect calls rect->x().value(someLengthContext) to get the current 'x' as float. If an animation
258 is running on that rect element it will automatically retrieve the last set animated value here - all under the hood.
259 I hope that sheds some light in those myserious functions, they were designed with animVal in mind, but we never had that until now :-)
260
261 (WebCore::SVGAnimatedPropertyTearOff::SVGAnimatedPropertyTearOff): Pass around AnimatedPropertyType.
262 (WebCore::SVGAnimatedPropertyTearOff::~SVGAnimatedPropertyTearOff): Add destructor to debug builds veryifing that m_isAnimating is false.
263 * svg/properties/SVGAnimatedStaticPropertyTearOff.h: Ditto.
264 (WebCore::SVGAnimatedStaticPropertyTearOff::create):
265 (WebCore::SVGAnimatedStaticPropertyTearOff::SVGAnimatedStaticPropertyTearOff):
266 * svg/properties/SVGAnimatedTransformListPropertyTearOff.h: Ditto.
267 (WebCore::SVGAnimatedTransformListPropertyTearOff::create):
268 (WebCore::SVGAnimatedTransformListPropertyTearOff::SVGAnimatedTransformListPropertyTearOff):
269 * svg/properties/SVGPropertyInfo.h: Add SVGGenericAnimatedType definition.
270 * svg/properties/SVGPropertyTearOff.h: Remove obsolete updateAnimVal method - switched to using setValue directly.
271