| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 Google 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 are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 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. | |
| 29 */ | |
| 30 | |
| 31 #include "sky/engine/core/animation/css/CSSAnimations.h" | |
| 32 | |
| 33 #include "gen/sky/core/StylePropertyShorthand.h" | |
| 34 #include "sky/engine/core/animation/ActiveAnimations.h" | |
| 35 #include "sky/engine/core/animation/AnimationTimeline.h" | |
| 36 #include "sky/engine/core/animation/KeyframeEffectModel.h" | |
| 37 #include "sky/engine/core/animation/LegacyStyleInterpolation.h" | |
| 38 #include "sky/engine/core/animation/css/CSSAnimatableValueFactory.h" | |
| 39 #include "sky/engine/core/animation/css/CSSPropertyEquality.h" | |
| 40 #include "sky/engine/core/css/CSSPropertyMetadata.h" | |
| 41 #include "sky/engine/core/css/CSSValueList.h" | |
| 42 #include "sky/engine/core/dom/Element.h" | |
| 43 #include "sky/engine/core/events/AnimationEvent.h" | |
| 44 #include "sky/engine/core/events/TransitionEvent.h" | |
| 45 #include "sky/engine/core/rendering/RenderLayer.h" | |
| 46 #include "sky/engine/core/rendering/RenderObject.h" | |
| 47 #include "sky/engine/core/rendering/style/KeyframeList.h" | |
| 48 #include "sky/engine/platform/animation/TimingFunction.h" | |
| 49 #include "sky/engine/public/platform/Platform.h" | |
| 50 #include "sky/engine/wtf/BitArray.h" | |
| 51 #include "sky/engine/wtf/HashSet.h" | |
| 52 | |
| 53 namespace blink { | |
| 54 | |
| 55 namespace { | |
| 56 | |
| 57 CSSPropertyID propertyForAnimation(CSSPropertyID property) | |
| 58 { | |
| 59 switch (property) { | |
| 60 case CSSPropertyWebkitPerspective: | |
| 61 return CSSPropertyPerspective; | |
| 62 case CSSPropertyWebkitTransform: | |
| 63 return CSSPropertyTransform; | |
| 64 case CSSPropertyWebkitPerspectiveOriginX: | |
| 65 case CSSPropertyWebkitPerspectiveOriginY: | |
| 66 return CSSPropertyPerspectiveOrigin; | |
| 67 case CSSPropertyWebkitTransformOriginX: | |
| 68 case CSSPropertyWebkitTransformOriginY: | |
| 69 case CSSPropertyWebkitTransformOriginZ: | |
| 70 return CSSPropertyTransformOrigin; | |
| 71 default: | |
| 72 break; | |
| 73 } | |
| 74 return property; | |
| 75 } | |
| 76 | |
| 77 } // namespace | |
| 78 | |
| 79 CSSAnimations::CSSAnimations() | |
| 80 { | |
| 81 } | |
| 82 | |
| 83 PassOwnPtr<CSSAnimationUpdate> CSSAnimations::calculateUpdate(Element* element,
const Element& parentElement, const RenderStyle& style, RenderStyle* parentStyle
) | |
| 84 { | |
| 85 OwnPtr<CSSAnimationUpdate> update = adoptPtr(new CSSAnimationUpdate()); | |
| 86 calculateAnimationActiveInterpolations(update.get(), element, parentElement.
document().timeline().currentTimeInternal()); | |
| 87 calculateTransitionUpdate(update.get(), element, style); | |
| 88 calculateTransitionActiveInterpolations(update.get(), element, parentElement
.document().timeline().currentTimeInternal()); | |
| 89 return update->isEmpty() ? nullptr : update.release(); | |
| 90 } | |
| 91 | |
| 92 void CSSAnimations::maybeApplyPendingUpdate(Element* element) | |
| 93 { | |
| 94 if (!m_pendingUpdate) { | |
| 95 m_previousActiveInterpolationsForAnimations.clear(); | |
| 96 return; | |
| 97 } | |
| 98 | |
| 99 OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); | |
| 100 | |
| 101 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolation
sForAnimations()); | |
| 102 | |
| 103 for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationN
ames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) { | |
| 104 RefPtr<AnimationPlayer> player = m_animations.take(*iter); | |
| 105 player->cancel(); | |
| 106 player->update(TimingUpdateOnDemand); | |
| 107 } | |
| 108 | |
| 109 for (Vector<AtomicString>::const_iterator iter = update->animationsWithPause
Toggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) { | |
| 110 AnimationPlayer* player = m_animations.get(*iter); | |
| 111 if (player->paused()) | |
| 112 player->unpause(); | |
| 113 else | |
| 114 player->pause(); | |
| 115 if (player->outdated()) | |
| 116 player->update(TimingUpdateOnDemand); | |
| 117 } | |
| 118 | |
| 119 for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update-
>newAnimations().begin(); iter != update->newAnimations().end(); ++iter) { | |
| 120 const InertAnimation* inertAnimation = iter->animation.get(); | |
| 121 OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEve
ntDelegate(element, iter->name)); | |
| 122 RefPtr<Animation> animation = Animation::create(element, inertAnimation-
>effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventD
elegate.release()); | |
| 123 RefPtr<AnimationPlayer> player = element->document().timeline().createAn
imationPlayer(animation.get()); | |
| 124 if (inertAnimation->paused()) | |
| 125 player->pause(); | |
| 126 player->update(TimingUpdateOnDemand); | |
| 127 m_animations.set(iter->name, player.get()); | |
| 128 } | |
| 129 | |
| 130 // Transitions that are run on the compositor only update main-thread state | |
| 131 // lazily. However, we need the new state to know what the from state shoud | |
| 132 // be when transitions are retargeted. Instead of triggering complete style | |
| 133 // recalculation, we find these cases by searching for new transitions that | |
| 134 // have matching cancelled animation property IDs on the compositor. | |
| 135 HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedComp
ositorTransitions; | |
| 136 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().
begin(); iter != update->cancelledTransitions().end(); ++iter) { | |
| 137 CSSPropertyID id = *iter; | |
| 138 ASSERT(m_transitions.contains(id)); | |
| 139 | |
| 140 RefPtr<AnimationPlayer> player = m_transitions.take(id).player; | |
| 141 player->cancel(); | |
| 142 player->update(TimingUpdateOnDemand); | |
| 143 } | |
| 144 | |
| 145 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->new
Transitions().begin(); iter != update->newTransitions().end(); ++iter) { | |
| 146 const CSSAnimationUpdate::NewTransition& newTransition = iter->value; | |
| 147 | |
| 148 RunningTransition runningTransition; | |
| 149 runningTransition.from = newTransition.from; | |
| 150 runningTransition.to = newTransition.to; | |
| 151 | |
| 152 CSSPropertyID id = newTransition.id; | |
| 153 InertAnimation* inertAnimation = newTransition.animation.get(); | |
| 154 OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionE
ventDelegate(element, newTransition.eventId)); | |
| 155 | |
| 156 RefPtr<AnimationEffect> effect = inertAnimation->effect(); | |
| 157 RefPtr<Animation> transition = Animation::create(element, effect, inertA
nimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.releas
e()); | |
| 158 RefPtr<AnimationPlayer> player = element->document().timeline().createAn
imationPlayer(transition.get()); | |
| 159 player->update(TimingUpdateOnDemand); | |
| 160 runningTransition.player = player; | |
| 161 m_transitions.set(id, runningTransition); | |
| 162 ASSERT(id != CSSPropertyInvalid); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 void CSSAnimations::calculateTransitionUpdateForProperty(CSSPropertyID id, CSSPr
opertyID eventId, const CSSTransitionData& transitionData, size_t transitionInde
x, const RenderStyle& oldStyle, const RenderStyle& style, const TransitionMap* a
ctiveTransitions, CSSAnimationUpdate* update, const Element* element) | |
| 167 { | |
| 168 RefPtr<AnimatableValue> to = nullptr; | |
| 169 if (activeTransitions) { | |
| 170 TransitionMap::const_iterator activeTransitionIter = activeTransitions->
find(id); | |
| 171 if (activeTransitionIter != activeTransitions->end()) { | |
| 172 to = CSSAnimatableValueFactory::create(id, style); | |
| 173 const AnimatableValue* activeTo = activeTransitionIter->value.to; | |
| 174 if (to->equals(activeTo)) | |
| 175 return; | |
| 176 update->cancelTransition(id); | |
| 177 ASSERT(!element->activeAnimations() || !element->activeAnimations()-
>isAnimationStyleChange()); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 if (CSSPropertyEquality::propertiesEqual(id, oldStyle, style)) | |
| 182 return; | |
| 183 if (!to) | |
| 184 to = CSSAnimatableValueFactory::create(id, style); | |
| 185 | |
| 186 RefPtr<AnimatableValue> from = CSSAnimatableValueFactory::create(id, oldStyl
e); | |
| 187 // If we have multiple transitions on the same property, we will use the | |
| 188 // last one since we iterate over them in order. | |
| 189 if (AnimatableValue::usesDefaultInterpolation(to.get(), from.get())) | |
| 190 return; | |
| 191 | |
| 192 Timing timing = transitionData.convertToTiming(transitionIndex); | |
| 193 if (timing.startDelay + timing.iterationDuration <= 0) | |
| 194 return; | |
| 195 | |
| 196 AnimatableValueKeyframeVector keyframes; | |
| 197 | |
| 198 RefPtr<AnimatableValueKeyframe> startKeyframe = AnimatableValueKeyframe::cre
ate(); | |
| 199 startKeyframe->setPropertyValue(id, from.get()); | |
| 200 startKeyframe->setOffset(0); | |
| 201 startKeyframe->setEasing(timing.timingFunction.release()); | |
| 202 timing.timingFunction = LinearTimingFunction::shared(); | |
| 203 keyframes.append(startKeyframe); | |
| 204 | |
| 205 RefPtr<AnimatableValueKeyframe> endKeyframe = AnimatableValueKeyframe::creat
e(); | |
| 206 endKeyframe->setPropertyValue(id, to.get()); | |
| 207 endKeyframe->setOffset(1); | |
| 208 keyframes.append(endKeyframe); | |
| 209 | |
| 210 RefPtr<AnimatableValueKeyframeEffectModel> effect = AnimatableValueKeyframeE
ffectModel::create(keyframes); | |
| 211 update->startTransition(id, eventId, from.get(), to.get(), InertAnimation::c
reate(effect, timing, false)); | |
| 212 ASSERT(!element->activeAnimations() || !element->activeAnimations()->isAnima
tionStyleChange()); | |
| 213 } | |
| 214 | |
| 215 void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate* update, const
Element* element, const RenderStyle& style) | |
| 216 { | |
| 217 if (!element) | |
| 218 return; | |
| 219 | |
| 220 ActiveAnimations* activeAnimations = element->activeAnimations(); | |
| 221 const TransitionMap* activeTransitions = activeAnimations ? &activeAnimation
s->cssAnimations().m_transitions : 0; | |
| 222 const CSSTransitionData* transitionData = style.transitions(); | |
| 223 | |
| 224 #if ENABLE(ASSERT) | |
| 225 // In debug builds we verify that it would have been safe to avoid populatin
g and testing listedProperties if the style recalc is due to animation. | |
| 226 const bool animationStyleRecalc = false; | |
| 227 #else | |
| 228 // In release builds we avoid the cost of checking for new and interrupted t
ransitions if the style recalc is due to animation. | |
| 229 const bool animationStyleRecalc = activeAnimations && activeAnimations->isAn
imationStyleChange(); | |
| 230 #endif | |
| 231 | |
| 232 BitArray<numCSSProperties> listedProperties; | |
| 233 bool anyTransitionHadTransitionAll = false; | |
| 234 const RenderObject* renderer = element->renderer(); | |
| 235 if (!animationStyleRecalc && style.display() != NONE && renderer && renderer
->style() && transitionData) { | |
| 236 const RenderStyle& oldStyle = *renderer->style(); | |
| 237 | |
| 238 for (size_t i = 0; i < transitionData->propertyList().size(); ++i) { | |
| 239 const CSSTransitionData::TransitionProperty& transitionProperty = tr
ansitionData->propertyList()[i]; | |
| 240 CSSTransitionData::TransitionPropertyType mode = transitionProperty.
propertyType; | |
| 241 CSSPropertyID property = transitionProperty.propertyId; | |
| 242 if (mode == CSSTransitionData::TransitionNone || mode == CSSTransiti
onData::TransitionUnknown) | |
| 243 continue; | |
| 244 | |
| 245 bool animateAll = mode == CSSTransitionData::TransitionAll; | |
| 246 ASSERT(animateAll || mode == CSSTransitionData::TransitionSingleProp
erty); | |
| 247 if (animateAll) | |
| 248 anyTransitionHadTransitionAll = true; | |
| 249 const StylePropertyShorthand& propertyList = animateAll ? CSSAnimati
ons::animatableProperties() : shorthandForProperty(property); | |
| 250 // If not a shorthand we only execute one iteration of this loop, an
d refer to the property directly. | |
| 251 for (unsigned j = 0; !j || j < propertyList.length(); ++j) { | |
| 252 CSSPropertyID id = propertyList.length() ? propertyList.properti
es()[j] : property; | |
| 253 CSSPropertyID eventId = id; | |
| 254 | |
| 255 if (!animateAll) { | |
| 256 id = propertyForAnimation(id); | |
| 257 if (CSSPropertyMetadata::isAnimatableProperty(id)) | |
| 258 listedProperties.set(id); | |
| 259 else | |
| 260 continue; | |
| 261 } | |
| 262 | |
| 263 if (!update->activeInterpolationsForAnimations().contains(id) | |
| 264 && (!activeAnimations || !activeAnimations->cssAnimations().
m_previousActiveInterpolationsForAnimations.contains(id))) { | |
| 265 calculateTransitionUpdateForProperty(id, eventId, *transitio
nData, i, oldStyle, style, activeTransitions, update, element); | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 if (activeTransitions) { | |
| 272 for (TransitionMap::const_iterator iter = activeTransitions->begin(); it
er != activeTransitions->end(); ++iter) { | |
| 273 const AnimationPlayer& player = *iter->value.player; | |
| 274 CSSPropertyID id = iter->key; | |
| 275 if (player.finishedInternal() || (!anyTransitionHadTransitionAll &&
!animationStyleRecalc && !listedProperties.get(id))) { | |
| 276 // TODO: Figure out why this fails on Chrome OS login page. crbu
g.com/365507 | |
| 277 // ASSERT(player.finishedInternal() || !(activeAnimations && act
iveAnimations->isAnimationStyleChange())); | |
| 278 update->cancelTransition(id); | |
| 279 } | |
| 280 } | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void CSSAnimations::cancel() | |
| 285 { | |
| 286 for (AnimationMap::iterator iter = m_animations.begin(); iter != m_animation
s.end(); ++iter) { | |
| 287 iter->value->cancel(); | |
| 288 iter->value->update(TimingUpdateOnDemand); | |
| 289 } | |
| 290 | |
| 291 for (TransitionMap::iterator iter = m_transitions.begin(); iter != m_transit
ions.end(); ++iter) { | |
| 292 iter->value.player->cancel(); | |
| 293 iter->value.player->update(TimingUpdateOnDemand); | |
| 294 } | |
| 295 | |
| 296 m_animations.clear(); | |
| 297 m_transitions.clear(); | |
| 298 m_pendingUpdate = nullptr; | |
| 299 } | |
| 300 | |
| 301 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* u
pdate, const Element* element, double timelineCurrentTime) | |
| 302 { | |
| 303 ActiveAnimations* activeAnimations = element ? element->activeAnimations() :
0; | |
| 304 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau
ltStack() : 0; | |
| 305 | |
| 306 HashMap<CSSPropertyID, RefPtr<Interpolation> > activeInterpolationsForAnimat
ions(AnimationStack::activeInterpolations(animationStack, 0, 0, Animation::Defau
ltPriority, timelineCurrentTime)); | |
| 307 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimat
ions); | |
| 308 } | |
| 309 | |
| 310 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate*
update, const Element* element, double timelineCurrentTime) | |
| 311 { | |
| 312 ActiveAnimations* activeAnimations = element ? element->activeAnimations() :
0; | |
| 313 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau
ltStack() : 0; | |
| 314 | |
| 315 HashMap<CSSPropertyID, RefPtr<Interpolation> > activeInterpolationsForTransi
tions; | |
| 316 if (update->newTransitions().isEmpty() && update->cancelledTransitions().isE
mpty()) { | |
| 317 activeInterpolationsForTransitions = AnimationStack::activeInterpolation
s(animationStack, 0, 0, Animation::TransitionPriority, timelineCurrentTime); | |
| 318 } else { | |
| 319 Vector<RawPtr<InertAnimation> > newTransitions; | |
| 320 for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update-
>newTransitions().begin(); iter != update->newTransitions().end(); ++iter) | |
| 321 newTransitions.append(iter->value.animation.get()); | |
| 322 | |
| 323 HashSet<RawPtr<const AnimationPlayer> > cancelledAnimationPlayers; | |
| 324 if (!update->cancelledTransitions().isEmpty()) { | |
| 325 ASSERT(activeAnimations); | |
| 326 const TransitionMap& transitionMap = activeAnimations->cssAnimations
().m_transitions; | |
| 327 for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransi
tions().begin(); iter != update->cancelledTransitions().end(); ++iter) { | |
| 328 ASSERT(transitionMap.contains(*iter)); | |
| 329 cancelledAnimationPlayers.add(transitionMap.get(*iter).player.ge
t()); | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 activeInterpolationsForTransitions = AnimationStack::activeInterpolation
s(animationStack, &newTransitions, &cancelledAnimationPlayers, Animation::Transi
tionPriority, timelineCurrentTime); | |
| 334 } | |
| 335 | |
| 336 // Properties being animated by animations don't get values from transitions
applied. | |
| 337 if (!update->activeInterpolationsForAnimations().isEmpty() && !activeInterpo
lationsForTransitions.isEmpty()) { | |
| 338 for (HashMap<CSSPropertyID, RefPtr<Interpolation> >::const_iterator iter
= update->activeInterpolationsForAnimations().begin(); iter != update->activeIn
terpolationsForAnimations().end(); ++iter) | |
| 339 activeInterpolationsForTransitions.remove(iter->key); | |
| 340 } | |
| 341 update->adoptActiveInterpolationsForTransitions(activeInterpolationsForTrans
itions); | |
| 342 } | |
| 343 | |
| 344 void CSSAnimations::AnimationEventDelegate::maybeDispatch(Document::ListenerType
listenerType, const AtomicString& eventName, double elapsedTime) | |
| 345 { | |
| 346 if (m_target->document().hasListenerType(listenerType)) { | |
| 347 RefPtr<AnimationEvent> event = AnimationEvent::create(eventName, m_name,
elapsedTime); | |
| 348 event->setTarget(m_target); | |
| 349 m_target->document().enqueueAnimationFrameEvent(event); | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 void CSSAnimations::AnimationEventDelegate::onEventCondition(const AnimationNode
* animationNode) | |
| 354 { | |
| 355 const AnimationNode::Phase currentPhase = animationNode->phase(); | |
| 356 const double currentIteration = animationNode->currentIteration(); | |
| 357 | |
| 358 if (m_previousPhase != currentPhase | |
| 359 && (currentPhase == AnimationNode::PhaseActive || currentPhase == Animat
ionNode::PhaseAfter) | |
| 360 && (m_previousPhase == AnimationNode::PhaseNone || m_previousPhase == An
imationNode::PhaseBefore)) { | |
| 361 // The spec states that the elapsed time should be | |
| 362 // 'delay < 0 ? -delay : 0', but we always use 0 to match the existing | |
| 363 // implementation. See crbug.com/279611 | |
| 364 maybeDispatch(Document::ANIMATIONSTART_LISTENER, EventTypeNames::animati
onstart, 0); | |
| 365 } | |
| 366 | |
| 367 if (currentPhase == AnimationNode::PhaseActive && m_previousPhase == current
Phase && m_previousIteration != currentIteration) { | |
| 368 // We fire only a single event for all iterations thast terminate | |
| 369 // between a single pair of samples. See http://crbug.com/275263. For | |
| 370 // compatibility with the existing implementation, this event uses | |
| 371 // the elapsedTime for the first iteration in question. | |
| 372 ASSERT(!std::isnan(animationNode->specifiedTiming().iterationDuration)); | |
| 373 const double elapsedTime = animationNode->specifiedTiming().iterationDur
ation * (m_previousIteration + 1); | |
| 374 maybeDispatch(Document::ANIMATIONITERATION_LISTENER, EventTypeNames::ani
mationiteration, elapsedTime); | |
| 375 } | |
| 376 | |
| 377 if (currentPhase == AnimationNode::PhaseAfter && m_previousPhase != Animatio
nNode::PhaseAfter) | |
| 378 maybeDispatch(Document::ANIMATIONEND_LISTENER, EventTypeNames::animation
end, animationNode->activeDurationInternal()); | |
| 379 | |
| 380 m_previousPhase = currentPhase; | |
| 381 m_previousIteration = currentIteration; | |
| 382 } | |
| 383 | |
| 384 void CSSAnimations::TransitionEventDelegate::onEventCondition(const AnimationNod
e* animationNode) | |
| 385 { | |
| 386 const AnimationNode::Phase currentPhase = animationNode->phase(); | |
| 387 if (currentPhase == AnimationNode::PhaseAfter && currentPhase != m_previousP
hase && m_target->document().hasListenerType(Document::TRANSITIONEND_LISTENER))
{ | |
| 388 String propertyName = getPropertyNameString(m_property); | |
| 389 const Timing& timing = animationNode->specifiedTiming(); | |
| 390 double elapsedTime = timing.iterationDuration; | |
| 391 const AtomicString& eventType = EventTypeNames::transitionend; | |
| 392 RefPtr<TransitionEvent> event = TransitionEvent::create(eventType, prope
rtyName, elapsedTime); | |
| 393 event->setTarget(m_target); | |
| 394 m_target->document().enqueueAnimationFrameEvent(event); | |
| 395 } | |
| 396 | |
| 397 m_previousPhase = currentPhase; | |
| 398 } | |
| 399 | |
| 400 const StylePropertyShorthand& CSSAnimations::animatableProperties() | |
| 401 { | |
| 402 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); | |
| 403 DEFINE_STATIC_LOCAL(StylePropertyShorthand, propertyShorthand, ()); | |
| 404 if (properties.isEmpty()) { | |
| 405 for (int i = firstCSSProperty; i < lastCSSProperty; ++i) { | |
| 406 CSSPropertyID id = convertToCSSPropertyID(i); | |
| 407 if (CSSPropertyMetadata::isAnimatableProperty(id)) | |
| 408 properties.append(id); | |
| 409 } | |
| 410 propertyShorthand = StylePropertyShorthand(CSSPropertyInvalid, propertie
s.begin(), properties.size()); | |
| 411 } | |
| 412 return propertyShorthand; | |
| 413 } | |
| 414 | |
| 415 // Animation properties are not allowed to be affected by Web Animations. | |
| 416 // http://dev.w3.org/fxtf/web-animations/#not-animatable | |
| 417 bool CSSAnimations::isAllowedAnimation(CSSPropertyID property) | |
| 418 { | |
| 419 switch (property) { | |
| 420 case CSSPropertyAnimation: | |
| 421 case CSSPropertyAnimationDelay: | |
| 422 case CSSPropertyAnimationDirection: | |
| 423 case CSSPropertyAnimationDuration: | |
| 424 case CSSPropertyAnimationFillMode: | |
| 425 case CSSPropertyAnimationIterationCount: | |
| 426 case CSSPropertyAnimationName: | |
| 427 case CSSPropertyAnimationPlayState: | |
| 428 case CSSPropertyAnimationTimingFunction: | |
| 429 case CSSPropertyDisplay: | |
| 430 case CSSPropertyTransition: | |
| 431 case CSSPropertyTransitionDelay: | |
| 432 case CSSPropertyTransitionDuration: | |
| 433 case CSSPropertyTransitionProperty: | |
| 434 case CSSPropertyTransitionTimingFunction: | |
| 435 return false; | |
| 436 default: | |
| 437 return true; | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 } // namespace blink | |
| OLD | NEW |