|
1 /* |
|
2 * Copyright (C) 2007 Apple Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions |
|
6 * are met: |
|
7 * |
|
8 * 1. Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * 2. Redistributions in binary form must reproduce the above copyright |
|
11 * notice, this list of conditions and the following disclaimer in the |
|
12 * documentation and/or other materials provided with the distribution. |
|
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
14 * its contributors may be used to endorse or promote products derived |
|
15 * from this software without specific prior written permission. |
|
16 * |
|
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include "config.h" |
|
30 |
|
31 #include "AnimationControllerPrivate.h" |
|
32 #include "CompositeAnimation.h" |
|
33 #include "CSSPropertyNames.h" |
|
34 #include "EventNames.h" |
|
35 #include "ImplicitAnimation.h" |
|
36 #include "KeyframeAnimation.h" |
|
37 #include "RenderLayer.h" |
|
38 #include "RenderLayerBacking.h" |
|
39 #include <wtf/UnusedParam.h> |
|
40 |
|
41 namespace WebCore { |
|
42 |
|
43 ImplicitAnimation::ImplicitAnimation(const Animation* transition, int animatingProperty, RenderObject* renderer, CompositeAnimation* compAnim, RenderStyle* fromStyle) |
|
44 : AnimationBase(transition, renderer, compAnim) |
|
45 , m_transitionProperty(transition->property()) |
|
46 , m_animatingProperty(animatingProperty) |
|
47 , m_overridden(false) |
|
48 , m_active(true) |
|
49 , m_fromStyle(fromStyle) |
|
50 { |
|
51 ASSERT(animatingProperty != cAnimateAll); |
|
52 } |
|
53 |
|
54 ImplicitAnimation::~ImplicitAnimation() |
|
55 { |
|
56 // // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed. |
|
57 if (!postActive()) |
|
58 endAnimation(); |
|
59 } |
|
60 |
|
61 bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inListenerType) const |
|
62 { |
|
63 return m_object->document()->hasListenerType(inListenerType); |
|
64 } |
|
65 |
|
66 void ImplicitAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) |
|
67 { |
|
68 // If we get this far and the animation is done, it means we are cleaning up a just finished animation. |
|
69 // So just return. Everything is already all cleaned up. |
|
70 if (postActive()) |
|
71 return; |
|
72 |
|
73 // Reset to start the transition if we are new |
|
74 if (isNew()) |
|
75 reset(targetStyle); |
|
76 |
|
77 // Run a cycle of animation. |
|
78 // We know we will need a new render style, so make one if needed |
|
79 if (!animatedStyle) |
|
80 animatedStyle = RenderStyle::clone(targetStyle); |
|
81 |
|
82 bool needsAnim = blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); |
|
83 // FIXME: we also need to detect cases where we have to software animate for other reasons, |
|
84 // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902 |
|
85 if (needsAnim) |
|
86 setAnimating(); |
|
87 else { |
|
88 #if USE(ACCELERATED_COMPOSITING) |
|
89 // If we are running an accelerated animation, set a flag in the style which causes the style |
|
90 // to compare as different to any other style. This ensures that changes to the property |
|
91 // that is animating are correctly detected during the animation (e.g. when a transition |
|
92 // gets interrupted). |
|
93 animatedStyle->setIsRunningAcceleratedAnimation(); |
|
94 #endif |
|
95 } |
|
96 |
|
97 // Fire the start timeout if needed |
|
98 fireAnimationEventsIfNeeded(); |
|
99 } |
|
100 |
|
101 void ImplicitAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) |
|
102 { |
|
103 if (!animatedStyle) |
|
104 animatedStyle = RenderStyle::clone(m_toStyle.get()); |
|
105 |
|
106 blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); |
|
107 } |
|
108 |
|
109 bool ImplicitAnimation::startAnimation(double timeOffset) |
|
110 { |
|
111 #if USE(ACCELERATED_COMPOSITING) |
|
112 if (m_object && m_object->hasLayer()) { |
|
113 RenderLayer* layer = toRenderBoxModelObject(m_object)->layer(); |
|
114 if (layer->isComposited()) |
|
115 return layer->backing()->startTransition(timeOffset, m_animatingProperty, m_fromStyle.get(), m_toStyle.get()); |
|
116 } |
|
117 #else |
|
118 UNUSED_PARAM(timeOffset); |
|
119 #endif |
|
120 return false; |
|
121 } |
|
122 |
|
123 void ImplicitAnimation::endAnimation() |
|
124 { |
|
125 #if USE(ACCELERATED_COMPOSITING) |
|
126 if (m_object && m_object->hasLayer()) { |
|
127 RenderLayer* layer = toRenderBoxModelObject(m_object)->layer(); |
|
128 if (layer->isComposited()) |
|
129 layer->backing()->transitionFinished(m_animatingProperty); |
|
130 } |
|
131 #endif |
|
132 } |
|
133 |
|
134 void ImplicitAnimation::onAnimationEnd(double elapsedTime) |
|
135 { |
|
136 // If we have a keyframe animation on this property, this transition is being overridden. The keyframe |
|
137 // animation keeps an unanimated style in case a transition starts while the keyframe animation is |
|
138 // running. But now that the transition has completed, we need to update this style with its new |
|
139 // destination. If we didn't, the next time through we would think a transition had started |
|
140 // (comparing the old unanimated style with the new final style of the transition). |
|
141 RefPtr<KeyframeAnimation> keyframeAnim = m_compAnim->getAnimationForProperty(m_animatingProperty); |
|
142 if (keyframeAnim) |
|
143 keyframeAnim->setUnanimatedStyle(m_toStyle); |
|
144 |
|
145 sendTransitionEvent(eventNames().webkitTransitionEndEvent, elapsedTime); |
|
146 endAnimation(); |
|
147 } |
|
148 |
|
149 bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, double elapsedTime) |
|
150 { |
|
151 if (eventType == eventNames().webkitTransitionEndEvent) { |
|
152 Document::ListenerType listenerType = Document::TRANSITIONEND_LISTENER; |
|
153 |
|
154 if (shouldSendEventForListener(listenerType)) { |
|
155 String propertyName; |
|
156 if (m_animatingProperty != cAnimateAll) |
|
157 propertyName = getPropertyName(static_cast<CSSPropertyID>(m_animatingProperty)); |
|
158 |
|
159 // Dispatch the event |
|
160 RefPtr<Element> element = 0; |
|
161 if (m_object->node() && m_object->node()->isElementNode()) |
|
162 element = static_cast<Element*>(m_object->node()); |
|
163 |
|
164 ASSERT(!element || (element->document() && !element->document()->inPageCache())); |
|
165 if (!element) |
|
166 return false; |
|
167 |
|
168 // Schedule event handling |
|
169 m_compAnim->animationController()->addEventToDispatch(element, eventType, propertyName, elapsedTime); |
|
170 |
|
171 // Restore the original (unanimated) style |
|
172 if (eventType == eventNames().webkitTransitionEndEvent && element->renderer()) |
|
173 setNeedsStyleRecalc(element.get()); |
|
174 |
|
175 return true; // Did dispatch an event |
|
176 } |
|
177 } |
|
178 |
|
179 return false; // Didn't dispatch an event |
|
180 } |
|
181 |
|
182 void ImplicitAnimation::reset(RenderStyle* to) |
|
183 { |
|
184 ASSERT(to); |
|
185 ASSERT(m_fromStyle); |
|
186 |
|
187 m_toStyle = to; |
|
188 |
|
189 // Restart the transition |
|
190 if (m_fromStyle && m_toStyle) |
|
191 updateStateMachine(AnimationStateInputRestartAnimation, -1); |
|
192 |
|
193 // set the transform animation list |
|
194 validateTransformFunctionList(); |
|
195 } |
|
196 |
|
197 void ImplicitAnimation::setOverridden(bool b) |
|
198 { |
|
199 if (b == m_overridden) |
|
200 return; |
|
201 |
|
202 m_overridden = b; |
|
203 updateStateMachine(m_overridden ? AnimationStateInputPauseOverride : AnimationStateInputResumeOverride, -1); |
|
204 } |
|
205 |
|
206 bool ImplicitAnimation::affectsProperty(int property) const |
|
207 { |
|
208 return (m_animatingProperty == property); |
|
209 } |
|
210 |
|
211 bool ImplicitAnimation::isTargetPropertyEqual(int prop, const RenderStyle* targetStyle) |
|
212 { |
|
213 // We can get here for a transition that has not started yet. This would make m_toStyle unset and null. |
|
214 // So we check that here (see <https://bugs.webkit.org/show_bug.cgi?id=26706>) |
|
215 if (!m_toStyle) |
|
216 return false; |
|
217 return propertiesEqual(prop, m_toStyle.get(), targetStyle); |
|
218 } |
|
219 |
|
220 void ImplicitAnimation::blendPropertyValueInStyle(int prop, RenderStyle* currentStyle) |
|
221 { |
|
222 // We should never add a transition with a 0 duration and delay. But if we ever did |
|
223 // it would have a null toStyle. So just in case, let's check that here. (See |
|
224 // <https://bugs.webkit.org/show_bug.cgi?id=24787> |
|
225 if (!m_toStyle) |
|
226 return; |
|
227 |
|
228 blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); |
|
229 } |
|
230 |
|
231 void ImplicitAnimation::validateTransformFunctionList() |
|
232 { |
|
233 m_transformFunctionListValid = false; |
|
234 |
|
235 if (!m_fromStyle || !m_toStyle) |
|
236 return; |
|
237 |
|
238 const TransformOperations* val = &m_fromStyle->transform(); |
|
239 const TransformOperations* toVal = &m_toStyle->transform(); |
|
240 |
|
241 if (val->operations().isEmpty()) |
|
242 val = toVal; |
|
243 |
|
244 if (val->operations().isEmpty()) |
|
245 return; |
|
246 |
|
247 // See if the keyframes are valid |
|
248 if (val != toVal) { |
|
249 // A list of length 0 matches anything |
|
250 if (!toVal->operations().isEmpty()) { |
|
251 // If the sizes of the function lists don't match, the lists don't match |
|
252 if (val->operations().size() != toVal->operations().size()) |
|
253 return; |
|
254 |
|
255 // If the types of each function are not the same, the lists don't match |
|
256 for (size_t j = 0; j < val->operations().size(); ++j) { |
|
257 if (!val->operations()[j]->isSameType(*toVal->operations()[j])) |
|
258 return; |
|
259 } |
|
260 } |
|
261 } |
|
262 |
|
263 // Keyframes are valid |
|
264 m_transformFunctionListValid = true; |
|
265 } |
|
266 |
|
267 double ImplicitAnimation::timeToNextService() |
|
268 { |
|
269 double t = AnimationBase::timeToNextService(); |
|
270 #if USE(ACCELERATED_COMPOSITING) |
|
271 if (t != 0 || preActive()) |
|
272 return t; |
|
273 |
|
274 // A return value of 0 means we need service. But if this is an accelerated animation we |
|
275 // only need service at the end of the transition. |
|
276 if (animationOfPropertyIsAccelerated(m_animatingProperty) && isAccelerated()) { |
|
277 bool isLooping; |
|
278 getTimeToNextEvent(t, isLooping); |
|
279 } |
|
280 #endif |
|
281 return t; |
|
282 } |
|
283 |
|
284 } // namespace WebCore |