Bug 768440 Part 2: Animate CSS Transitions on the compositor r=roc,dbaron

This commit is contained in:
David Zbarsky
2012-07-31 10:28:22 -07:00
parent c1125ae35c
commit 6a56e516b5
8 changed files with 261 additions and 124 deletions

View File

@@ -11,6 +11,7 @@
#include "nsChangeHint.h" #include "nsChangeHint.h"
#include "nsINode.h" #include "nsINode.h"
#include "nsIDocument.h" // for IsInHTMLDocument #include "nsIDocument.h" // for IsInHTMLDocument
#include "nsCSSProperty.h"
// Forward declarations // Forward declarations
class nsIAtom; class nsIAtom;

View File

@@ -39,6 +39,7 @@
#include "nsSVGClipPathFrame.h" #include "nsSVGClipPathFrame.h"
#include "sampler.h" #include "sampler.h"
#include "nsAnimationManager.h" #include "nsAnimationManager.h"
#include "nsTransitionManager.h"
#include "nsIViewManager.h" #include "nsIViewManager.h"
#include "mozilla/StandardInteger.h" #include "mozilla/StandardInteger.h"
@@ -267,13 +268,85 @@ ToTimingFunction(css::ComputedTimingFunction& aCTF)
} }
static void static void
AddTransformAnimations(ElementAnimations* ea, Layer* aLayer, AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
const nsPoint& aOrigin) ElementAnimation* ea, Layer* aLayer,
AnimationData& aData)
{ {
if (!ea)
return;
NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer"); NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
nsIFrame* frame = ea->mElement->GetPrimaryFrame(); nsStyleContext* styleContext = aFrame->GetStyleContext();
nsPresContext* presContext = aFrame->PresContext();
nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame);
float scale = nsDeviceContext::AppUnitsPerCSSPixel();
float iterations = ea->mIterationCount != NS_IEEEPositiveInfinity()
? ea->mIterationCount : -1;
for (PRUint32 propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) {
AnimationProperty* property = &ea->mProperties[propIdx];
InfallibleTArray<AnimationSegment> segments;
if (aProperty != property->mProperty) {
continue;
}
for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
AnimationPropertySegment* segment = &property->mSegments[segIdx];
if (aProperty == eCSSProperty_transform) {
nsCSSValueList* list = segment->mFromValue.GetCSSValueListValue();
InfallibleTArray<TransformFunction> fromFunctions;
AddTransformFunctions(list, styleContext,
presContext, bounds,
scale, fromFunctions);
list = segment->mToValue.GetCSSValueListValue();
InfallibleTArray<TransformFunction> toFunctions;
AddTransformFunctions(list, styleContext,
presContext, bounds,
scale, toFunctions);
segments.AppendElement(AnimationSegment(fromFunctions, toFunctions,
segment->mFromKey, segment->mToKey,
ToTimingFunction(segment->mTimingFunction)));
} 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(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); nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(frame);
float scale = nsDeviceContext::AppUnitsPerCSSPixel(); float scale = nsDeviceContext::AppUnitsPerCSSPixel();
gfxPoint3D offsetToTransformOrigin = gfxPoint3D offsetToTransformOrigin =
@@ -288,96 +361,55 @@ AddTransformAnimations(ElementAnimations* ea, Layer* aLayer,
perspective = disp->mChildPerspective.GetCoordValue(); 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++) { for (PRUint32 animIdx = 0; animIdx < ea->mAnimations.Length(); animIdx++) {
ElementAnimation* anim = &ea->mAnimations[animIdx]; ElementAnimation* anim = &ea->mAnimations[animIdx];
if (!anim->CanPerformOnCompositor(ea->mElement, TimeStamp::Now())) { if (!anim->CanPerformOnCompositor(ea->mElement, currentTime)) {
continue; continue;
} }
float iterations = anim->mIterationCount != NS_IEEEPositiveInfinity() AddAnimationsForProperty(frame, aProperty, anim,
? anim->mIterationCount : -1; aLayer, data);
for (PRUint32 propIdx = 0; propIdx < anim->mProperties.Length(); propIdx++) {
AnimationProperty* property = &anim->mProperties[propIdx];
InfallibleTArray<AnimationSegment> segments;
if (property->mProperty != eCSSProperty_transform) {
continue;
}
for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
AnimationPropertySegment* segment = &property->mSegments[segIdx];
nsCSSValueList* list = segment->mFromValue.GetCSSValueListValue();
InfallibleTArray<TransformFunction> fromFunctions;
AddTransformFunctions(list, frame->GetStyleContext(),
frame->PresContext(), bounds,
scale, fromFunctions);
list = segment->mToValue.GetCSSValueListValue();
InfallibleTArray<TransformFunction> toFunctions;
AddTransformFunctions(list, frame->GetStyleContext(),
frame->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<AnimationSegment> segments;
if (property->mProperty != eCSSProperty_opacity) {
continue;
}
for (PRUint32 segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) {
AnimationPropertySegment* segment = &property->mSegments[segIdx];
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()));
}
}
}
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
Mode aMode, bool aBuildCaret) Mode aMode, bool aBuildCaret)
: mReferenceFrame(aReferenceFrame), : mReferenceFrame(aReferenceFrame),
@@ -2321,12 +2353,7 @@ nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
return nullptr; return nullptr;
container->SetOpacity(mFrame->GetStyleDisplay()->mOpacity); container->SetOpacity(mFrame->GetStyleDisplay()->mOpacity);
AddAnimationsAndTransitionsToLayer(container, this, eCSSProperty_opacity);
container->ClearAnimations();
ElementAnimations* ea =
nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
eCSSProperty_opacity);
AddOpacityAnimations(ea, container);
return container.forget(); return container.forget();
} }
@@ -2354,7 +2381,7 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
!IsItemTooSmallForActiveLayer(this)) !IsItemTooSmallForActiveLayer(this))
return LAYER_ACTIVE; return LAYER_ACTIVE;
if (mFrame->GetContent()) { if (mFrame->GetContent()) {
if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
eCSSProperty_opacity)) { eCSSProperty_opacity)) {
return LAYER_ACTIVE; return LAYER_ACTIVE;
} }
@@ -3338,12 +3365,7 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu
container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D); container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
} }
container->ClearAnimations(); AddAnimationsAndTransitionsToLayer(container, this, eCSSProperty_transform);
ElementAnimations* ea =
nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(),
eCSSProperty_transform);
AddTransformAnimations(ea, container, ToReferenceFrame());
return container.forget(); return container.forget();
} }
@@ -3359,7 +3381,7 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D()) if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D())
return LAYER_ACTIVE; return LAYER_ACTIVE;
if (mFrame->GetContent()) { if (mFrame->GetContent()) {
if (nsAnimationManager::GetAnimationsForCompositor(mFrame->GetContent(), if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
eCSSProperty_transform)) { eCSSProperty_transform)) {
return LAYER_ACTIVE; return LAYER_ACTIVE;
} }

