/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"AnimationCommon.h"#include"nsTransitionManager.h"#include"nsAnimationManager.h"#include"ActiveLayerTracker.h"#include"gfxPlatform.h"#include"nsRuleData.h"#include"nsCSSPropertySet.h"#include"nsCSSValue.h"#include"nsCycleCollectionParticipant.h"#include"nsDOMMutationObserver.h"#include"nsStyleContext.h"#include"nsIFrame.h"#include"nsLayoutUtils.h"#include"mozilla/LookAndFeel.h"#include"Layers.h"#include"FrameLayerBuilder.h"#include"nsDisplayList.h"#include"mozilla/MemoryReporting.h"#include"mozilla/dom/KeyframeEffect.h"#include"RestyleManager.h"#include"nsRuleProcessorData.h"#include"nsStyleSet.h"#include"nsStyleChangeList.h"usingmozilla::layers::Layer;usingmozilla::dom::Animation;usingmozilla::dom::KeyframeEffectReadOnly;namespacemozilla{/* static */boolIsGeometricProperty(nsCSSPropertyaProperty){switch(aProperty){caseeCSSProperty_bottom:caseeCSSProperty_height:caseeCSSProperty_left:caseeCSSProperty_right:caseeCSSProperty_top:caseeCSSProperty_width:returntrue;default:returnfalse;}}CommonAnimationManager::CommonAnimationManager(nsPresContext*aPresContext):mPresContext(aPresContext),mIsObservingRefreshDriver(false){}CommonAnimationManager::~CommonAnimationManager(){MOZ_ASSERT(!mPresContext,"Disconnect should have been called");}voidCommonAnimationManager::Disconnect(){// Content nodes might outlive the transition or animation manager.RemoveAllElementCollections();mPresContext=nullptr;}voidCommonAnimationManager::AddElementCollection(AnimationCollection*aCollection){if(!mIsObservingRefreshDriver){NS_ASSERTION(aCollection->mNeedsRefreshes,"Added data which doesn't need refreshing?");// We need to observe the refresh driver.mPresContext->RefreshDriver()->AddRefreshObserver(this,Flush_Style);mIsObservingRefreshDriver=true;}mElementCollections.insertBack(aCollection);}voidCommonAnimationManager::RemoveAllElementCollections(){while(AnimationCollection*head=mElementCollections.getFirst()){head->Destroy();// Note: this removes 'head' from mElementCollections.}}voidCommonAnimationManager::MaybeStartObservingRefreshDriver(){if(mIsObservingRefreshDriver||!NeedsRefresh()){return;}mPresContext->RefreshDriver()->AddRefreshObserver(this,Flush_Style);mIsObservingRefreshDriver=true;}voidCommonAnimationManager::MaybeStartOrStopObservingRefreshDriver(){boolneedsRefresh=NeedsRefresh();if(needsRefresh&&!mIsObservingRefreshDriver){mPresContext->RefreshDriver()->AddRefreshObserver(this,Flush_Style);}elseif(!needsRefresh&&mIsObservingRefreshDriver){mPresContext->RefreshDriver()->RemoveRefreshObserver(this,Flush_Style);}mIsObservingRefreshDriver=needsRefresh;}boolCommonAnimationManager::NeedsRefresh()const{for(constAnimationCollection*collection=mElementCollections.getFirst();collection;collection=collection->getNext()){if(collection->mNeedsRefreshes){returntrue;}}returnfalse;}AnimationCollection*CommonAnimationManager::GetAnimationCollection(constnsIFrame*aFrame){nsIContent*content=aFrame->GetContent();if(!content){returnnullptr;}nsIAtom*animProp;if(aFrame->IsGeneratedContentFrame()){nsIFrame*parent=aFrame->GetParent();if(parent->IsGeneratedContentFrame()){returnnullptr;}nsIAtom*name=content->NodeInfo()->NameAtom();if(name==nsGkAtoms::mozgeneratedcontentbefore){animProp=GetAnimationsBeforeAtom();}elseif(name==nsGkAtoms::mozgeneratedcontentafter){animProp=GetAnimationsAfterAtom();}else{returnnullptr;}content=content->GetParent();if(!content){returnnullptr;}}else{if(!content->MayHaveAnimations()){returnnullptr;}animProp=GetAnimationsAtom();}returnstatic_cast<AnimationCollection*>(content->GetProperty(animProp));}AnimationCollection*CommonAnimationManager::GetAnimationsForCompositor(constnsIFrame*aFrame,nsCSSPropertyaProperty){AnimationCollection*collection=GetAnimationCollection(aFrame);if(!collection||!collection->HasCurrentAnimationOfProperty(aProperty)||!collection->CanPerformOnCompositorThread(AnimationCollection::CanAnimate_AllowPartial)){returnnullptr;}// This animation can be done on the compositor.returncollection;}nsRestyleHintCommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData*aData){returnnsRestyleHint(0);}nsRestyleHintCommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData*aData){returnnsRestyleHint(0);}boolCommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData*aData){returnfalse;}nsRestyleHintCommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData*aData,RestyleHintData&aRestyleHintDataResult){returnnsRestyleHint(0);}/* virtual */boolCommonAnimationManager::MediumFeaturesChanged(nsPresContext*aPresContext){returnfalse;}/* virtual */voidCommonAnimationManager::RulesMatching(ElementRuleProcessorData*aData){MOZ_ASSERT(aData->mPresContext==mPresContext,"pres context mismatch");nsIStyleRule*rule=GetAnimationRule(aData->mElement,nsCSSPseudoElements::ePseudo_NotPseudoElement);if(rule){aData->mRuleWalker->Forward(rule);aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();}}/* virtual */voidCommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData*aData){MOZ_ASSERT(aData->mPresContext==mPresContext,"pres context mismatch");if(aData->mPseudoType!=nsCSSPseudoElements::ePseudo_before&&aData->mPseudoType!=nsCSSPseudoElements::ePseudo_after){return;}// FIXME: Do we really want to be the only thing keeping a// pseudo-element alive? I *think* the non-animation restyle should// handle that, but should add a test.nsIStyleRule*rule=GetAnimationRule(aData->mElement,aData->mPseudoType);if(rule){aData->mRuleWalker->Forward(rule);aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();}}/* virtual */voidCommonAnimationManager::RulesMatching(AnonBoxRuleProcessorData*aData){}#ifdef MOZ_XUL/* virtual */voidCommonAnimationManager::RulesMatching(XULTreeRuleProcessorData*aData){}#endif/* virtual */size_tCommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOfaMallocSizeOf)const{// Measurement of the following members may be added later if DMD finds it is// worthwhile:// - mElementCollections//// The following members are not measured// - mPresContext, because it's non-owningreturn0;}/* virtual */size_tCommonAnimationManager::SizeOfIncludingThis(mozilla::MallocSizeOfaMallocSizeOf)const{returnaMallocSizeOf(this)+SizeOfExcludingThis(aMallocSizeOf);}voidCommonAnimationManager::AddStyleUpdatesTo(RestyleTracker&aTracker){TimeStampnow=mPresContext->RefreshDriver()->MostRecentRefresh();for(AnimationCollection*collection=mElementCollections.getFirst();collection;collection=collection->getNext()){collection->EnsureStyleRuleFor(now);dom::Element*elementToRestyle=collection->GetElementToRestyle();if(elementToRestyle){nsRestyleHintrshint=collection->IsForTransitions()?eRestyle_CSSTransitions:eRestyle_CSSAnimations;aTracker.AddPendingRestyle(elementToRestyle,rshint,nsChangeHint(0));}}}/* static */boolCommonAnimationManager::ExtractComputedValueForTransition(nsCSSPropertyaProperty,nsStyleContext*aStyleContext,StyleAnimationValue&aComputedValue){boolresult=StyleAnimationValue::ExtractComputedValue(aProperty,aStyleContext,aComputedValue);if(aProperty==eCSSProperty_visibility){MOZ_ASSERT(aComputedValue.GetUnit()==StyleAnimationValue::eUnit_Enumerated,"unexpected unit");aComputedValue.SetIntValue(aComputedValue.GetIntValue(),StyleAnimationValue::eUnit_Visibility);}returnresult;}AnimationCollection*CommonAnimationManager::GetAnimations(dom::Element*aElement,nsCSSPseudoElements::TypeaPseudoType,boolaCreateIfNeeded){if(!aCreateIfNeeded&&mElementCollections.isEmpty()){// Early return for the most common case.returnnullptr;}nsIAtom*propName;if(aPseudoType==nsCSSPseudoElements::ePseudo_NotPseudoElement){propName=GetAnimationsAtom();}elseif(aPseudoType==nsCSSPseudoElements::ePseudo_before){propName=GetAnimationsBeforeAtom();}elseif(aPseudoType==nsCSSPseudoElements::ePseudo_after){propName=GetAnimationsAfterAtom();}else{NS_ASSERTION(!aCreateIfNeeded,"should never try to create transitions for pseudo ""other than :before or :after");returnnullptr;}AnimationCollection*collection=static_cast<AnimationCollection*>(aElement->GetProperty(propName));if(!collection&&aCreateIfNeeded){// FIXME: Consider arena-allocating?collection=newAnimationCollection(aElement,propName,this);nsresultrv=aElement->SetProperty(propName,collection,&AnimationCollection::PropertyDtor,false);if(NS_FAILED(rv)){NS_WARNING("SetProperty failed");// The collection must be destroyed via PropertyDtor, otherwise// mCalledPropertyDtor assertion is triggered in destructor.AnimationCollection::PropertyDtor(aElement,propName,collection,nullptr);returnnullptr;}if(aPseudoType==nsCSSPseudoElements::ePseudo_NotPseudoElement){aElement->SetMayHaveAnimations();}AddElementCollection(collection);}returncollection;}voidCommonAnimationManager::FlushAnimations(){TimeStampnow=mPresContext->RefreshDriver()->MostRecentRefresh();for(AnimationCollection*collection=mElementCollections.getFirst();collection;collection=collection->getNext()){if(collection->mStyleRuleRefreshTime==now){continue;}collection->RequestRestyle(AnimationCollection::RestyleType::Standard);}}nsIStyleRule*CommonAnimationManager::GetAnimationRule(mozilla::dom::Element*aElement,nsCSSPseudoElements::TypeaPseudoType){MOZ_ASSERT(aPseudoType==nsCSSPseudoElements::ePseudo_NotPseudoElement||aPseudoType==nsCSSPseudoElements::ePseudo_before||aPseudoType==nsCSSPseudoElements::ePseudo_after,"forbidden pseudo type");if(!mPresContext->IsDynamic()){// For print or print preview, ignore animations.returnnullptr;}AnimationCollection*collection=GetAnimations(aElement,aPseudoType,false);if(!collection){returnnullptr;}RestyleManager*restyleManager=mPresContext->RestyleManager();if(restyleManager->SkipAnimationRules()){returnnullptr;}collection->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh());returncollection->mStyleRule;}/* static */constCommonAnimationManager::LayerAnimationRecordCommonAnimationManager::sLayerAnimationInfo[]={{eCSSProperty_transform,nsDisplayItem::TYPE_TRANSFORM,nsChangeHint_UpdateTransformLayer},{eCSSProperty_opacity,nsDisplayItem::TYPE_OPACITY,nsChangeHint_UpdateOpacityLayer}};/* static */constCommonAnimationManager::LayerAnimationRecord*CommonAnimationManager::LayerAnimationRecordFor(nsCSSPropertyaProperty){MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),"unexpected property");constauto&info=sLayerAnimationInfo;for(size_ti=0;i<ArrayLength(info);++i){if(aProperty==info[i].mProperty){return&info[i];}}returnnullptr;}/* virtual */voidCommonAnimationManager::WillRefresh(TimeStampaTime){MOZ_ASSERT(mPresContext,"refresh driver should not notify additional observers ""after pres context has been destroyed");if(!mPresContext->GetPresShell()){// Someone might be keeping mPresContext alive past the point// where it has been torn down; don't bother doing anything in// this case. But do get rid of all our animations so we stop// triggering refreshes.RemoveAllElementCollections();return;}nsAutoAnimationMutationBatchmb(mPresContext->Document());for(AnimationCollection*collection=mElementCollections.getFirst();collection;collection=collection->getNext()){collection->Tick();}MaybeStartOrStopObservingRefreshDriver();}#ifdef DEBUG/* static */voidCommonAnimationManager::Initialize(){constauto&info=CommonAnimationManager::sLayerAnimationInfo;for(size_ti=0;i<ArrayLength(info);i++){autorecord=info[i];MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty,CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),"CSS property with entry in sLayerAnimationInfo does not ""have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag");}// Check that every property with the flag for animating on the// compositor has an entry in sLayerAnimationInfo.for(nsCSSPropertyprop=nsCSSProperty(0);prop<eCSSProperty_COUNT;prop=nsCSSProperty(prop+1)){if(nsCSSProps::PropHasFlags(prop,CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)){boolfound=false;for(size_ti=0;i<ArrayLength(info);i++){autorecord=info[i];if(record.mProperty==prop){found=true;break;}}MOZ_ASSERT(found,"CSS property with the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR ""flag does not have an entry in sLayerAnimationInfo");}}}#endifNS_IMPL_ISUPPORTS(AnimValuesStyleRule,nsIStyleRule)/* virtual */voidAnimValuesStyleRule::MapRuleInfoInto(nsRuleData*aRuleData){nsStyleContext*contextParent=aRuleData->mStyleContext->GetParent();if(contextParent&&contextParent->HasPseudoElementData()){// Don't apply transitions or animations to things inside of// pseudo-elements.// FIXME (Bug 522599): Add tests for this.// Prevent structs from being cached on the rule node since we're inside// a pseudo-element, as we could determine cacheability differently// when walking the rule tree for a style context that is not inside// a pseudo-element. Note that nsRuleNode::GetStyle##name_ and GetStyleData// will never look at cached structs when we're animating things inside// a pseduo-element, so that we don't incorrectly return a struct that// is only appropriate for non-pseudo-elements.aRuleData->mConditions.SetUncacheable();return;}for(uint32_ti=0,i_end=mPropertyValuePairs.Length();i<i_end;++i){PropertyValuePair&cv=mPropertyValuePairs[i];if(aRuleData->mSIDs&nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[cv.mProperty])){nsCSSValue*prop=aRuleData->ValueFor(cv.mProperty);if(prop->GetUnit()==eCSSUnit_Null){#ifdef DEBUGboolok=#endifStyleAnimationValue::UncomputeValue(cv.mProperty,cv.mValue,*prop);MOZ_ASSERT(ok,"could not store computed value");}}}}#ifdef DEBUG/* virtual */voidAnimValuesStyleRule::List(FILE*out,int32_taIndent)const{nsAutoCStringstr;for(int32_tindex=aIndent;--index>=0;){str.AppendLiteral(" ");}str.AppendLiteral("[anim values] { ");for(uint32_ti=0,i_end=mPropertyValuePairs.Length();i<i_end;++i){constPropertyValuePair&pair=mPropertyValuePairs[i];str.Append(nsCSSProps::GetStringValue(pair.mProperty));str.AppendLiteral(": ");nsAutoStringvalue;StyleAnimationValue::UncomputeValue(pair.mProperty,pair.mValue,value);AppendUTF16toUTF8(value,str);str.AppendLiteral("; ");}str.AppendLiteral("}\n");fprintf_stderr(out,"%s",str.get());}#endifboolAnimationCollection::CanAnimatePropertyOnCompositor(constdom::Element*aElement,nsCSSPropertyaProperty,CanAnimateFlagsaFlags){boolshouldLog=nsLayoutUtils::IsAnimationLoggingEnabled();if(!gfxPlatform::OffMainThreadCompositingEnabled()){if(shouldLog){nsCStringmessage;message.AppendLiteral("Performance warning: Compositor disabled");LogAsyncAnimationFailure(message);}returnfalse;}nsIFrame*frame=nsLayoutUtils::GetStyleFrame(aElement);if(IsGeometricProperty(aProperty)){if(shouldLog){nsCStringmessage;message.AppendLiteral("Performance warning: Async animation of geometric property '");message.Append(nsCSSProps::GetStringValue(aProperty));message.AppendLiteral("' is disabled");LogAsyncAnimationFailure(message,aElement);}returnfalse;}if(aProperty==eCSSProperty_transform){if(frame->Preserves3D()||frame->Preserves3DChildren()){if(shouldLog){nsCStringmessage;message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");LogAsyncAnimationFailure(message,aElement);}returnfalse;}// Note that testing BackfaceIsHidden() is not a sufficient test for// what we need for animating backface-visibility correctly if we// remove the above test for Preserves3DChildren(); that would require// looking at backface-visibility on descendants as well.if(frame->StyleDisplay()->BackfaceIsHidden()){if(shouldLog){nsCStringmessage;message.AppendLiteral("Gecko bug: Async animation of 'backface-visibility: hidden' transforms is not supported. See bug 1186204.");LogAsyncAnimationFailure(message,aElement);}returnfalse;}if(frame->IsSVGTransformed()){if(shouldLog){nsCStringmessage;message.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");LogAsyncAnimationFailure(message,aElement);}returnfalse;}if(aFlags&CanAnimate_HasGeometricProperty){if(shouldLog){nsCStringmessage;message.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");LogAsyncAnimationFailure(message,aElement);}returnfalse;}}boolenabled=nsLayoutUtils::AreAsyncAnimationsEnabled();if(!enabled&&shouldLog){nsCStringmessage;message.AppendLiteral("Performance warning: Async animations are disabled");LogAsyncAnimationFailure(message);}boolpropertyAllowed=(aProperty==eCSSProperty_transform)||(aProperty==eCSSProperty_opacity)||(aFlags&CanAnimate_AllowPartial);returnenabled&&propertyAllowed;}/* static */boolAnimationCollection::IsCompositorAnimationDisabledForFrame(nsIFrame*aFrame){void*prop=aFrame->Properties().Get(nsIFrame::RefusedAsyncAnimation());returnbool(reinterpret_cast<intptr_t>(prop));}boolAnimationCollection::CanPerformOnCompositorThread(CanAnimateFlagsaFlags)const{dom::Element*element=GetElementToRestyle();if(!element){returnfalse;}nsIFrame*frame=nsLayoutUtils::GetStyleFrame(element);if(!frame){returnfalse;}for(size_tanimIdx=mAnimations.Length();animIdx--!=0;){constAnimation*anim=mAnimations[animIdx];if(!anim->IsPlaying()){continue;}constKeyframeEffectReadOnly*effect=anim->GetEffect();MOZ_ASSERT(effect,"A playing animation should have an effect");for(size_tpropIdx=0,propEnd=effect->Properties().Length();propIdx!=propEnd;++propIdx){if(IsGeometricProperty(effect->Properties()[propIdx].mProperty)){aFlags=CanAnimateFlags(aFlags|CanAnimate_HasGeometricProperty);break;}}}boolexistsProperty=false;for(size_tanimIdx=mAnimations.Length();animIdx--!=0;){constAnimation*anim=mAnimations[animIdx];if(!anim->IsPlaying()){continue;}constKeyframeEffectReadOnly*effect=anim->GetEffect();MOZ_ASSERT(effect,"A playing animation should have an effect");existsProperty=existsProperty||effect->Properties().Length()>0;for(size_tpropIdx=0,propEnd=effect->Properties().Length();propIdx!=propEnd;++propIdx){constAnimationProperty&prop=effect->Properties()[propIdx];if(!CanAnimatePropertyOnCompositor(element,prop.mProperty,aFlags)||IsCompositorAnimationDisabledForFrame(frame)){returnfalse;}}}// No properties to animateif(!existsProperty){returnfalse;}returntrue;}boolAnimationCollection::HasCurrentAnimationOfProperty(nsCSSPropertyaProperty)const{for(Animation*animation:mAnimations){if(animation->HasCurrentEffect()&&animation->GetEffect()->HasAnimationOfProperty(aProperty)){returntrue;}}returnfalse;}/*static*/nsStringAnimationCollection::PseudoTypeAsString(nsCSSPseudoElements::TypeaPseudoType){switch(aPseudoType){casensCSSPseudoElements::ePseudo_before:returnNS_LITERAL_STRING("::before");casensCSSPseudoElements::ePseudo_after:returnNS_LITERAL_STRING("::after");default:MOZ_ASSERT(aPseudoType==nsCSSPseudoElements::ePseudo_NotPseudoElement,"Unexpected pseudo type");returnEmptyString();}}mozilla::dom::Element*AnimationCollection::GetElementToRestyle()const{if(IsForElement()){returnmElement;}nsIFrame*primaryFrame=mElement->GetPrimaryFrame();if(!primaryFrame){returnnullptr;}nsIFrame*pseudoFrame;if(IsForBeforePseudo()){pseudoFrame=nsLayoutUtils::GetBeforeFrame(primaryFrame);}elseif(IsForAfterPseudo()){pseudoFrame=nsLayoutUtils::GetAfterFrame(primaryFrame);}else{MOZ_ASSERT(false,"unknown mElementProperty");returnnullptr;}if(!pseudoFrame){returnnullptr;}returnpseudoFrame->GetContent()->AsElement();}/* static */voidAnimationCollection::LogAsyncAnimationFailure(nsCString&aMessage,constnsIContent*aContent){if(aContent){aMessage.AppendLiteral(" [");aMessage.Append(nsAtomCString(aContent->NodeInfo()->NameAtom()));nsIAtom*id=aContent->GetID();if(id){aMessage.AppendLiteral(" with id '");aMessage.Append(nsAtomCString(aContent->GetID()));aMessage.Append('\'');}aMessage.Append(']');}aMessage.Append('\n');printf_stderr("%s",aMessage.get());}/*static*/voidAnimationCollection::PropertyDtor(void*aObject,nsIAtom*aPropertyName,void*aPropertyValue,void*aData){AnimationCollection*collection=static_cast<AnimationCollection*>(aPropertyValue);#ifdef DEBUGMOZ_ASSERT(!collection->mCalledPropertyDtor,"can't call dtor twice");collection->mCalledPropertyDtor=true;#endif{nsAutoAnimationMutationBatchmb(collection->mElement->OwnerDoc());for(size_tanimIdx=collection->mAnimations.Length();animIdx--!=0;){collection->mAnimations[animIdx]->CancelFromStyle();}}deletecollection;}voidAnimationCollection::Tick(){for(size_tanimIdx=0,animEnd=mAnimations.Length();animIdx!=animEnd;animIdx++){mAnimations[animIdx]->Tick();}}voidAnimationCollection::EnsureStyleRuleFor(TimeStampaRefreshTime){mHasPendingAnimationRestyle=false;if(!mNeedsRefreshes){mStyleRuleRefreshTime=aRefreshTime;return;}if(!mStyleRuleRefreshTime.IsNull()&&mStyleRuleRefreshTime==aRefreshTime){// mStyleRule may be null and valid, if we have no style to apply.return;}if(mManager->IsAnimationManager()){// Update cascade results before updating the style rule, since the// cascade results can influence the style rule.static_cast<nsAnimationManager*>(mManager)->MaybeUpdateCascadeResults(this);}mStyleRuleRefreshTime=aRefreshTime;mStyleRule=nullptr;// We'll set mNeedsRefreshes to true below in all cases where we need them.mNeedsRefreshes=false;// If multiple animations specify behavior for the same property the// animation which occurs last in the value of animation-name wins.// As a result, we iterate from last animation to first and, if a// property has already been set, we don't leave it.nsCSSPropertySetproperties;for(size_tanimIdx=mAnimations.Length();animIdx--!=0;){mAnimations[animIdx]->ComposeStyle(mStyleRule,properties,mNeedsRefreshes);}mManager->MaybeStartObservingRefreshDriver();}boolAnimationCollection::CanThrottleTransformChanges(TimeStampaTime){if(!nsLayoutUtils::AreAsyncAnimationsEnabled()){returnfalse;}// If we know that the animation cannot cause overflow,// we can just disable flushes for this animation.// If we don't show scrollbars, we don't care about overflow.if(LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars)==0){returntrue;}// If this animation can cause overflow, we can throttle some of the ticks.if(!mStyleRuleRefreshTime.IsNull()&&(aTime-mStyleRuleRefreshTime)<TimeDuration::FromMilliseconds(200)){returntrue;}dom::Element*element=GetElementToRestyle();if(!element){returnfalse;}// If the nearest scrollable ancestor has overflow:hidden,// we don't care about overflow.nsIScrollableFrame*scrollable=nsLayoutUtils::GetNearestScrollableFrame(nsLayoutUtils::GetStyleFrame(element));if(!scrollable){returntrue;}ScrollbarStylesss=scrollable->GetScrollbarStyles();if(ss.mVertical==NS_STYLE_OVERFLOW_HIDDEN&&ss.mHorizontal==NS_STYLE_OVERFLOW_HIDDEN&&scrollable->GetLogicalScrollPosition()==nsPoint(0,0)){returntrue;}returnfalse;}boolAnimationCollection::CanThrottleAnimation(TimeStampaTime){dom::Element*element=GetElementToRestyle();if(!element){returnfalse;}nsIFrame*frame=nsLayoutUtils::GetStyleFrame(element);if(!frame){returnfalse;}constauto&info=CommonAnimationManager::sLayerAnimationInfo;for(size_ti=0;i<ArrayLength(info);i++){autorecord=info[i];// We only need to worry about *current* animations here.// - If we have a newly-finished animation, Animation::CanThrottle will// detect that and force an unthrottled sample.// - If we have a newly-idle animation, then whatever caused the animation// to be idle will update the animation generation so we'll return false// from the layer generation check below for any other running compositor// animations (and if no other compositor animations exist we won't get// this far).if(!HasCurrentAnimationOfProperty(record.mProperty)){continue;}Layer*layer=FrameLayerBuilder::GetDedicatedLayer(frame,record.mLayerType);if(!layer||mAnimationGeneration>layer->GetAnimationGeneration()){returnfalse;}if(record.mProperty==eCSSProperty_transform&&!CanThrottleTransformChanges(aTime)){returnfalse;}}returntrue;}voidAnimationCollection::RequestRestyle(RestyleTypeaRestyleType){MOZ_ASSERT(IsForElement()||IsForBeforePseudo()||IsForAfterPseudo(),"Unexpected mElementProperty; might restyle too much");nsPresContext*presContext=mManager->PresContext();if(!presContext){// Pres context will be null after the manager is disconnected.return;}MOZ_ASSERT(mElement->GetCrossShadowCurrentDoc()==presContext->Document(),"Element::UnbindFromTree should have destroyed the element ""transition/animations object");// Steps for Restyle::Layer:if(aRestyleType==RestyleType::Layer){mStyleRuleRefreshTime=TimeStamp();// FIXME: We should be able to remove these two lines once we move// ticking to animation timelines as part of bug 1151731.mNeedsRefreshes=true;mManager->MaybeStartObservingRefreshDriver();// Prompt layers to re-sync their animations.presContext->ClearLastStyleUpdateForAllAnimations();presContext->RestyleManager()->IncrementAnimationGeneration();UpdateAnimationGeneration(presContext);}// Steps for RestyleType::Standard and above:if(mHasPendingAnimationRestyle){return;}// Upgrade throttled restyles if other factors prevent// throttling (e.g. async animations are not enabled).if(aRestyleType==RestyleType::Throttled){TimeStampnow=presContext->RefreshDriver()->MostRecentRefresh();if(!CanPerformOnCompositorThread(CanAnimateFlags(0))||!CanThrottleAnimation(now)){aRestyleType=RestyleType::Standard;}}if(aRestyleType>=RestyleType::Standard){mHasPendingAnimationRestyle=true;PostRestyleForAnimation(presContext);return;}// Steps for RestyleType::Throttled:MOZ_ASSERT(aRestyleType==RestyleType::Throttled,"Should have already handled all non-throttled restyles");presContext->Document()->SetNeedStyleFlush();}voidAnimationCollection::UpdateAnimationGeneration(nsPresContext*aPresContext){mAnimationGeneration=aPresContext->RestyleManager()->GetAnimationGeneration();}voidAnimationCollection::UpdateCheckGeneration(nsPresContext*aPresContext){mCheckGeneration=aPresContext->RestyleManager()->GetAnimationGeneration();}boolAnimationCollection::HasCurrentAnimations()const{for(size_tanimIdx=mAnimations.Length();animIdx--!=0;){if(mAnimations[animIdx]->HasCurrentEffect()){returntrue;}}returnfalse;}boolAnimationCollection::HasCurrentAnimationsForProperties(constnsCSSProperty*aProperties,size_taPropertyCount)const{for(size_tanimIdx=mAnimations.Length();animIdx--!=0;){constAnimation&anim=*mAnimations[animIdx];constKeyframeEffectReadOnly*effect=anim.GetEffect();if(effect&&effect->IsCurrent(anim)&&effect->HasAnimationOfProperties(aProperties,aPropertyCount)){returntrue;}}returnfalse;}nsPresContext*OwningElementRef::GetRenderedPresContext()const{if(!mElement){returnnullptr;}nsIDocument*doc=mElement->GetComposedDoc();if(!doc){returnnullptr;}nsIPresShell*shell=doc->GetShell();if(!shell){returnnullptr;}returnshell->GetPresContext();}}// namespace mozilla