1 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "AnimationCommon.h"
7 #include "nsTransitionManager.h"
8 #include "nsAnimationManager.h"
10 #include "ActiveLayerTracker.h"
11 #include "gfxPlatform.h"
12 #include "nsRuleData.h"
13 #include "nsCSSPropertySet.h"
14 #include "nsCSSValue.h"
15 #include "nsCycleCollectionParticipant.h"
16 #include "nsStyleContext.h"
18 #include "nsLayoutUtils.h"
19 #include "mozilla/LookAndFeel.h"
21 #include "FrameLayerBuilder.h"
22 #include "nsDisplayList.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "RestyleManager.h"
25 #include "nsStyleSet.h"
26 #include "nsStyleChangeList.h"
29 using mozilla::layers::Layer
;
30 using mozilla::dom::AnimationPlayer
;
31 using mozilla::dom::Animation
;
36 IsGeometricProperty(nsCSSProperty aProperty
)
39 case eCSSProperty_bottom
:
40 case eCSSProperty_height
:
41 case eCSSProperty_left
:
42 case eCSSProperty_right
:
43 case eCSSProperty_top
:
44 case eCSSProperty_width
:
53 CommonAnimationManager::CommonAnimationManager(nsPresContext
*aPresContext
)
54 : mPresContext(aPresContext
)
55 , mIsObservingRefreshDriver(false)
57 PR_INIT_CLIST(&mElementCollections
);
60 CommonAnimationManager::~CommonAnimationManager()
62 NS_ABORT_IF_FALSE(!mPresContext
, "Disconnect should have been called");
66 CommonAnimationManager::Disconnect()
68 // Content nodes might outlive the transition or animation manager.
69 RemoveAllElementCollections();
71 mPresContext
= nullptr;
75 CommonAnimationManager::AddElementCollection(AnimationPlayerCollection
*
78 if (!mIsObservingRefreshDriver
) {
79 NS_ASSERTION(aCollection
->mNeedsRefreshes
,
80 "Added data which doesn't need refreshing?");
81 // We need to observe the refresh driver.
82 mPresContext
->RefreshDriver()->AddRefreshObserver(this, Flush_Style
);
83 mIsObservingRefreshDriver
= true;
86 PR_INSERT_BEFORE(aCollection
, &mElementCollections
);
90 CommonAnimationManager::RemoveAllElementCollections()
92 while (!PR_CLIST_IS_EMPTY(&mElementCollections
)) {
93 AnimationPlayerCollection
* head
=
94 static_cast<AnimationPlayerCollection
*>(
95 PR_LIST_HEAD(&mElementCollections
));
101 CommonAnimationManager::CheckNeedsRefresh()
103 for (PRCList
*l
= PR_LIST_HEAD(&mElementCollections
);
104 l
!= &mElementCollections
;
105 l
= PR_NEXT_LINK(l
)) {
106 if (static_cast<AnimationPlayerCollection
*>(l
)->mNeedsRefreshes
) {
107 if (!mIsObservingRefreshDriver
) {
108 mPresContext
->RefreshDriver()->AddRefreshObserver(this, Flush_Style
);
109 mIsObservingRefreshDriver
= true;
114 if (mIsObservingRefreshDriver
) {
115 mIsObservingRefreshDriver
= false;
116 mPresContext
->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style
);
120 AnimationPlayerCollection
*
121 CommonAnimationManager::GetAnimationsForCompositor(nsIContent
* aContent
,
122 nsIAtom
* aElementProperty
,
123 nsCSSProperty aProperty
)
125 if (!aContent
->MayHaveAnimations())
127 AnimationPlayerCollection
* collection
=
128 static_cast<AnimationPlayerCollection
*>(
129 aContent
->GetProperty(aElementProperty
));
131 !collection
->HasAnimationOfProperty(aProperty
) ||
132 !collection
->CanPerformOnCompositorThread(
133 AnimationPlayerCollection::CanAnimate_AllowPartial
)) {
137 // This animation can be done on the compositor.
138 // Mark the frame as active, in case we are able to throttle this animation.
139 nsIFrame
* frame
= nsLayoutUtils::GetStyleFrame(collection
->mElement
);
141 if (aProperty
== eCSSProperty_opacity
) {
142 ActiveLayerTracker::NotifyAnimated(frame
, eCSSProperty_opacity
);
143 } else if (aProperty
== eCSSProperty_transform
) {
144 ActiveLayerTracker::NotifyAnimated(frame
, eCSSProperty_transform
);
152 * nsISupports implementation
155 NS_IMPL_ISUPPORTS(CommonAnimationManager
, nsIStyleRuleProcessor
)
158 CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData
* aData
)
160 return nsRestyleHint(0);
164 CommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData
* aData
)
166 return nsRestyleHint(0);
170 CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData
* aData
)
176 CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData
* aData
)
178 return nsRestyleHint(0);
182 CommonAnimationManager::MediumFeaturesChanged(nsPresContext
* aPresContext
)
188 CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
190 // Measurement of the following members may be added later if DMD finds it is
192 // - mElementCollections
194 // The following members are not measured
195 // - mPresContext, because it's non-owning
201 CommonAnimationManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
203 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
207 CommonAnimationManager::AddStyleUpdatesTo(RestyleTracker
& aTracker
)
209 TimeStamp now
= mPresContext
->RefreshDriver()->MostRecentRefresh();
211 PRCList
* next
= PR_LIST_HEAD(&mElementCollections
);
212 while (next
!= &mElementCollections
) {
213 AnimationPlayerCollection
* collection
=
214 static_cast<AnimationPlayerCollection
*>(next
);
215 next
= PR_NEXT_LINK(next
);
217 collection
->EnsureStyleRuleFor(now
, EnsureStyleRule_IsNotThrottled
);
219 dom::Element
* elementToRestyle
= collection
->GetElementToRestyle();
220 if (elementToRestyle
) {
221 nsRestyleHint rshint
= collection
->IsForTransitions()
222 ? eRestyle_CSSTransitions
: eRestyle_CSSAnimations
;
223 aTracker
.AddPendingRestyle(elementToRestyle
, rshint
, nsChangeHint(0));
229 CommonAnimationManager::NotifyCollectionUpdated(AnimationPlayerCollection
&
233 mPresContext
->ClearLastStyleUpdateForAllAnimations();
234 aCollection
.PostRestyleForAnimation(mPresContext
);
238 CommonAnimationManager::ExtractComputedValueForTransition(
239 nsCSSProperty aProperty
,
240 nsStyleContext
* aStyleContext
,
241 StyleAnimationValue
& aComputedValue
)
243 bool result
= StyleAnimationValue::ExtractComputedValue(aProperty
,
246 if (aProperty
== eCSSProperty_visibility
) {
247 NS_ABORT_IF_FALSE(aComputedValue
.GetUnit() ==
248 StyleAnimationValue::eUnit_Enumerated
,
250 aComputedValue
.SetIntValue(aComputedValue
.GetIntValue(),
251 StyleAnimationValue::eUnit_Visibility
);
256 NS_IMPL_ISUPPORTS(AnimValuesStyleRule
, nsIStyleRule
)
259 AnimValuesStyleRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
261 nsStyleContext
*contextParent
= aRuleData
->mStyleContext
->GetParent();
262 if (contextParent
&& contextParent
->HasPseudoElementData()) {
263 // Don't apply transitions or animations to things inside of
265 // FIXME (Bug 522599): Add tests for this.
269 for (uint32_t i
= 0, i_end
= mPropertyValuePairs
.Length(); i
< i_end
; ++i
) {
270 PropertyValuePair
&cv
= mPropertyValuePairs
[i
];
271 if (aRuleData
->mSIDs
& nsCachedStyleData::GetBitForSID(
272 nsCSSProps::kSIDTable
[cv
.mProperty
]))
274 nsCSSValue
*prop
= aRuleData
->ValueFor(cv
.mProperty
);
275 if (prop
->GetUnit() == eCSSUnit_Null
) {
279 StyleAnimationValue::UncomputeValue(cv
.mProperty
, cv
.mValue
, *prop
);
280 NS_ABORT_IF_FALSE(ok
, "could not store computed value");
288 AnimValuesStyleRule::List(FILE* out
, int32_t aIndent
) const
290 for (int32_t index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
291 fputs("[anim values] { ", out
);
292 for (uint32_t i
= 0, i_end
= mPropertyValuePairs
.Length(); i
< i_end
; ++i
) {
293 const PropertyValuePair
&pair
= mPropertyValuePairs
[i
];
295 StyleAnimationValue::UncomputeValue(pair
.mProperty
, pair
.mValue
, value
);
296 fprintf(out
, "%s: %s; ", nsCSSProps::GetStringValue(pair
.mProperty
).get(),
297 NS_ConvertUTF16toUTF8(value
).get());
303 } /* end sub-namespace css */
306 AnimationPlayerCollection::CanAnimatePropertyOnCompositor(
307 const dom::Element
*aElement
,
308 nsCSSProperty aProperty
,
309 CanAnimateFlags aFlags
)
311 bool shouldLog
= nsLayoutUtils::IsAnimationLoggingEnabled();
312 if (!gfxPlatform::OffMainThreadCompositingEnabled()) {
315 message
.AppendLiteral("Performance warning: Compositor disabled");
316 LogAsyncAnimationFailure(message
);
321 nsIFrame
* frame
= nsLayoutUtils::GetStyleFrame(aElement
);
322 if (IsGeometricProperty(aProperty
)) {
325 message
.AppendLiteral("Performance warning: Async animation of geometric property '");
326 message
.Append(nsCSSProps::GetStringValue(aProperty
));
327 message
.AppendLiteral("' is disabled");
328 LogAsyncAnimationFailure(message
, aElement
);
332 if (aProperty
== eCSSProperty_transform
) {
333 if (frame
->Preserves3D() &&
334 frame
->Preserves3DChildren()) {
337 message
.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");
338 LogAsyncAnimationFailure(message
, aElement
);
342 if (frame
->IsSVGTransformed()) {
345 message
.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");
346 LogAsyncAnimationFailure(message
, aElement
);
350 if (aFlags
& CanAnimate_HasGeometricProperty
) {
353 message
.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");
354 LogAsyncAnimationFailure(message
, aElement
);
359 bool enabled
= nsLayoutUtils::AreAsyncAnimationsEnabled();
360 if (!enabled
&& shouldLog
) {
362 message
.AppendLiteral("Performance warning: Async animations are disabled");
363 LogAsyncAnimationFailure(message
);
365 bool propertyAllowed
= (aProperty
== eCSSProperty_transform
) ||
366 (aProperty
== eCSSProperty_opacity
) ||
367 (aFlags
& CanAnimate_AllowPartial
);
368 return enabled
&& propertyAllowed
;
372 AnimationPlayerCollection::IsCompositorAnimationDisabledForFrame(
375 void* prop
= aFrame
->Properties().Get(nsIFrame::RefusedAsyncAnimation());
376 return bool(reinterpret_cast<intptr_t>(prop
));
380 AnimationPlayerCollection::CanPerformOnCompositorThread(
381 CanAnimateFlags aFlags
) const
383 nsIFrame
* frame
= nsLayoutUtils::GetStyleFrame(mElement
);
388 if (mElementProperty
!= nsGkAtoms::transitionsProperty
&&
389 mElementProperty
!= nsGkAtoms::animationsProperty
) {
390 if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
392 message
.AppendLiteral("Gecko bug: Async animation of pseudoelements"
393 " not supported. See bug 771367 (");
394 message
.Append(nsAtomCString(mElementProperty
));
396 LogAsyncAnimationFailure(message
, mElement
);
401 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
402 const AnimationPlayer
* player
= mPlayers
[playerIdx
];
403 if (!player
->IsRunning() || !player
->GetSource()) {
406 const Animation
* anim
= player
->GetSource();
407 for (size_t propIdx
= 0, propEnd
= anim
->Properties().Length();
408 propIdx
!= propEnd
; ++propIdx
) {
409 if (IsGeometricProperty(anim
->Properties()[propIdx
].mProperty
)) {
410 aFlags
= CanAnimateFlags(aFlags
| CanAnimate_HasGeometricProperty
);
416 bool existsProperty
= false;
417 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
418 const AnimationPlayer
* player
= mPlayers
[playerIdx
];
419 if (!player
->IsRunning() || !player
->GetSource()) {
423 const Animation
* anim
= player
->GetSource();
424 existsProperty
= existsProperty
|| anim
->Properties().Length() > 0;
426 for (size_t propIdx
= 0, propEnd
= anim
->Properties().Length();
427 propIdx
!= propEnd
; ++propIdx
) {
428 const AnimationProperty
& prop
= anim
->Properties()[propIdx
];
429 if (!CanAnimatePropertyOnCompositor(mElement
,
432 IsCompositorAnimationDisabledForFrame(frame
)) {
438 // No properties to animate
439 if (!existsProperty
) {
447 AnimationPlayerCollection::HasAnimationOfProperty(
448 nsCSSProperty aProperty
) const
450 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
451 const Animation
* anim
= mPlayers
[playerIdx
]->GetSource();
452 if (anim
&& anim
->HasAnimationOfProperty(aProperty
) &&
453 !anim
->IsFinishedTransition()) {
460 mozilla::dom::Element
*
461 AnimationPlayerCollection::GetElementToRestyle() const
463 if (IsForElement()) {
467 nsIFrame
* primaryFrame
= mElement
->GetPrimaryFrame();
471 nsIFrame
* pseudoFrame
;
472 if (IsForBeforePseudo()) {
473 pseudoFrame
= nsLayoutUtils::GetBeforeFrame(primaryFrame
);
474 } else if (IsForAfterPseudo()) {
475 pseudoFrame
= nsLayoutUtils::GetAfterFrame(primaryFrame
);
477 MOZ_ASSERT(false, "unknown mElementProperty");
483 return pseudoFrame
->GetContent()->AsElement();
487 AnimationPlayerCollection::NotifyPlayerUpdated()
489 // On the next flush, force us to update the style rule
490 mNeedsRefreshes
= true;
491 mStyleRuleRefreshTime
= TimeStamp();
493 mManager
->NotifyCollectionUpdated(*this);
497 AnimationPlayerCollection::LogAsyncAnimationFailure(nsCString
& aMessage
,
498 const nsIContent
* aContent
)
501 aMessage
.AppendLiteral(" [");
502 aMessage
.Append(nsAtomCString(aContent
->Tag()));
504 nsIAtom
* id
= aContent
->GetID();
506 aMessage
.AppendLiteral(" with id '");
507 aMessage
.Append(nsAtomCString(aContent
->GetID()));
508 aMessage
.Append('\'');
510 aMessage
.Append(']');
512 aMessage
.Append('\n');
513 printf_stderr("%s", aMessage
.get());
517 AnimationPlayerCollection::PropertyDtor(void *aObject
, nsIAtom
*aPropertyName
,
518 void *aPropertyValue
, void *aData
)
520 AnimationPlayerCollection
* collection
=
521 static_cast<AnimationPlayerCollection
*>(aPropertyValue
);
523 NS_ABORT_IF_FALSE(!collection
->mCalledPropertyDtor
, "can't call dtor twice");
524 collection
->mCalledPropertyDtor
= true;
530 AnimationPlayerCollection::Tick()
532 for (size_t playerIdx
= 0, playerEnd
= mPlayers
.Length();
533 playerIdx
!= playerEnd
; playerIdx
++) {
534 mPlayers
[playerIdx
]->Tick();
539 AnimationPlayerCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime
,
540 EnsureStyleRuleFlags aFlags
)
542 if (!mNeedsRefreshes
) {
543 mStyleRuleRefreshTime
= aRefreshTime
;
547 // If we're performing animations on the compositor thread, then we can skip
548 // most of the work in this method. But even if we are throttled, then we
549 // have to do the work if an animation is ending in order to get correct end
550 // of animation behaviour (the styles of the animation disappear, or the fill
551 // mode behaviour). CanThrottle returns false for any finishing animations
552 // so we can force style recalculation in that case.
553 if (aFlags
== EnsureStyleRule_IsThrottled
) {
554 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
555 if (!mPlayers
[playerIdx
]->CanThrottle()) {
556 aFlags
= EnsureStyleRule_IsNotThrottled
;
562 if (aFlags
== EnsureStyleRule_IsThrottled
) {
566 // mStyleRule may be null and valid, if we have no style to apply.
567 if (mStyleRuleRefreshTime
.IsNull() ||
568 mStyleRuleRefreshTime
!= aRefreshTime
) {
569 mStyleRuleRefreshTime
= aRefreshTime
;
570 mStyleRule
= nullptr;
571 // We'll set mNeedsRefreshes to true below in all cases where we need them.
572 mNeedsRefreshes
= false;
574 // If multiple animations specify behavior for the same property the
575 // animation which occurs last in the value of animation-name wins.
576 // As a result, we iterate from last animation to first and, if a
577 // property has already been set, we don't leave it.
578 nsCSSPropertySet properties
;
580 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
581 mPlayers
[playerIdx
]->ComposeStyle(mStyleRule
, properties
,
586 mManager
->CheckNeedsRefresh();
590 AnimationPlayerCollection::CanThrottleTransformChanges(TimeStamp aTime
)
592 if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
596 // If we know that the animation cannot cause overflow,
597 // we can just disable flushes for this animation.
599 // If we don't show scrollbars, we don't care about overflow.
600 if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars
) == 0) {
604 // If this animation can cause overflow, we can throttle some of the ticks.
605 if ((aTime
- mStyleRuleRefreshTime
) < TimeDuration::FromMilliseconds(200)) {
609 // If the nearest scrollable ancestor has overflow:hidden,
610 // we don't care about overflow.
611 nsIScrollableFrame
* scrollable
= nsLayoutUtils::GetNearestScrollableFrame(
612 nsLayoutUtils::GetStyleFrame(mElement
));
617 ScrollbarStyles ss
= scrollable
->GetScrollbarStyles();
618 if (ss
.mVertical
== NS_STYLE_OVERFLOW_HIDDEN
&&
619 ss
.mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
&&
620 scrollable
->GetLogicalScrollPosition() == nsPoint(0, 0)) {
628 AnimationPlayerCollection::CanThrottleAnimation(TimeStamp aTime
)
630 nsIFrame
* frame
= nsLayoutUtils::GetStyleFrame(mElement
);
635 bool hasTransform
= HasAnimationOfProperty(eCSSProperty_transform
);
636 bool hasOpacity
= HasAnimationOfProperty(eCSSProperty_opacity
);
638 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(
639 frame
, nsDisplayItem::TYPE_OPACITY
);
640 if (!layer
|| mAnimationGeneration
> layer
->GetAnimationGeneration()) {
649 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(
650 frame
, nsDisplayItem::TYPE_TRANSFORM
);
651 if (!layer
|| mAnimationGeneration
> layer
->GetAnimationGeneration()) {
655 return CanThrottleTransformChanges(aTime
);
659 AnimationPlayerCollection::UpdateAnimationGeneration(
660 nsPresContext
* aPresContext
)
662 mAnimationGeneration
=
663 aPresContext
->RestyleManager()->GetAnimationGeneration();
667 AnimationPlayerCollection::HasCurrentAnimations() const
669 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
670 if (mPlayers
[playerIdx
]->HasCurrentSource()) {
679 AnimationPlayerCollection::HasCurrentAnimationsForProperty(nsCSSProperty
682 for (size_t playerIdx
= mPlayers
.Length(); playerIdx
-- != 0; ) {
683 const Animation
* anim
= mPlayers
[playerIdx
]->GetSource();
684 if (anim
&& anim
->IsCurrent() && anim
->HasAnimationOfProperty(aProperty
)) {