View File

@@ -83,6 +83,8 @@
#endif #endif
#include "sampler.h" #include "sampler.h"
#include "nsAnimationManager.h"
#include "nsTransitionManager.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::layers; using namespace mozilla::layers;
@@ -113,6 +115,32 @@ static ContentMap& GetContentMap() {
return *sContentMap; return *sContentMap;
} }
bool
nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent,
nsCSSProperty aProperty)
{
if (!aContent->MayHaveAnimations())
return false;
ElementAnimations* animations =
static_cast<ElementAnimations*>(aContent->GetProperty(nsGkAtoms::animationsProperty));
if (animations) {
bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
if (propertyMatches && animations->CanPerformOnCompositorThread()) {
return true;
}
}
ElementTransitions* transitions =
static_cast<ElementTransitions*>(aContent->GetProperty(nsGkAtoms::transitionsProperty));
if (transitions) {
bool propertyMatches = transitions->HasTransitionOfProperty(aProperty);
if (propertyMatches && transitions->CanPerformOnCompositorThread()) {
return true;
}
}
return false;
}
bool bool
nsLayoutUtils::Are3DTransformsEnabled() nsLayoutUtils::Are3DTransformsEnabled()

View File

@@ -1497,6 +1497,13 @@ public:
nsMallocSizeOfFun aMallocSizeOf, nsMallocSizeOfFun aMallocSizeOf,
bool clear); 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. * Checks if CSS 3D transforms are currently enabled.
*/ */

View File

