| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 #include "core/animation/css/CSSAnimations.h" | 32 #include "core/animation/css/CSSAnimations.h" |
| 33 | 33 |
| 34 #include "StylePropertyShorthand.h" |
| 34 #include "core/animation/ActiveAnimations.h" | 35 #include "core/animation/ActiveAnimations.h" |
| 35 #include "core/animation/DocumentTimeline.h" | 36 #include "core/animation/DocumentTimeline.h" |
| 36 #include "core/animation/KeyframeAnimationEffect.h" | 37 #include "core/animation/KeyframeAnimationEffect.h" |
| 38 #include "core/animation/css/CSSAnimatableValueFactory.h" |
| 37 #include "core/css/CSSKeyframeRule.h" | 39 #include "core/css/CSSKeyframeRule.h" |
| 38 #include "core/css/resolver/StyleResolver.h" | 40 #include "core/css/resolver/StyleResolver.h" |
| 39 #include "core/dom/Element.h" | 41 #include "core/dom/Element.h" |
| 40 #include "core/events/ThreadLocalEventNames.h" | 42 #include "core/events/ThreadLocalEventNames.h" |
| 43 #include "core/events/TransitionEvent.h" |
| 41 #include "core/events/WebKitAnimationEvent.h" | 44 #include "core/events/WebKitAnimationEvent.h" |
| 42 #include "core/platform/animation/CSSAnimationDataList.h" | 45 #include "core/platform/animation/CSSAnimationDataList.h" |
| 43 #include "core/platform/animation/TimingFunction.h" | 46 #include "core/platform/animation/TimingFunction.h" |
| 44 #include "wtf/HashSet.h" | 47 #include "wtf/HashSet.h" |
| 45 | 48 |
| 49 namespace WebCore { |
| 50 |
| 51 struct CandidateTransition { |
| 52 CandidateTransition(PassRefPtr<AnimatableValue> from, PassRefPtr<AnimatableV
alue> to, const CSSAnimationData* anim) |
| 53 : from(from) |
| 54 , to(to) |
| 55 , anim(anim) |
| 56 { |
| 57 } |
| 58 CandidateTransition() { } // The HashMap calls the default ctor |
| 59 RefPtr<AnimatableValue> from; |
| 60 RefPtr<AnimatableValue> to; |
| 61 const CSSAnimationData* anim; |
| 62 }; |
| 63 typedef HashMap<CSSPropertyID, CandidateTransition> CandidateTransitionMap; |
| 64 |
| 46 namespace { | 65 namespace { |
| 47 | 66 |
| 48 using namespace WebCore; | |
| 49 | |
| 50 bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference) | 67 bool isEarlierPhase(TimedItem::Phase target, TimedItem::Phase reference) |
| 51 { | 68 { |
| 52 ASSERT(target != TimedItem::PhaseNone); | 69 ASSERT(target != TimedItem::PhaseNone); |
| 53 ASSERT(reference != TimedItem::PhaseNone); | 70 ASSERT(reference != TimedItem::PhaseNone); |
| 54 return target < reference; | 71 return target < reference; |
| 55 } | 72 } |
| 56 | 73 |
| 57 bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference) | 74 bool isLaterPhase(TimedItem::Phase target, TimedItem::Phase reference) |
| 58 { | 75 { |
| 59 ASSERT(target != TimedItem::PhaseNone); | 76 ASSERT(target != TimedItem::PhaseNone); |
| 60 ASSERT(reference != TimedItem::PhaseNone); | 77 ASSERT(reference != TimedItem::PhaseNone); |
| 61 return target > reference; | 78 return target > reference; |
| 62 } | 79 } |
| 63 | 80 |
| 64 } // namespace | |
| 65 | |
| 66 namespace WebCore { | |
| 67 | |
| 68 // Returns the default timing function. | 81 // Returns the default timing function. |
| 69 const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData*
animationData, Timing& timing) | 82 const PassRefPtr<TimingFunction> timingFromAnimationData(const CSSAnimationData*
animationData, Timing& timing) |
| 70 { | 83 { |
| 71 if (animationData->isDelaySet()) | 84 if (animationData->isDelaySet()) |
| 72 timing.startDelay = animationData->delay(); | 85 timing.startDelay = animationData->delay(); |
| 73 if (animationData->isDurationSet()) { | 86 if (animationData->isDurationSet()) { |
| 74 timing.iterationDuration = animationData->duration(); | 87 timing.iterationDuration = animationData->duration(); |
| 75 timing.hasIterationDuration = true; | 88 timing.hasIterationDuration = true; |
| 76 } | 89 } |
| 77 if (animationData->isIterationCountSet()) { | 90 if (animationData->isIterationCountSet()) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 case CSSAnimationData::AnimationDirectionAlternateReverse: | 127 case CSSAnimationData::AnimationDirectionAlternateReverse: |
| 115 timing.direction = Timing::PlaybackDirectionAlternateReverse; | 128 timing.direction = Timing::PlaybackDirectionAlternateReverse; |
| 116 break; | 129 break; |
| 117 default: | 130 default: |
| 118 ASSERT_NOT_REACHED(); | 131 ASSERT_NOT_REACHED(); |
| 119 } | 132 } |
| 120 } | 133 } |
| 121 return animationData->isTimingFunctionSet() ? animationData->timingFunction(
) : CSSAnimationData::initialAnimationTimingFunction(); | 134 return animationData->isTimingFunctionSet() ? animationData->timingFunction(
) : CSSAnimationData::initialAnimationTimingFunction(); |
| 122 } | 135 } |
| 123 | 136 |
| 137 void calculateCandidateTransitionForProperty(const CSSAnimationData* anim, CSSPr
opertyID id, const RenderStyle* oldStyle, const RenderStyle* newStyle, Candidate
TransitionMap& candidateMap) |
| 138 { |
| 139 RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyl
e); |
| 140 RefPtr<AnimatableValue> to = CSSAnimatableValueFactory::create(id, newStyle)
; |
| 141 // If we have multiple transitions on the same property, we will use the |
| 142 // last one since we iterate over them in order and this will override |
| 143 // a previously set CandidateTransition. |
| 144 if (!from->equals(to.get())) |
| 145 candidateMap.add(id, CandidateTransition(from.release(), to.release(), a
nim)); |
| 146 } |
| 147 |
| 148 void computeCandidateTransitions(const RenderStyle* oldStyle, const RenderStyle*
newStyle, CandidateTransitionMap& candidateMap, HashSet<CSSPropertyID>& listedP
roperties) |
| 149 { |
| 150 if (!newStyle->transitions()) |
| 151 return; |
| 152 |
| 153 for (size_t i = 0; i < newStyle->transitions()->size(); ++i) { |
| 154 const CSSAnimationData* anim = newStyle->transitions()->animation(i); |
| 155 CSSAnimationData::AnimationMode mode = anim->animationMode(); |
| 156 if (anim->duration() + anim->delay() <= 0 || mode == CSSAnimationData::A
nimateNone) |
| 157 continue; |
| 158 |
| 159 bool animateAll = mode == CSSAnimationData::AnimateAll; |
| 160 ASSERT(animateAll || mode == CSSAnimationData::AnimateSingleProperty); |
| 161 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimations:
:animatableProperties() : shorthandForProperty(anim->property()); |
| 162 if (!propertyList.length()) { |
| 163 listedProperties.add(anim->property()); |
| 164 calculateCandidateTransitionForProperty(anim, anim->property(), oldS
tyle, newStyle, candidateMap); |
| 165 } else { |
| 166 for (unsigned i = 0; i < propertyList.length(); ++i) { |
| 167 CSSPropertyID id = propertyList.properties()[i]; |
| 168 if (!animateAll && !CSSAnimations::isAnimatableProperty(id)) |
| 169 continue; |
| 170 listedProperties.add(id); |
| 171 calculateCandidateTransitionForProperty(anim, id, oldStyle, newS
tyle, candidateMap); |
| 172 } |
| 173 } |
| 174 } |
| 175 } |
| 176 |
| 177 } // namespace |
| 178 |
| 124 CSSAnimationUpdateScope::CSSAnimationUpdateScope(Element* target) | 179 CSSAnimationUpdateScope::CSSAnimationUpdateScope(Element* target) |
| 125 : m_target(target) | 180 : m_target(target) |
| 126 { | 181 { |
| 127 if (!m_target) | 182 if (!m_target) |
| 128 return; | 183 return; |
| 129 ActiveAnimations* activeAnimations = m_target->activeAnimations(); | 184 ActiveAnimations* activeAnimations = m_target->activeAnimations(); |
| 130 CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnima
tions() : 0; | 185 CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnima
tions() : 0; |
| 131 // It's possible than an update was created outside an update scope. That's
harmless | 186 // It's possible than an update was created outside an update scope. That's
harmless |
| 132 // but we must clear it now to avoid applying it if an updated replacement i
s not | 187 // but we must clear it now to avoid applying it if an updated replacement i
s not |
| 133 // created in this scope. | 188 // created in this scope. |
| 134 if (cssAnimations) | 189 if (cssAnimations) |
| 135 cssAnimations->setPendingUpdate(nullptr); | 190 cssAnimations->setPendingUpdate(nullptr); |
| 136 } | 191 } |
| 137 | 192 |
| 138 CSSAnimationUpdateScope::~CSSAnimationUpdateScope() | 193 CSSAnimationUpdateScope::~CSSAnimationUpdateScope() |
| 139 { | 194 { |
| 140 if (!m_target) | 195 if (!m_target) |
| 141 return; | 196 return; |
| 142 ActiveAnimations* activeAnimations = m_target->activeAnimations(); | 197 ActiveAnimations* activeAnimations = m_target->activeAnimations(); |
| 143 CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnima
tions() : 0; | 198 CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cssAnima
tions() : 0; |
| 144 if (cssAnimations) | 199 if (cssAnimations) |
| 145 cssAnimations->maybeApplyPendingUpdate(m_target); | 200 cssAnimations->maybeApplyPendingUpdate(m_target); |
| 146 } | 201 } |
| 147 | 202 |
| 148 bool CSSAnimations::needsUpdate(const Element* element, const RenderStyle* style
) | 203 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element,
const RenderStyle* style, StyleResolver* resolver) |
| 204 { |
| 205 ASSERT(RuntimeEnabledFeatures::webAnimationsCSSEnabled()); |
| 206 OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate()); |
| 207 calculateAnimationUpdate(update.get(), element, style, resolver); |
| 208 calculateTransitionUpdate(update.get(), element, style); |
| 209 return update->isEmpty() ? nullptr : update.release(); |
| 210 } |
| 211 |
| 212 void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, Element
* element, const RenderStyle* style, StyleResolver* resolver) |
| 149 { | 213 { |
| 150 ActiveAnimations* activeAnimations = element->activeAnimations(); | 214 ActiveAnimations* activeAnimations = element->activeAnimations(); |
| 151 const CSSAnimationDataList* animations = style->animations(); | 215 const CSSAnimationDataList* animationDataList = style->animations(); |
| 152 const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cs
sAnimations() : 0; | 216 const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cs
sAnimations() : 0; |
| 153 EDisplay display = style->display(); | |
| 154 return (display != NONE && animations && animations->size()) || (cssAnimatio
ns && !cssAnimations->isEmpty()); | |
| 155 } | |
| 156 | 217 |
| 157 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element,
const RenderStyle* style, const CSSAnimations* cssAnimations, const CSSAnimation
DataList* animationDataList, StyleResolver* resolver) | |
| 158 { | |
| 159 OwnPtr<CSSAnimationUpdate> update; | |
| 160 HashSet<AtomicString> inactive; | 218 HashSet<AtomicString> inactive; |
| 161 if (cssAnimations) | 219 if (cssAnimations) |
| 162 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.beg
in(); iter != cssAnimations->m_animations.end(); ++iter) | 220 for (AnimationMap::const_iterator iter = cssAnimations->m_animations.beg
in(); iter != cssAnimations->m_animations.end(); ++iter) |
| 163 inactive.add(iter->key); | 221 inactive.add(iter->key); |
| 164 | 222 |
| 165 if (style->display() != NONE) { | 223 if (style->display() != NONE) { |
| 166 for (size_t i = 0; animationDataList && i < animationDataList->size(); +
+i) { | 224 for (size_t i = 0; animationDataList && i < animationDataList->size(); +
+i) { |
| 167 const CSSAnimationData* animationData = animationDataList->animation
(i); | 225 const CSSAnimationData* animationData = animationDataList->animation
(i); |
| 168 if (animationData->isNoneAnimation()) | 226 if (animationData->isNoneAnimation()) |
| 169 continue; | 227 continue; |
| 170 ASSERT(animationData->isValidAnimation()); | 228 ASSERT(animationData->isValidAnimation()); |
| 171 AtomicString animationName(animationData->name()); | 229 AtomicString animationName(animationData->name()); |
| 172 | 230 |
| 173 if (cssAnimations) { | 231 if (cssAnimations) { |
| 174 AnimationMap::const_iterator existing(cssAnimations->m_animation
s.find(animationName)); | 232 AnimationMap::const_iterator existing(cssAnimations->m_animation
s.find(animationName)); |
| 175 if (existing != cssAnimations->m_animations.end()) { | 233 if (existing != cssAnimations->m_animations.end()) { |
| 176 // FIXME: The play-state of this animation might have change
d, record the change in the update. | 234 // FIXME: The play-state of this animation might have change
d, record the change in the update. |
| 177 inactive.remove(animationName); | 235 inactive.remove(animationName); |
| 178 continue; | 236 continue; |
| 179 } | 237 } |
| 180 } | 238 } |
| 181 | 239 |
| 182 Timing timing; | 240 Timing timing; |
| 183 RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationDa
ta(animationData, timing); | 241 RefPtr<TimingFunction> defaultTimingFunction = timingFromAnimationDa
ta(animationData, timing); |
| 184 Vector<std::pair<KeyframeAnimationEffect::KeyframeVector, RefPtr<Tim
ingFunction> > > keyframesAndTimingFunctions; | 242 Vector<std::pair<KeyframeAnimationEffect::KeyframeVector, RefPtr<Tim
ingFunction> > > keyframesAndTimingFunctions; |
| 185 resolver->resolveKeyframes(element, style, animationName, defaultTim
ingFunction.get(), keyframesAndTimingFunctions); | 243 resolver->resolveKeyframes(element, style, animationName, defaultTim
ingFunction.get(), keyframesAndTimingFunctions); |
| 186 if (!keyframesAndTimingFunctions.isEmpty()) { | 244 if (!keyframesAndTimingFunctions.isEmpty()) { |
| 187 if (!update) | |
| 188 update = adoptPtr(new CSSAnimationUpdate()); | |
| 189 HashSet<RefPtr<InertAnimation> > animations; | 245 HashSet<RefPtr<InertAnimation> > animations; |
| 190 for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j)
{ | 246 for (size_t j = 0; j < keyframesAndTimingFunctions.size(); ++j)
{ |
| 191 ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty()); | 247 ASSERT(!keyframesAndTimingFunctions[j].first.isEmpty()); |
| 192 timing.timingFunction = keyframesAndTimingFunctions[j].secon
d; | 248 timing.timingFunction = keyframesAndTimingFunctions[j].secon
d; |
| 193 // FIXME: crbug.com/268791 - Keyframes are already normalize
d, perhaps there should be a flag on KeyframeAnimationEffect to skip normalizati
on. | 249 // FIXME: crbug.com/268791 - Keyframes are already normalize
d, perhaps there should be a flag on KeyframeAnimationEffect to skip normalizati
on. |
| 194 animations.add(InertAnimation::create(KeyframeAnimationEffec
t::create(keyframesAndTimingFunctions[j].first), timing)); | 250 animations.add(InertAnimation::create(KeyframeAnimationEffec
t::create(keyframesAndTimingFunctions[j].first), timing)); |
| 195 } | 251 } |
| 196 update->startAnimation(animationName, animations); | 252 update->startAnimation(animationName, animations); |
| 197 } | 253 } |
| 198 } | 254 } |
| 199 } | 255 } |
| 200 | 256 |
| 201 if (!inactive.isEmpty() && !update) | |
| 202 update = adoptPtr(new CSSAnimationUpdate()); | |
| 203 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter !=
inactive.end(); ++iter) | 257 for (HashSet<AtomicString>::const_iterator iter = inactive.begin(); iter !=
inactive.end(); ++iter) |
| 204 update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter)); | 258 update->cancelAnimation(*iter, cssAnimations->m_animations.get(*iter)); |
| 205 | |
| 206 return update.release(); | |
| 207 } | 259 } |
| 208 | 260 |
| 209 void CSSAnimations::maybeApplyPendingUpdate(Element* element) | 261 void CSSAnimations::maybeApplyPendingUpdate(Element* element) |
| 210 { | 262 { |
| 211 if (!element->renderer()) | 263 if (!element->renderer()) |
| 212 m_pendingUpdate = nullptr; | 264 m_pendingUpdate = nullptr; |
| 213 | 265 |
| 214 if (!m_pendingUpdate) | 266 if (!m_pendingUpdate) |
| 215 return; | 267 return; |
| 216 | 268 |
| 217 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); | 269 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); |
| 218 | 270 |
| 219 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationN
ames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) { | 271 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationN
ames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) { |
| 220 const HashSet<RefPtr<Player> >& players = m_animations.take(*iter); | 272 const HashSet<RefPtr<Player> >& players = m_animations.take(*iter); |
| 221 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); it
er != players.end(); ++iter) | 273 for (HashSet<RefPtr<Player> >::const_iterator iter = players.begin(); it
er != players.end(); ++iter) |
| 222 (*iter)->cancel(); | 274 (*iter)->cancel(); |
| 223 } | 275 } |
| 224 | 276 |
| 225 // FIXME: Apply updates to play-state. | 277 // FIXME: Apply updates to play-state. |
| 226 | 278 |
| 227 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update-
>newAnimations().begin(); iter != update->newAnimations().end(); ++iter) { | 279 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update-
>newAnimations().begin(); iter != update->newAnimations().end(); ++iter) { |
| 228 OwnPtr<CSSAnimations::EventDelegate> eventDelegate = adoptPtr(new EventD
elegate(element, iter->name)); | 280 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEve
ntDelegate(element, iter->name)); |
| 229 HashSet<RefPtr<Player> > players; | 281 HashSet<RefPtr<Player> > players; |
| 230 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = i
ter->animations.begin(); animationsIter != iter->animations.end(); ++animationsI
ter) { | 282 for (HashSet<RefPtr<InertAnimation> >::const_iterator animationsIter = i
ter->animations.begin(); animationsIter != iter->animations.end(); ++animationsI
ter) { |
| 231 const InertAnimation* inertAnimation = animationsIter->get(); | 283 const InertAnimation* inertAnimation = animationsIter->get(); |
| 232 // The event delegate is set on the the first animation only. We | 284 // The event delegate is set on the the first animation only. We |
| 233 // rely on the behavior of OwnPtr::release() to achieve this. | 285 // rely on the behavior of OwnPtr::release() to achieve this. |
| 234 RefPtr<Animation> animation = Animation::create(element, inertAnimat
ion->effect(), inertAnimation->specified(), eventDelegate.release()); | 286 RefPtr<Animation> animation = Animation::create(element, inertAnimat
ion->effect(), inertAnimation->specified(), eventDelegate.release()); |
| 235 players.add(element->document().timeline()->play(animation.get())); | 287 players.add(element->document().timeline()->play(animation.get())); |
| 236 } | 288 } |
| 237 m_animations.set(iter->name, players); | 289 m_animations.set(iter->name, players); |
| 238 } | 290 } |
| 291 |
| 292 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().
begin(); iter != update->cancelledTransitions().end(); ++iter) { |
| 293 ASSERT(m_transitions.contains(*iter)); |
| 294 m_transitions.take(*iter).player->cancel(); |
| 295 } |
| 296 |
| 297 for (size_t i = 0; i < update->newTransitions().size(); ++i) { |
| 298 const CSSAnimationUpdate::NewTransition& newTransition = update->newTran
sitions()[i]; |
| 299 |
| 300 RunningTransition runningTransition; |
| 301 runningTransition.from = newTransition.from; |
| 302 runningTransition.to = newTransition.to; |
| 303 |
| 304 CSSPropertyID id = newTransition.id; |
| 305 InertAnimation* inertAnimation = newTransition.animation.get(); |
| 306 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionE
ventDelegate(element, id)); |
| 307 RefPtr<Animation> transition = Animation::create(element, inertAnimation
->effect(), inertAnimation->specified(), eventDelegate.release()); |
| 308 // FIXME: Transitions need to be added to a separate timeline. |
| 309 runningTransition.player = element->document().timeline()->play(transiti
on.get()); |
| 310 m_transitions.set(id, runningTransition); |
| 311 } |
| 312 } |
| 313 |
| 314 void CSSAnimations::calculateTransitionUpdateForProperty(CSSAnimationUpdate* upd
ate, CSSPropertyID id, const CandidateTransition& newTransition, const Transitio
nMap* existingTransitions) |
| 315 { |
| 316 // FIXME: Skip the rest of this if there is a running animation on this prop
erty |
| 317 |
| 318 if (existingTransitions) { |
| 319 TransitionMap::const_iterator existingTransitionIter = existingTransitio
ns->find(id); |
| 320 |
| 321 if (existingTransitionIter != existingTransitions->end() && !update->can
celledTransitions().contains(id)) { |
| 322 const AnimatableValue* existingTo = existingTransitionIter->value.to
; |
| 323 if (newTransition.to->equals(existingTo)) |
| 324 return; |
| 325 update->cancelTransition(id); |
| 326 } |
| 327 } |
| 328 |
| 329 KeyframeAnimationEffect::KeyframeVector keyframes; |
| 330 |
| 331 RefPtr<Keyframe> startKeyframe = Keyframe::create(); |
| 332 startKeyframe->setPropertyValue(id, newTransition.from.get()); |
| 333 startKeyframe->setOffset(0); |
| 334 keyframes.append(startKeyframe); |
| 335 |
| 336 RefPtr<Keyframe> endKeyframe = Keyframe::create(); |
| 337 endKeyframe->setPropertyValue(id, newTransition.to.get()); |
| 338 endKeyframe->setOffset(1); |
| 339 keyframes.append(endKeyframe); |
| 340 |
| 341 RefPtr<KeyframeAnimationEffect> effect = KeyframeAnimationEffect::create(key
frames); |
| 342 |
| 343 Timing timing; |
| 344 RefPtr<TimingFunction> timingFunction = timingFromAnimationData(newTransitio
n.anim, timing); |
| 345 timing.timingFunction = timingFunction; |
| 346 // Note that the backwards part is required for delay to work. |
| 347 timing.fillMode = Timing::FillModeBoth; |
| 348 |
| 349 update->startTransition(id, newTransition.from.get(), newTransition.to.get()
, InertAnimation::create(effect, timing)); |
| 350 } |
| 351 |
| 352 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
Element* element, const RenderStyle* style) |
| 353 { |
| 354 ActiveAnimations* activeAnimations = element->activeAnimations(); |
| 355 const CSSAnimations* cssAnimations = activeAnimations ? activeAnimations->cs
sAnimations() : 0; |
| 356 const TransitionMap* transitions = cssAnimations ? &cssAnimations->m_transit
ions : 0; |
| 357 |
| 358 HashSet<CSSPropertyID> listedProperties; |
| 359 if (style->display() != NONE && element->renderer() && element->renderer()->
style()) { |
| 360 CandidateTransitionMap candidateMap; |
| 361 computeCandidateTransitions(element->renderer()->style(), style, candida
teMap, listedProperties); |
| 362 for (CandidateTransitionMap::const_iterator iter = candidateMap.begin();
iter != candidateMap.end(); ++iter) |
| 363 calculateTransitionUpdateForProperty(update, iter->key, iter->value,
transitions); |
| 364 } |
| 365 |
| 366 if (transitions) { |
| 367 for (TransitionMap::const_iterator iter = transitions->begin(); iter !=
transitions->end(); ++iter) { |
| 368 const TimedItem* timedItem = iter->value.player->source(); |
| 369 CSSPropertyID id = iter->key; |
| 370 if (timedItem->phase() == TimedItem::PhaseAfter || !listedProperties
.contains(id)) |
| 371 update->cancelTransition(id); |
| 372 } |
| 373 } |
| 239 } | 374 } |
| 240 | 375 |
| 241 void CSSAnimations::cancel() | 376 void CSSAnimations::cancel() |
| 242 { | 377 { |
| 243 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animation
s.end(); ++iter) { | 378 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animation
s.end(); ++iter) { |
| 244 const HashSet<RefPtr<Player> >& players = iter->value; | 379 const HashSet<RefPtr<Player> >& players = iter->value; |
| 245 for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.b
egin(); animationsIter != players.end(); ++animationsIter) | 380 for (HashSet<RefPtr<Player> >::const_iterator animationsIter = players.b
egin(); animationsIter != players.end(); ++animationsIter) |
| 246 (*animationsIter)->cancel(); | 381 (*animationsIter)->cancel(); |
| 247 } | 382 } |
| 248 | 383 |
| 384 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transit
ions.end(); ++iter) |
| 385 iter->value.player->cancel(); |
| 386 |
| 249 m_animations.clear(); | 387 m_animations.clear(); |
| 388 m_transitions.clear(); |
| 250 m_pendingUpdate = nullptr; | 389 m_pendingUpdate = nullptr; |
| 251 } | 390 } |
| 252 | 391 |
| 253 void CSSAnimations::EventDelegate::maybeDispatch(Document::ListenerType listener
Type, const AtomicString& eventName, double elapsedTime) | 392 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType
listenerType, const AtomicString& eventName, double elapsedTime) |
| 254 { | 393 { |
| 255 if (m_target->document().hasListenerType(listenerType)) | 394 if (m_target->document().hasListenerType(listenerType)) |
| 256 m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnim
ationEvent::create(eventName, m_name, elapsedTime)); | 395 m_target->document().timeline()->addEventToDispatch(m_target, WebKitAnim
ationEvent::create(eventName, m_name, elapsedTime)); |
| 257 } | 396 } |
| 258 | 397 |
| 259 void CSSAnimations::EventDelegate::onEventCondition(const TimedItem* timedItem,
bool isFirstSample, TimedItem::Phase previousPhase, double previousIteration) | 398 void CSSAnimations::AnimationEventDelegate::onEventCondition(const TimedItem* ti
medItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIter
ation) |
| 260 { | 399 { |
| 261 // Events for a single document are queued and dispatched as a group at | 400 // Events for a single document are queued and dispatched as a group at |
| 262 // the end of DocumentTimeline::serviceAnimations. | 401 // the end of DocumentTimeline::serviceAnimations. |
| 263 // FIXME: Events which are queued outside of serviceAnimations should | 402 // FIXME: Events which are queued outside of serviceAnimations should |
| 264 // trigger a timer to dispatch when control is released. | 403 // trigger a timer to dispatch when control is released. |
| 265 const TimedItem::Phase currentPhase = timedItem->phase(); | 404 const TimedItem::Phase currentPhase = timedItem->phase(); |
| 266 const double currentIteration = timedItem->currentIteration(); | 405 const double currentIteration = timedItem->currentIteration(); |
| 267 | 406 |
| 268 // Note that the elapsedTime is measured from when the animation starts play
ing. | 407 // Note that the elapsedTime is measured from when the animation starts play
ing. |
| 269 if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhas
e == TimedItem::PhaseActive && previousIteration != currentIteration) { | 408 if (!isFirstSample && previousPhase == TimedItem::PhaseActive && currentPhas
e == TimedItem::PhaseActive && previousIteration != currentIteration) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 282 ASSERT(timedItem->specified().startDelay > 0 || isFirstSample); | 421 ASSERT(timedItem->specified().startDelay > 0 || isFirstSample); |
| 283 // The spec states that the elapsed time should be | 422 // The spec states that the elapsed time should be |
| 284 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing | 423 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing |
| 285 // implementation. See crbug.com/279611 | 424 // implementation. See crbug.com/279611 |
| 286 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animati
onstart, 0); | 425 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animati
onstart, 0); |
| 287 } | 426 } |
| 288 if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter))
&& currentPhase == TimedItem::PhaseAfter) | 427 if ((isFirstSample || isEarlierPhase(previousPhase, TimedItem::PhaseAfter))
&& currentPhase == TimedItem::PhaseAfter) |
| 289 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animation
end, timedItem->activeDuration()); | 428 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animation
end, timedItem->activeDuration()); |
| 290 } | 429 } |
| 291 | 430 |
| 431 void CSSAnimations::TransitionEventDelegate::onEventCondition(const TimedItem* t
imedItem, bool isFirstSample, TimedItem::Phase previousPhase, double previousIte
ration) |
| 432 { |
| 433 // Events for a single document are queued and dispatched as a group at |
| 434 // the end of DocumentTimeline::serviceAnimations. |
| 435 // FIXME: Events which are queued outside of serviceAnimations should |
| 436 // trigger a timer to dispatch when control is released. |
| 437 const TimedItem::Phase currentPhase = timedItem->phase(); |
| 438 if (currentPhase == TimedItem::PhaseAfter && (isFirstSample || previousPhase
!= currentPhase) && m_target->document().hasListenerType(Document::TRANSITIONEN
D_LISTENER)) { |
| 439 String propertyName = getPropertyNameString(m_property); |
| 440 const Timing& timing = timedItem->specified(); |
| 441 double elapsedTime = timing.iterationDuration; |
| 442 const AtomicString& eventType = EventTypeNames::transitionend; |
| 443 String pseudoElement = PseudoElement::pseudoElementNameForEvents(m_targe
t->pseudoId()); |
| 444 m_target->document().timeline()->addEventToDispatch(m_target, Transition
Event::create(eventType, propertyName, elapsedTime, pseudoElement)); |
| 445 } |
| 446 } |
| 447 |
| 448 |
| 292 bool CSSAnimations::isAnimatableProperty(CSSPropertyID property) | 449 bool CSSAnimations::isAnimatableProperty(CSSPropertyID property) |
| 293 { | 450 { |
| 294 switch (property) { | 451 switch (property) { |
| 295 case CSSPropertyBackgroundColor: | 452 case CSSPropertyBackgroundColor: |
| 296 case CSSPropertyBackgroundImage: | 453 case CSSPropertyBackgroundImage: |
| 297 case CSSPropertyBackgroundPositionX: | 454 case CSSPropertyBackgroundPositionX: |
| 298 case CSSPropertyBackgroundPositionY: | 455 case CSSPropertyBackgroundPositionY: |
| 299 case CSSPropertyBackgroundSize: | 456 case CSSPropertyBackgroundSize: |
| 300 case CSSPropertyBaselineShift: | 457 case CSSPropertyBaselineShift: |
| 301 case CSSPropertyBorderBottomColor: | 458 case CSSPropertyBorderBottomColor: |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 case CSSPropertyWidth: | 555 case CSSPropertyWidth: |
| 399 case CSSPropertyWordSpacing: | 556 case CSSPropertyWordSpacing: |
| 400 case CSSPropertyZIndex: | 557 case CSSPropertyZIndex: |
| 401 case CSSPropertyZoom: | 558 case CSSPropertyZoom: |
| 402 return true; | 559 return true; |
| 403 default: | 560 default: |
| 404 return false; | 561 return false; |
| 405 } | 562 } |
| 406 } | 563 } |
| 407 | 564 |
| 565 const StylePropertyShorthand& CSSAnimations::animatableProperties() |
| 566 { |
| 567 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); |
| 568 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ()); |
| 569 if (properties.isEmpty()) { |
| 570 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) { |
| 571 CSSPropertyID id = convertToCSSPropertyID(i); |
| 572 // FIXME: This is the only shorthand marked as animatable, |
| 573 // it'll be removed from the list once we switch to the new implemen
tation. |
| 574 if (id == CSSPropertyFlex) |
| 575 continue; |
| 576 if (isAnimatableProperty(id)) |
| 577 properties.append(id); |
| 578 } |
| 579 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, propertie
s.begin(), properties.size()); |
| 580 } |
| 581 return propertyShorthand; |
| 582 } |
| 583 |
| 408 } // namespace WebCore | 584 } // namespace WebCore |
| OLD | NEW |