void KeyframeEffectReadOnly::UnregisterTarget() { EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType); if (effectSet) { effectSet->RemoveEffect(*this); if (effectSet->IsEmpty()) { EffectSet::DestroyEffectSet(mTarget->mElement, mTarget->mPseudoType); } } }
// Helper function to factor out the common logic from // GetAnimationsForCompositor and HasAnimationsForCompositor. // // Takes an optional array to fill with eligible animations. // // Returns true if there are eligible animations, false otherwise. bool FindAnimationsForCompositor(const nsIFrame* aFrame, nsCSSProperty aProperty, nsTArray<RefPtr<dom::Animation>>* aMatches /*out*/) { MOZ_ASSERT(!aMatches || aMatches->IsEmpty(), "Matches array, if provided, should be empty"); EffectSet* effects = EffectSet::GetEffectSet(aFrame); if (!effects || effects->IsEmpty()) { return false; } if (aFrame->RefusedAsyncAnimation()) { return false; } // The animation cascade will almost always be up-to-date by this point // but there are some cases such as when we are restoring the refresh driver // from test control after seeking where it might not be the case. // // Those cases are probably not important but just to be safe, let's make // sure the cascade is up to date since if it *is* up to date, this is // basically a no-op. Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>> pseudoElement = EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame); if (pseudoElement) { EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->first(), pseudoElement->second(), aFrame->StyleContext()); } if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) { if (nsLayoutUtils::IsAnimationLoggingEnabled()) { nsCString message; message.AppendLiteral("Performance warning: Async animations are " "disabled"); AnimationUtils::LogAsyncAnimationFailure(message); } return false; } bool foundSome = false; for (KeyframeEffectReadOnly* effect : *effects) { MOZ_ASSERT(effect && effect->GetAnimation()); Animation* animation = effect->GetAnimation(); if (!animation->IsPlaying()) { continue; } if (effect->ShouldBlockCompositorAnimations(aFrame)) { if (aMatches) { aMatches->Clear(); } return false; } if (!effect->HasAnimationOfProperty(aProperty)) { continue; } if (aMatches) { aMatches->AppendElement(animation); } foundSome = true; } MOZ_ASSERT(!foundSome || !aMatches || !aMatches->IsEmpty(), "If return value is true, matches array should be non-empty"); return foundSome; }
/* static */ void EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet, Element* aElement, nsCSSPseudoElements::Type aPseudoType, nsStyleContext* aStyleContext) { MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet, "Effect set should correspond to the specified (pseudo-)element"); if (aEffectSet.IsEmpty()) { aEffectSet.MarkCascadeUpdated(); return; } // Get a list of effects sorted by composite order. nsTArray<KeyframeEffectReadOnly*> sortedEffectList; for (KeyframeEffectReadOnly* effect : aEffectSet) { sortedEffectList.AppendElement(effect); } sortedEffectList.Sort(EffectCompositeOrderComparator()); // Get properties that override the *animations* level of the cascade. // // We only do this for properties that we can animate on the compositor // since we will apply other properties on the main thread where the usual // cascade applies. nsCSSPropertySet overriddenProperties; if (aStyleContext) { GetOverriddenProperties(aStyleContext, aEffectSet, overriddenProperties); } bool changed = false; nsCSSPropertySet animatedProperties; // Iterate from highest to lowest composite order. for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) { MOZ_ASSERT(effect->GetAnimation(), "Effects on a target element should have an Animation"); bool inEffect = effect->IsInEffect(); for (AnimationProperty& prop : effect->Properties()) { bool winsInCascade = !animatedProperties.HasProperty(prop.mProperty) && inEffect; // If this property wins in the cascade, add it to the set of animated // properties. We need to do this even if the property is overridden // (in which case we set winsInCascade to false below) since we don't // want to fire transitions on these properties. if (winsInCascade) { animatedProperties.AddProperty(prop.mProperty); } // For effects that will be applied to the animations level of the // cascade, we need to check that the property isn't being set by // something with higher priority in the cascade. // // We only do this, however, for properties that can be animated on // the compositor. For properties animated on the main thread the usual // cascade ensures these animations will be correctly overridden. if (winsInCascade && effect->GetAnimation()->CascadeLevel() == CascadeLevel::Animations && overriddenProperties.HasProperty(prop.mProperty)) { winsInCascade = false; } if (winsInCascade != prop.mWinsInCascade) { changed = true; } prop.mWinsInCascade = winsInCascade; } } aEffectSet.MarkCascadeUpdated(); // If there is any change in the cascade result, update animations on // layers with the winning animations. nsPresContext* presContext = GetPresContext(aElement); if (changed && presContext) { // We currently unconditionally update both animations and transitions // even if we could, for example, get away with only updating animations. // This is a temporary measure until we unify all animation style updating // under EffectCompositor. AnimationCollection* animations = presContext->AnimationManager()->GetAnimationCollection(aElement, aPseudoType, false); /* don't create */ if (animations) { animations->RequestRestyle(AnimationCollection::RestyleType::Layer); } AnimationCollection* transitions = presContext->TransitionManager()->GetAnimationCollection(aElement, aPseudoType, false); /* don't create */ if (transitions) { transitions->RequestRestyle(AnimationCollection::RestyleType::Layer); } } }