@@ -93,6 +93,7 @@
#include "nsAbsoluteContainingBlock.h" #include "nsAbsoluteContainingBlock.h"
#include "nsFontInflationData.h" #include "nsFontInflationData.h"
#include "nsAnimationManager.h" #include "nsAnimationManager.h"
#include "nsTransitionManager.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h" #include "mozilla/LookAndFeel.h"
@@ -940,14 +941,16 @@ nsIFrame::IsTransformed() const
(GetStyleDisplay()->HasTransform() || (GetStyleDisplay()->HasTransform() ||
IsSVGTransformed() || IsSVGTransformed() ||
(mContent && (mContent &&
nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_transform)))); nsLayoutUtils::HasAnimationsForCompositor(mContent,
eCSSProperty_transform))));
} }
bool bool
nsIFrame::HasOpacity() const nsIFrame::HasOpacity() const
{ {
return GetStyleDisplay()->mOpacity < 1.0f || (mContent && return GetStyleDisplay()->mOpacity < 1.0f || (mContent &&
nsAnimationManager::GetAnimationsForCompositor(mContent, eCSSProperty_opacity)); nsLayoutUtils::HasAnimationsForCompositor(mContent,
eCSSProperty_opacity));
} }
bool bool
@@ -1774,9 +1777,12 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
nsRect clipPropClip; nsRect clipPropClip;
const nsStyleDisplay* disp = GetStyleDisplay(); const nsStyleDisplay* disp = GetStyleDisplay();
// We can stop right away if this is a zero-opacity stacking context and // We can stop right away if this is a zero-opacity stacking context and
// we're painting. // we're painting, and we're not animating opacity.
if (disp->mOpacity == 0.0 && aBuilder->IsForPainting()) if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() &&
!nsLayoutUtils::HasAnimationsForCompositor(mContent,
eCSSProperty_opacity)) {
return NS_OK; return NS_OK;
}
bool applyClipPropClipping = bool applyClipPropClipping =
ApplyClipPropClipping(aBuilder, disp, this, &clipPropClip); ApplyClipPropClipping(aBuilder, disp, this, &clipPropClip);
@@ -1921,7 +1927,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
// resultList was emptied // resultList was emptied
resultList.AppendToTop(item); resultList.AppendToTop(item);
} }
/* If there are any SVG effects, wrap the list up in an SVG effects 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 * (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 * item even if resultList is empty, since a filter can produce graphical

View File

@@ -22,6 +22,8 @@
#include "nsEventDispatcher.h" #include "nsEventDispatcher.h"
#include "nsGUIEvent.h" #include "nsGUIEvent.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "nsIFrame.h"
#include "nsCSSFrameConstructor.h"
using mozilla::TimeStamp; using mozilla::TimeStamp;
using mozilla::TimeDuration; using mozilla::TimeDuration;
@@ -36,7 +38,6 @@ ElementTransitions::ElementTransitions(mozilla::dom::Element *aElement, nsIAtom
{ {
} }
double double
ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const 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 * * nsTransitionManager *
*****************************************************************************/ *****************************************************************************/
@@ -298,10 +334,6 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement,
// rule. // rule.
nsRefPtr<css::AnimValuesStyleRule> coverRule = new css::AnimValuesStyleRule; nsRefPtr<css::AnimValuesStyleRule> coverRule = new css::AnimValuesStyleRule;
if (!coverRule) {
NS_WARNING("out of memory");
return nullptr;
}
nsTArray<ElementPropertyTransition> &pts = et->mPropertyTransitions; nsTArray<ElementPropertyTransition> &pts = et->mPropertyTransitions;
for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) { for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) {
@@ -347,9 +379,19 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
pt.mStartValue) && pt.mStartValue) &&
ExtractComputedValueForTransition(aProperty, aNewStyleContext, ExtractComputedValueForTransition(aProperty, aNewStyleContext,
pt.mEndValue); 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 = bool shouldAnimate =
haveValues && haveValues &&
pt.mStartValue != pt.mEndValue && (haveChange || haveOMTA) &&
// Check that we can interpolate between these values // Check that we can interpolate between these values
// (If this is ever a performance problem, we could add a // (If this is ever a performance problem, we could add a
// CanInterpolate method, but it seems fine for now.) // CanInterpolate method, but it seems fine for now.)
@@ -450,6 +492,7 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
// reduce positive delays. // reduce positive delays.
if (delay < 0.0f) if (delay < 0.0f)
delay *= valuePortion; delay *= valuePortion;
duration *= valuePortion; duration *= valuePortion;
pt.mStartForReversingTest = oldPT.mEndValue; pt.mStartForReversingTest = oldPT.mEndValue;
@@ -461,7 +504,6 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay); pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay);
pt.mDuration = TimeDuration::FromMilliseconds(duration); pt.mDuration = TimeDuration::FromMilliseconds(duration);
pt.mTimingFunction.Init(tf); pt.mTimingFunction.Init(tf);
if (!aElementTransitions) { if (!aElementTransitions) {
aElementTransitions = aElementTransitions =
GetElementTransitions(aElement, aNewStyleContext->GetPseudoType(), GetElementTransitions(aElement, aNewStyleContext->GetPseudoType(),
@@ -495,6 +537,7 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
nsCSSPseudoElements::ePseudo_NotPseudoElement ? nsCSSPseudoElements::ePseudo_NotPseudoElement ?
eRestyle_Self : eRestyle_Subtree; eRestyle_Self : eRestyle_Subtree;
presContext->PresShell()->RestyleForAnimation(aElement, hint); presContext->PresShell()->RestyleForAnimation(aElement, hint);
// XXXdz: invalidate the frame here, once animations are throttled.
*aStartedAny = true; *aStartedAny = true;
aWhichStarted->AddProperty(aProperty); aWhichStarted->AddProperty(aProperty);
@@ -535,6 +578,9 @@ nsTransitionManager::GetElementTransitions(dom::Element *aElement,
delete et; delete et;
return nullptr; return nullptr;
} }
if (propName == nsGkAtoms::transitionsProperty) {
aElement->SetMayHaveAnimations();
}
AddElementData(et); AddElementData(et);
} }
@@ -688,8 +734,6 @@ nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime)
// completion. See comment below. // completion. See comment below.
et->mPropertyTransitions.RemoveElementAt(i); et->mPropertyTransitions.RemoveElementAt(i);
} else if (pt.mStartTime + pt.mDuration <= aTime) { } else if (pt.mStartTime + pt.mDuration <= aTime) {
// This transition has completed.
// Fire transitionend events only for transitions on elements // Fire transitionend events only for transitions on elements
// and not those on pseudo-elements, since we can't target an // and not those on pseudo-elements, since we can't target an
// event at pseudo-elements. // event at pseudo-elements.
@@ -723,6 +767,9 @@ nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime)
nsRestyleHint hint = et->mElementProperty == nsGkAtoms::transitionsProperty ? nsRestyleHint hint = et->mElementProperty == nsGkAtoms::transitionsProperty ?
eRestyle_Self : eRestyle_Subtree; eRestyle_Self : eRestyle_Subtree;
mPresContext->PresShell()->RestyleForAnimation(et->mElement, hint); 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()) { if (et->mPropertyTransitions.IsEmpty()) {
et->Destroy(); et->Destroy();

View File

@@ -22,6 +22,8 @@ struct nsTransition;
struct ElementPropertyTransition struct ElementPropertyTransition
{ {
ElementPropertyTransition() {}
nsCSSProperty mProperty; nsCSSProperty mProperty;
nsStyleAnimation::Value mStartValue, mEndValue; nsStyleAnimation::Value mStartValue, mEndValue;
mozilla::TimeStamp mStartTime; // actual start plus transition delay mozilla::TimeStamp mStartTime; // actual start plus transition delay
@@ -62,6 +64,9 @@ struct ElementPropertyTransition
// assign the null time stamp // assign the null time stamp
mStartTime = mozilla::TimeStamp(); mStartTime = mozilla::TimeStamp();
} }
bool CanPerformOnCompositor(mozilla::dom::Element* aElement,
mozilla::TimeStamp aTime) const;
}; };
struct ElementTransitions : public mozilla::css::CommonElementAnimationData struct ElementTransitions : public mozilla::css::CommonElementAnimationData
@@ -71,8 +76,10 @@ struct ElementTransitions : public mozilla::css::CommonElementAnimationData
void EnsureStyleRuleFor(mozilla::TimeStamp aRefreshTime); void EnsureStyleRuleFor(mozilla::TimeStamp aRefreshTime);
bool HasTransitionOfProperty(nsCSSProperty aProperty) const;
// True if this animation can be performed on the compositor thread. // 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: // Either zero or one for each CSS property:
nsTArray<ElementPropertyTransition> mPropertyTransitions; nsTArray<ElementPropertyTransition> mPropertyTransitions;
@@ -97,6 +104,26 @@ public:
{ {
} }
static ElementTransitions* GetTransitions(nsIContent* aContent) {
return static_cast<ElementTransitions*>
(aContent->GetProperty(nsGkAtoms::transitionsProperty));
}
static ElementTransitions*
GetTransitionsForCompositor(nsIContent* aContent,
nsCSSProperty aProperty)
{
if (!aContent->MayHaveAnimations())
return nullptr;
ElementTransitions* transitions = GetTransitions(aContent);
if (!transitions ||
!transitions->HasTransitionOfProperty(aProperty) ||
!transitions->CanPerformOnCompositorThread()) {
return nullptr;
}
return transitions;
}
/** /**
* StyleContextChanged * StyleContextChanged
* *