MIDIOutputMap* MIDIAccess::outputs() const { HeapHashMap<String, Member<MIDIOutput> > outputs; size_t inactiveCount = 0; for (size_t i = 0; i < m_outputs.size(); ++i) { MIDIOutput* output = m_outputs[i]; if (output->isActive()) outputs.add(output->id(), output); else inactiveCount++; } if ((outputs.size() + inactiveCount) != m_outputs.size()) { // There is id duplication that violates the spec. outputs.clear(); } return new MIDIOutputMap(outputs); }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { m_previousActiveInterpolationsForAnimations.clear(); if (m_pendingUpdate.isEmpty()) return; m_previousActiveInterpolationsForAnimations.swap(m_pendingUpdate.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 : m_pendingUpdate.cancelledAnimationNames()) { Animation* animation = m_animations.take(animationName)->animation; animation->cancel(); animation->update(TimingUpdateOnDemand); } for (const AtomicString& animationName : m_pendingUpdate.animationsWithPauseToggled()) { Animation* animation = m_animations.get(animationName)->animation.get(); if (animation->paused()) animation->unpause(); else animation->pause(); if (animation->outdated()) animation->update(TimingUpdateOnDemand); } for (const auto& animation : m_pendingUpdate.updatedCompositorKeyframes()) animation->setCompositorPending(true); for (const auto& entry : m_pendingUpdate.animationsWithUpdates()) { KeyframeEffect* effect = toKeyframeEffect(entry.animation->effect()); effect->setModel(entry.effect->model()); effect->updateSpecifiedTiming(entry.effect->specifiedTiming()); m_animations.find(entry.name)->value->update(entry); } for (const auto& entry : m_pendingUpdate.newAnimations()) { const InertEffect* inertAnimation = entry.effect.get(); AnimationEventDelegate* eventDelegate = new AnimationEventDelegate(element, entry.name); KeyframeEffect* effect = KeyframeEffect::create(element, inertAnimation->model(), inertAnimation->specifiedTiming(), KeyframeEffect::DefaultPriority, eventDelegate); effect->setName(inertAnimation->name()); Animation* animation = element->document().timeline().play(effect); if (inertAnimation->paused()) animation->pause(); animation->update(TimingUpdateOnDemand); m_animations.set(entry.name, new RunningAnimation(animation, entry)); } // 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. HeapHashMap<CSSPropertyID, std::pair<Member<KeyframeEffect>, double>> retargetedCompositorTransitions; for (CSSPropertyID id : m_pendingUpdate.cancelledTransitions()) { ASSERT(m_transitions.contains(id)); Animation* animation = m_transitions.take(id).animation; KeyframeEffect* effect = toKeyframeEffect(animation->effect()); if (effect->hasActiveAnimationsOnCompositor(id) && m_pendingUpdate.newTransitions().find(id) != m_pendingUpdate.newTransitions().end() && !animation->limited()) retargetedCompositorTransitions.add(id, std::pair<KeyframeEffect*, double>(effect, animation->startTimeInternal())); animation->cancel(); // after cancelation, transitions must be downgraded or they'll fail // to be considered when retriggering themselves. This can happen if // the transition is captured through getAnimations then played. if (animation->effect() && animation->effect()->isKeyframeEffect()) toKeyframeEffect(animation->effect())->downgradeToNormal(); animation->update(TimingUpdateOnDemand); } for (CSSPropertyID id : m_pendingUpdate.finishedTransitions()) { // This transition can also be cancelled and finished at the same time if (m_transitions.contains(id)) { Animation* animation = m_transitions.take(id).animation; // Transition must be downgraded if (animation->effect() && animation->effect()->isKeyframeEffect()) toKeyframeEffect(animation->effect())->downgradeToNormal(); } } for (const auto& entry : m_pendingUpdate.newTransitions()) { const CSSAnimationUpdate::NewTransition& newTransition = entry.value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertEffect* inertAnimation = newTransition.effect.get(); TransitionEventDelegate* eventDelegate = new TransitionEventDelegate(element, id); EffectModel* model = inertAnimation->model(); if (retargetedCompositorTransitions.contains(id)) { const std::pair<Member<KeyframeEffect>, double>& oldTransition = retargetedCompositorTransitions.get(id); KeyframeEffect* oldAnimation = oldTransition.first; double oldStartTime = oldTransition.second; double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime; AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->model()); const KeyframeVector& frames = oldEffect->getFrames(); AnimatableValueKeyframeVector newFrames; newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[2]->clone().get())); newFrames[0]->clearPropertyValue(id); newFrames[1]->clearPropertyValue(id); InertEffect* inertAnimationForSampling = InertEffect::create(oldAnimation->model(), oldAnimation->specifiedTiming(), false, inheritedTime); OwnPtr<Vector<RefPtr<Interpolation>>> sample = nullptr; inertAnimationForSampling->sample(sample); if (sample && sample->size() == 1) { newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); newFrames[1]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); model = AnimatableValueKeyframeEffectModel::create(newFrames); } } KeyframeEffect* transition = KeyframeEffect::create(element, model, inertAnimation->specifiedTiming(), KeyframeEffect::TransitionPriority, eventDelegate); transition->setName(inertAnimation->name()); Animation* animation = element->document().timeline().play(transition); // Set the current time as the start time for retargeted transitions if (retargetedCompositorTransitions.contains(id)) animation->setStartTime(element->document().timeline().currentTime()); animation->update(TimingUpdateOnDemand); runningTransition.animation = animation; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id)); } clearPendingUpdate(); }