From ab1bc2614f7578daa807d69d8a13441bfcd5fe7b Mon Sep 17 00:00:00 2001 From: David Zbarsky Date: Wed, 25 Jul 2012 01:48:10 -0700 Subject: [PATCH] Bug 768440 Part 2: Animate CSS Transitions on the compositor r=roc,dbaron --- content/base/public/nsIContent.h | 1 + layout/base/nsDisplayList.cpp | 216 +++++++++++++++------------ layout/base/nsDisplayList.h | 8 +- layout/base/nsLayoutUtils.cpp | 28 ++++ layout/base/nsLayoutUtils.h | 7 + layout/generic/nsFrame.cpp | 20 ++- layout/style/nsTransitionManager.cpp | 66 ++++++-- layout/style/nsTransitionManager.h | 43 +++++- 8 files changed, 262 insertions(+), 127 deletions(-) diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index c6bb0b64b6c4..a0db0b8da2e8 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -11,6 +11,7 @@ #include "nsChangeHint.h" #include "nsINode.h" #include "nsIDocument.h" // for IsInHTMLDocument +#include "nsCSSProperty.h" // Forward declarations class nsIAtom; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index d2fd9549a35d..760e80ad74d6 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -39,6 +39,7 @@ #include "nsSVGClipPathFrame.h" #include "sampler.h" #include "nsAnimationManager.h" +#include "nsTransitionManager.h" #include "nsIViewManager.h" #include "mozilla/StandardInteger.h" @@ -267,113 +268,144 @@ ToTimingFunction(css::ComputedTimingFunction& aCTF) } static void -AddTransformAnimations(ElementAnimations* ea, Layer* aLayer, - const nsPoint& aOrigin) +AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, + ElementAnimation* ea, Layer* aLayer, + AnimationData& aData) { - if (!ea) - return; NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer"); - nsIFrame* frame = ea->mElement->GetPrimaryFrame(); - nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(frame); + nsStyleContext* styleContext = aFrame->GetStyleContext(); + nsPresContext* presContext = aFrame->PresContext(); + nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); float scale = nsDeviceContext::AppUnitsPerCSSPixel(); - gfxPoint3D offsetToTransformOrigin = - nsDisplayTransform::GetDeltaToMozTransformOrigin(frame, scale, &bounds); - gfxPoint3D offsetToPerspectiveOrigin = - nsDisplayTransform::GetDeltaToMozPerspectiveOrigin(frame, scale); - nscoord perspective = 0.0; - nsStyleContext* parentStyleContext = frame->GetStyleContext()->GetParent(); - if (parentStyleContext) { - const nsStyleDisplay* disp = parentStyleContext->GetStyleDisplay(); - if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { - perspective = disp->mChildPerspective.GetCoordValue(); - } - } + float iterations = ea->mIterationCount != NS_IEEEPositiveInfinity() + ? ea->mIterationCount : -1; + for (PRUint32 propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) { + AnimationProperty* property = &ea->mProperties[propIdx]; + InfallibleTArray segments; - for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) { - ElementAnimation* anim = &ea->mAnimations[animIdx]; - if (!anim->CanPerformOnCompositor(ea->mElement, TimeStamp::Now())) { + if (aProperty != property->mProperty) { continue; } - float iterations = anim->mIterationCount != NS_IEEEPositiveInfinity() - ? anim->mIterationCount : -1; - for (PRUint32 propIdx = 0; propIdx < anim->mProperties.Length(); propIdx++) { - AnimationProperty* property = &anim->mProperties[propIdx]; - InfallibleTArray segments; - if (property->mProperty != eCSSProperty_transform) { - continue; - } + for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { + AnimationPropertySegment* segment = &property->mSegments[segIdx]; - for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { - AnimationPropertySegment* segment = &property->mSegments[segIdx]; + if (aProperty == eCSSProperty_transform) { nsCSSValueList* list = segment->mFromValue.GetCSSValueListValue(); InfallibleTArray fromFunctions; - AddTransformFunctions(list, frame->GetStyleContext(), - frame->PresContext(), bounds, + AddTransformFunctions(list, styleContext, + presContext, bounds, scale, fromFunctions); list = segment->mToValue.GetCSSValueListValue(); InfallibleTArray toFunctions; - AddTransformFunctions(list, frame->GetStyleContext(), - frame->PresContext(), bounds, + AddTransformFunctions(list, styleContext, + presContext, bounds, scale, toFunctions); segments.AppendElement(AnimationSegment(fromFunctions, toFunctions, segment->mFromKey, segment->mToKey, ToTimingFunction(segment->mTimingFunction))); - } - - if (segments.Length() == 0) { - continue; - } - aLayer->AddAnimation(Animation(anim->mStartTime, - anim->mIterationDuration, - segments, - iterations, - anim->mDirection, - TransformData(aOrigin, offsetToTransformOrigin, - offsetToPerspectiveOrigin, - bounds, perspective))); - } - } -} - -static void -AddOpacityAnimations(ElementAnimations* ea, Layer* aLayer) -{ - if (!ea) - return; - NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer"); - for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) { - ElementAnimation* anim = &ea->mAnimations[animIdx]; - float iterations = anim->mIterationCount != NS_IEEEPositiveInfinity() - ? anim->mIterationCount : -1; - if (!anim->CanPerformOnCompositor(ea->mElement, TimeStamp::Now())) { - continue; - } - for (PRUint32 propIdx = 0; propIdx < anim->mProperties.Length(); propIdx++) { - AnimationProperty* property = &anim->mProperties[propIdx]; - InfallibleTArray segments; - if (property->mProperty != eCSSProperty_opacity) { - continue; - } - - for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { - AnimationPropertySegment* segment = &property->mSegments[segIdx]; + } else if (aProperty == eCSSProperty_opacity) { segments.AppendElement(AnimationSegment(Opacity(segment->mFromValue.GetFloatValue()), Opacity(segment->mToValue.GetFloatValue()), segment->mFromKey, segment->mToKey, ToTimingFunction(segment->mTimingFunction))); } + } - aLayer->AddAnimation(Animation(anim->mStartTime, - anim->mIterationDuration, - segments, - iterations, - anim->mDirection, - null_t())); - } + aLayer->AddAnimation(Animation(ea->mStartTime, + ea->mIterationDuration, + segments, + iterations, + ea->mDirection, + aData)); + } +} + +static void +AddAnimationsAndTransitionsToLayer(Layer* aLayer, nsDisplayItem* aItem, + nsCSSProperty aProperty) +{ + aLayer->ClearAnimations(); + + nsIFrame* frame = aItem->GetUnderlyingFrame(); + nsIContent* aContent = frame->GetContent(); + ElementTransitions* et = + nsTransitionManager::GetTransitionsForCompositor(aContent, aProperty); + + ElementAnimations* ea = + nsAnimationManager::GetAnimationsForCompositor(aContent, aProperty); + + if (!ea && !et) { + return; + } + + mozilla::TimeStamp currentTime = + frame->PresContext()->RefreshDriver()->MostRecentRefresh(); + AnimationData data; + if (aProperty == eCSSProperty_transform) { + nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(frame); + float scale = nsDeviceContext::AppUnitsPerCSSPixel(); + gfxPoint3D offsetToTransformOrigin = + nsDisplayTransform::GetDeltaToMozTransformOrigin(frame, scale, &bounds); + gfxPoint3D offsetToPerspectiveOrigin = + nsDisplayTransform::GetDeltaToMozPerspectiveOrigin(frame, scale); + nscoord perspective = 0.0; + nsStyleContext* parentStyleContext = frame->GetStyleContext()->GetParent(); + if (parentStyleContext) { + const nsStyleDisplay* disp = parentStyleContext->GetStyleDisplay(); + if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { + perspective = disp->mChildPerspective.GetCoordValue(); + } + } + nsPoint origin = aItem->ToReferenceFrame(); + + data = TransformData(origin, offsetToTransformOrigin, + offsetToPerspectiveOrigin, bounds, perspective); + } else if (aProperty == eCSSProperty_opacity) { + data = null_t(); + } + + if (et) { + for (PRUint32 tranIdx = 0; tranIdx < et->mPropertyTransitions.Length(); tranIdx++) { + ElementPropertyTransition* pt = &et->mPropertyTransitions[tranIdx]; + if (!pt->CanPerformOnCompositor(et->mElement, currentTime)) { + continue; + } + + ElementAnimation anim; + anim.mIterationCount = 1; + anim.mDirection = NS_STYLE_ANIMATION_DIRECTION_NORMAL; + anim.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_NONE; + anim.mStartTime = pt->mStartTime; + anim.mIterationDuration = pt->mDuration; + + AnimationProperty& prop = *anim.mProperties.AppendElement(); + prop.mProperty = pt->mProperty; + + AnimationPropertySegment& segment = *prop.mSegments.AppendElement(); + segment.mFromKey = 0; + segment.mToKey = 1; + segment.mFromValue = pt->mStartValue; + segment.mToValue = pt->mEndValue; + segment.mTimingFunction = pt->mTimingFunction; + + AddAnimationsForProperty(frame, aProperty, &anim, + aLayer, data); + } + } + + if (ea) { + for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) { + ElementAnimation* anim = &ea->mAnimations[animIdx]; + if (!anim->CanPerformOnCompositor(ea->mElement, currentTime)) { + continue; + } + AddAnimationsForProperty(frame, aProperty, anim, + aLayer, data); + } } } @@ -2322,12 +2354,7 @@ nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder, return nsnull; container->SetOpacity(mFrame->GetStyleDisplay()->mOpacity); - - container->ClearAnimations(); - ElementAnimations* ea = - nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), - eCSSProperty_opacity); - AddOpacityAnimations(ea, container); + AddAnimationsAndTransitionsToLayer(container, this, eCSSProperty_opacity); return container.forget(); } @@ -2355,8 +2382,8 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, !IsItemTooSmallForActiveLayer(this)) return LAYER_ACTIVE; if (mFrame->GetContent()) { - if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), - eCSSProperty_opacity)) { + if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), + eCSSProperty_opacity)) { return LAYER_ACTIVE; } } @@ -3338,12 +3365,7 @@ already_AddRefed nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D); } - container->ClearAnimations(); - ElementAnimations* ea = - nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), - eCSSProperty_transform); - AddTransformAnimations(ea, container, ToReferenceFrame()); - + AddAnimationsAndTransitionsToLayer(container, this, eCSSProperty_transform); return container.forget(); } @@ -3359,8 +3381,8 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D()) return LAYER_ACTIVE; if (mFrame->GetContent()) { - if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), - eCSSProperty_transform)) { + if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), + eCSSProperty_transform)) { return LAYER_ACTIVE; } } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 8e88bd8f729f..c4874ef884d4 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -108,10 +108,10 @@ public: * display lists that we make. */ enum Mode { - PAINTING, - EVENT_DELIVERY, - PLUGIN_GEOMETRY, - OTHER + PAINTING, + EVENT_DELIVERY, + PLUGIN_GEOMETRY, + OTHER }; nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret); ~nsDisplayListBuilder(); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index bca9bf3e70d8..9bcbdc9e6e92 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -83,6 +83,8 @@ #endif #include "sampler.h" +#include "nsAnimationManager.h" +#include "nsTransitionManager.h" using namespace mozilla; using namespace mozilla::layers; @@ -113,6 +115,32 @@ static ContentMap& GetContentMap() { return *sContentMap; } +bool +nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent, + nsCSSProperty aProperty) +{ + if (!aContent->MayHaveAnimations()) + return false; + ElementAnimations* animations = + static_cast(aContent->GetProperty(nsGkAtoms::animationsProperty)); + if (animations) { + bool propertyMatches = animations->HasAnimationOfProperty(aProperty); + if (propertyMatches && animations->CanPerformOnCompositorThread()) { + return true; + } + } + + ElementTransitions* transitions = + static_cast(aContent->GetProperty(nsGkAtoms::transitionsProperty)); + if (transitions) { + bool propertyMatches = transitions->HasTransitionOfProperty(aProperty); + if (propertyMatches && transitions->CanPerformOnCompositorThread()) { + return true; + } + } + + return false; +} bool nsLayoutUtils::Are3DTransformsEnabled() diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 2f3ac071cbf0..abdfe079ff28 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1497,6 +1497,13 @@ public: nsMallocSizeOfFun aMallocSizeOf, bool clear); + /** + * Returns true if the content node has animations or transitions that can be + * performed on the compositor. + */ + static bool HasAnimationsForCompositor(nsIContent* aContent, + nsCSSProperty aProperty); + /** * Checks if CSS 3D transforms are currently enabled. */ diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 7c6dd1a1e1c4..db9d9cf61d7b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -93,6 +93,7 @@ #include "nsAbsoluteContainingBlock.h" #include "nsFontInflationData.h" #include "nsAnimationManager.h" +#include "nsTransitionManager.h" #include "mozilla/Preferences.h" #include "mozilla/LookAndFeel.h" @@ -940,14 +941,16 @@ nsIFrame::IsTransformed() const (GetStyleDisplay()->HasTransform() || IsSVGTransformed() || (mContent && - nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_transform)))); + nsLayoutUtils::HasAnimationsForCompositor(mContent, + eCSSProperty_transform)))); } bool nsIFrame::HasOpacity() const { return GetStyleDisplay()->mOpacity < 1.0f || (mContent && - nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_opacity)); + nsLayoutUtils::HasAnimationsForCompositor(mContent, + eCSSProperty_opacity)); } bool @@ -1765,7 +1768,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsDisplayList* aList) { if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; - // Replaced elements have their visibility handled here, because // they're visually atomic if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder)) @@ -1773,10 +1775,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsRect clipPropClip; const nsStyleDisplay* disp = GetStyleDisplay(); - // We can stop right away if this is a zero-opacity stacking context and - // we're painting. - if (disp->mOpacity == 0.0 && aBuilder->IsForPainting()) + // We can stop right away if this is a zero-opacity stacking context, + // we're painting, and we're not animating opacity. + if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && + !nsLayoutUtils::HasAnimationsForCompositor(mContent, + eCSSProperty_opacity)) { return NS_OK; + } bool applyClipPropClipping = ApplyClipPropClipping(aBuilder, disp, this, &clipPropClip); @@ -1793,7 +1798,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // gives us really weird results. I believe this is from points that lie beyond the // vanishing point. As a workaround we transform the overflow rect into screen space // and compare in that coordinate system. - + // Transform the overflow rect into screen space nsRect overflow = GetVisualOverflowRectRelativeToSelf(); nsPoint offset = aBuilder->ToReferenceFrame(this); @@ -1930,7 +1935,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // resultList was emptied resultList.AppendToTop(item); } - /* If there are any SVG effects, wrap the list up in an SVG effects item * (which also handles CSS group opacity). Note that we create an SVG effects * item even if resultList is empty, since a filter can produce graphical diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 1326341cc8f5..c6408e298ff8 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -22,6 +22,8 @@ #include "nsEventDispatcher.h" #include "nsGUIEvent.h" #include "mozilla/dom/Element.h" +#include "nsIFrame.h" +#include "nsCSSFrameConstructor.h" using mozilla::TimeStamp; using mozilla::TimeDuration; @@ -36,7 +38,6 @@ ElementTransitions::ElementTransitions(mozilla::dom::Element *aElement, nsIAtom { } - double ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const { @@ -107,6 +108,41 @@ ElementTransitions::EnsureStyleRuleFor(TimeStamp aRefreshTime) } } +bool +ElementPropertyTransition::CanPerformOnCompositor(mozilla::dom::Element* aElement, + TimeStamp aTime) const { + return css::CommonElementAnimationData:: + CanAnimatePropertyOnCompositor(aElement, mProperty) && !IsRemovedSentinel() && + mStartTime < aTime && aTime < mStartTime + mDuration; +} + +bool +ElementTransitions::HasTransitionOfProperty(nsCSSProperty aProperty) const +{ + for (PRUint32 tranIdx = mPropertyTransitions.Length(); tranIdx-- != 0; ) { + if (aProperty == mPropertyTransitions[tranIdx].mProperty) { + return true; + } + } + return false; +} + +bool +ElementTransitions::CanPerformOnCompositorThread() const +{ + for (PRUint32 i = 0, i_end = mPropertyTransitions.Length(); i < i_end; ++i) { + const ElementPropertyTransition &pt = mPropertyTransitions[i]; + if (pt.IsRemovedSentinel()) { + continue; + } + if (!css::CommonElementAnimationData::CanAnimatePropertyOnCompositor(mElement, + pt.mProperty)) { + return false; + } + } + return true; +} + /***************************************************************************** * nsTransitionManager * *****************************************************************************/ @@ -161,7 +197,6 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement, return nsnull; } - if (aNewStyleContext->PresContext()->IsProcessingAnimationStyleChange()) { return nsnull; } @@ -298,10 +333,6 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement, // rule. nsRefPtr coverRule = new css::AnimValuesStyleRule; - if (!coverRule) { - NS_WARNING("out of memory"); - return nsnull; - } nsTArray &pts = et->mPropertyTransitions; for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) { @@ -347,9 +378,19 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty, pt.mStartValue) && ExtractComputedValueForTransition(aProperty, aNewStyleContext, pt.mEndValue); + + bool haveChange = pt.mStartValue != pt.mEndValue; + bool haveOMTA = false; + if (!aNewStyleContext->GetPseudoType()) { + ElementTransitions* et = nsTransitionManager::GetTransitions(aElement); + if (et) { + haveOMTA = et->CanPerformOnCompositorThread(); + } + } + bool shouldAnimate = haveValues && - pt.mStartValue != pt.mEndValue && + (haveChange || haveOMTA) && // Check that we can interpolate between these values // (If this is ever a performance problem, we could add a // CanInterpolate method, but it seems fine for now.) @@ -450,6 +491,7 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty, // reduce positive delays. if (delay < 0.0f) delay *= valuePortion; + duration *= valuePortion; pt.mStartForReversingTest = oldPT.mEndValue; @@ -461,7 +503,6 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty, pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay); pt.mDuration = TimeDuration::FromMilliseconds(duration); pt.mTimingFunction.Init(tf); - if (!aElementTransitions) { aElementTransitions = GetElementTransitions(aElement, aNewStyleContext->GetPseudoType(), @@ -495,6 +536,7 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty, nsCSSPseudoElements::ePseudo_NotPseudoElement ? eRestyle_Self : eRestyle_Subtree; presContext->PresShell()->RestyleForAnimation(aElement, hint); + // XXXdz: invalidate the frame here, once animations are throttled. *aStartedAny = true; aWhichStarted->AddProperty(aProperty); @@ -535,6 +577,9 @@ nsTransitionManager::GetElementTransitions(dom::Element *aElement, delete et; return nsnull; } + if (propName == nsGkAtoms::transitionsProperty) { + aElement->SetMayHaveAnimations(); + } AddElementData(et); } @@ -688,8 +733,6 @@ nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime) // completion. See comment below. et->mPropertyTransitions.RemoveElementAt(i); } else if (pt.mStartTime + pt.mDuration <= aTime) { - // This transition has completed. - // Fire transitionend events only for transitions on elements // and not those on pseudo-elements, since we can't target an // event at pseudo-elements. @@ -723,6 +766,9 @@ nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime) nsRestyleHint hint = et->mElementProperty == nsGkAtoms::transitionsProperty ? eRestyle_Self : eRestyle_Subtree; mPresContext->PresShell()->RestyleForAnimation(et->mElement, hint); + // XXXdz: if we have started a transition since the last tick and are + // performing the transition off the main thread, we need to invalidate + // the frame once we start throttling animation ticks. if (et->mPropertyTransitions.IsEmpty()) { et->Destroy(); diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index 245b6b00a3db..fc638ecd2fa0 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -22,6 +22,8 @@ struct nsTransition; struct ElementPropertyTransition { + ElementPropertyTransition() {} + nsCSSProperty mProperty; nsStyleAnimation::Value mStartValue, mEndValue; mozilla::TimeStamp mStartTime; // actual start plus transition delay @@ -29,7 +31,7 @@ struct ElementPropertyTransition // data from the relevant nsTransition mozilla::TimeDuration mDuration; mozilla::css::ComputedTimingFunction mTimingFunction; - + // This is the start value to be used for a check for whether a // transition is being reversed. Normally the same as mStartValue, // except when this transition started as the reversal of another @@ -46,24 +48,27 @@ struct ElementPropertyTransition // in again when the transition is back to 2px, the mReversePortion // for the third transition (from 0px/2px to 10px) will be 0.8. double mReversePortion; - + // Compute the portion of the *value* space that we should be through // at the given time. (The input to the transition timing function // has time units, the output has value units.) double ValuePortionFor(mozilla::TimeStamp aRefreshTime) const; - + bool IsRemovedSentinel() const { return mStartTime.IsNull(); } - + void SetRemovedSentinel() { // assign the null time stamp mStartTime = mozilla::TimeStamp(); } + + bool CanPerformOnCompositor(mozilla::dom::Element* aElement, + mozilla::TimeStamp aTime) const; }; - + struct ElementTransitions : public mozilla::css::CommonElementAnimationData { ElementTransitions(mozilla::dom::Element *aElement, nsIAtom *aElementProperty, @@ -71,11 +76,13 @@ struct ElementTransitions : public mozilla::css::CommonElementAnimationData void EnsureStyleRuleFor(mozilla::TimeStamp aRefreshTime); + + bool HasTransitionOfProperty(nsCSSProperty aProperty) const; // True if this animation can be performed on the compositor thread. - // virtual CanPerformOnCompositorThread() const; + bool CanPerformOnCompositorThread() const; // Either zero or one for each CSS property: nsTArray mPropertyTransitions; - + // This style rule overrides style data with the currently // transitioning value for an element that is executing a transition. // It only matches when styling with animation. When we style without @@ -97,8 +104,28 @@ public: { } + static ElementTransitions* GetTransitions(nsIContent* aContent) { + return static_cast + (aContent->GetProperty(nsGkAtoms::transitionsProperty)); + } + + static ElementTransitions* + GetTransitionsForCompositor(nsIContent* aContent, + nsCSSProperty aProperty) + { + if (!aContent->MayHaveAnimations()) + return nsnull; + ElementTransitions* transitions = GetTransitions(aContent); + if (!transitions || + !transitions->HasTransitionOfProperty(aProperty) || + !transitions->CanPerformOnCompositorThread()) { + return nsnull; + } + return transitions; + } + /** - * StyleContextChanged + * StyleContextChanged * * To be called from nsFrameManager::ReResolveStyleContext when the * style of an element has changed, to initiate transitions from