TEST_F(AnimationAnimationStackTest, NewAnimations) { play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 15); play(makeAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(2))).get(), 10); WillBeHeapVector<RawPtrWillBeMember<InertAnimation>> newAnimations; RefPtrWillBeRawPtr<InertAnimation> inert1 = makeInertAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(3))); RefPtrWillBeRawPtr<InertAnimation> inert2 = makeInertAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(4))); newAnimations.append(inert1.get()); newAnimations.append(inert2.get()); WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> result = AnimationStack::activeInterpolations(&element->elementAnimations()->defaultStack(), &newAnimations, 0, Animation::DefaultPriority, 10); EXPECT_EQ(2u, result.size()); EXPECT_TRUE(interpolationValue(result.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(3).get())); EXPECT_TRUE(interpolationValue(result.get(CSSPropertyZIndex))->equals(AnimatableDouble::create(4).get())); }
void HTMLImport::recalcTreeState(HTMLImport* root) { WillBeHeapHashMap<RawPtrWillBeMember<HTMLImport>, HTMLImportState> snapshot; WillBeHeapVector<RawPtrWillBeMember<HTMLImport>> updated; for (HTMLImport* i = root; i; i = traverseNext(i)) { snapshot.add(i, i->state()); i->m_state = HTMLImportState::invalidState(); } // The post-visit DFS order matters here because // HTMLImportStateResolver in recalcState() Depends on // |m_state| of its children and precedents of ancestors. // Accidental cycle dependency of state computation is prevented // by invalidateCachedState() and isStateCacheValid() check. for (HTMLImport* i = traverseFirstPostOrder(root); i; i = traverseNextPostOrder(i)) { ASSERT(!i->m_state.isValid()); i->m_state = HTMLImportStateResolver(i).resolve(); HTMLImportState newState = i->state(); HTMLImportState oldState = snapshot.get(i); // Once the state reaches Ready, it shouldn't go back. ASSERT(!oldState.isReady() || oldState <= newState); if (newState != oldState) updated.append(i); } for (size_t i = 0; i < updated.size(); ++i) updated[i]->stateDidChange(); }
TEST_F(AnimationAnimationStackTest, ElementAnimationsSorted) { play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 10); play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(2))).get(), 15); play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(3))).get(), 5); WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> result = AnimationStack::activeInterpolations(&element->elementAnimations()->defaultStack(), 0, 0, Animation::DefaultPriority, 0); EXPECT_EQ(1u, result.size()); EXPECT_TRUE(interpolationValue(result.get(CSSPropertyFontSize))->equals(AnimatableDouble::create(3).get())); }
TEST_F(AnimationAnimationStackTest, CancelledAnimationPlayers) { WillBeHeapHashSet<RawPtrWillBeMember<const AnimationPlayer>> cancelledAnimationPlayers; RefPtrWillBeRawPtr<AnimationPlayer> player = play(makeAnimation(makeAnimationEffect(CSSPropertyFontSize, AnimatableDouble::create(1))).get(), 0); cancelledAnimationPlayers.add(player.get()); play(makeAnimation(makeAnimationEffect(CSSPropertyZIndex, AnimatableDouble::create(2))).get(), 0); WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> result = AnimationStack::activeInterpolations(&element->elementAnimations()->defaultStack(), 0, &cancelledAnimationPlayers, Animation::DefaultPriority, 0); EXPECT_EQ(1u, result.size()); EXPECT_TRUE(interpolationValue(result.get(CSSPropertyZIndex))->equals(AnimatableDouble::create(2).get())); }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { if (!m_pendingUpdate) { m_previousActiveInterpolationsForAnimations.clear(); return; } OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations()); // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here // since we call this from recalc style. // https://code.google.com/p/chromium/issues/detail?id=339847 DisableCompositingQueryAsserts disabler; for (const AtomicString& animationName : update->cancelledAnimationNames()) { RefPtrWillBeRawPtr<AnimationPlayer> player = m_animations.take(animationName); player->cancel(); player->update(TimingUpdateOnDemand); } for (const AtomicString& animationName : update->animationsWithPauseToggled()) { AnimationPlayer* player = m_animations.get(animationName); if (player->paused()) player->unpause(); else player->pause(); if (player->outdated()) player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newAnimations()) { const InertAnimation* inertAnimation = entry.animation.get(); OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, entry.name)); RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release()); animation->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get()); if (inertAnimation->paused()) player->pause(); player->update(TimingUpdateOnDemand); m_animations.set(entry.name, player.get()); } // Transitions that are run on the compositor only update main-thread state // lazily. However, we need the new state to know what the from state shoud // be when transitions are retargeted. Instead of triggering complete style // recalculation, we find these cases by searching for new transitions that // have matching cancelled animation property IDs on the compositor. WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double>> retargetedCompositorTransitions; for (CSSPropertyID id : update->cancelledTransitions()) { ASSERT(m_transitions.contains(id)); RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player; Animation* animation = toAnimation(player->source()); if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end()) retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal())); player->cancel(); player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newTransitions()) { const CSSAnimationUpdate::NewTransition& newTransition = entry.value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertAnimation* inertAnimation = newTransition.animation.get(); OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId)); RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect(); if (retargetedCompositorTransitions.contains(id)) { const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id); RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first; double oldStartTime = oldTransition.second; double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime; AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect()); const KeyframeVector& frames = oldEffect->getFrames(); AnimatableValueKeyframeVector newFrames; newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get())); newFrames[0]->clearPropertyValue(id); RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false); OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation>>> sample = inertAnimationForSampling->sample(inheritedTime); ASSERT(sample->size() == 1); newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); effect = AnimatableValueKeyframeEffectModel::create(newFrames); } RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release()); transition->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get()); player->update(TimingUpdateOnDemand); runningTransition.player = player; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id)); } }