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 "gfxPlatform.h"
7 #include "AnimationCommon.h"
8 #include "nsRuleData.h"
9 #include "nsCSSFrameConstructor.h"
10 #include "nsCSSValue.h"
11 #include "nsStyleContext.h"
13 #include "nsLayoutUtils.h"
14 #include "mozilla/LookAndFeel.h"
16 #include "FrameLayerBuilder.h"
17 #include "nsDisplayList.h"
18 #include "mozilla/Preferences.h"
20 using namespace mozilla::layers
;
26 IsGeometricProperty(nsCSSProperty aProperty
)
29 case eCSSProperty_bottom
:
30 case eCSSProperty_height
:
31 case eCSSProperty_left
:
32 case eCSSProperty_right
:
33 case eCSSProperty_top
:
34 case eCSSProperty_width
:
41 CommonAnimationManager::CommonAnimationManager(nsPresContext
*aPresContext
)
42 : mPresContext(aPresContext
)
44 PR_INIT_CLIST(&mElementData
);
47 CommonAnimationManager::~CommonAnimationManager()
49 NS_ABORT_IF_FALSE(!mPresContext
, "Disconnect should have been called");
53 CommonAnimationManager::Disconnect()
55 // Content nodes might outlive the transition or animation manager.
56 RemoveAllElementData();
58 mPresContext
= nullptr;
62 CommonAnimationManager::AddElementData(CommonElementAnimationData
* aData
)
64 if (PR_CLIST_IS_EMPTY(&mElementData
)) {
65 // We need to observe the refresh driver.
66 nsRefreshDriver
*rd
= mPresContext
->RefreshDriver();
67 rd
->AddRefreshObserver(this, Flush_Style
);
70 PR_INSERT_BEFORE(aData
, &mElementData
);
74 CommonAnimationManager::ElementDataRemoved()
76 // If we have no transitions or animations left, remove ourselves from
77 // the refresh driver.
78 if (PR_CLIST_IS_EMPTY(&mElementData
)) {
79 mPresContext
->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style
);
84 CommonAnimationManager::RemoveAllElementData()
86 while (!PR_CLIST_IS_EMPTY(&mElementData
)) {
87 CommonElementAnimationData
*head
=
88 static_cast<CommonElementAnimationData
*>(PR_LIST_HEAD(&mElementData
));
94 * nsISupports implementation
97 NS_IMPL_ISUPPORTS1(CommonAnimationManager
, nsIStyleRuleProcessor
)
100 CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData
* aData
)
102 return nsRestyleHint(0);
106 CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData
* aData
)
112 CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData
* aData
)
114 return nsRestyleHint(0);
118 CommonAnimationManager::MediumFeaturesChanged(nsPresContext
* aPresContext
)
124 CommonAnimationManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf
) const
126 // Measurement of the following members may be added later if DMD finds it is
130 // The following members are not measured
131 // - mPresContext, because it's non-owning
137 CommonAnimationManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf
) const
139 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
143 CommonAnimationManager::ExtractComputedValueForTransition(
144 nsCSSProperty aProperty
,
145 nsStyleContext
* aStyleContext
,
146 nsStyleAnimation::Value
& aComputedValue
)
149 nsStyleAnimation::ExtractComputedValue(aProperty
, aStyleContext
,
151 if (aProperty
== eCSSProperty_visibility
) {
152 NS_ABORT_IF_FALSE(aComputedValue
.GetUnit() ==
153 nsStyleAnimation::eUnit_Enumerated
,
155 aComputedValue
.SetIntValue(aComputedValue
.GetIntValue(),
156 nsStyleAnimation::eUnit_Visibility
);
162 CommonAnimationManager::ThrottlingEnabled()
164 static bool sThrottlePref
= false;
165 static bool sThrottlePrefCached
= false;
167 if (!sThrottlePrefCached
) {
168 Preferences::AddBoolVarCache(&sThrottlePref
,
169 "layers.offmainthreadcomposition.throttle-animations", false);
170 sThrottlePrefCached
= true;
173 return sThrottlePref
;
177 NS_IMPL_ISUPPORTS1(AnimValuesStyleRule
, nsIStyleRule
)
180 AnimValuesStyleRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
182 nsStyleContext
*contextParent
= aRuleData
->mStyleContext
->GetParent();
183 if (contextParent
&& contextParent
->HasPseudoElementData()) {
184 // Don't apply transitions or animations to things inside of
186 // FIXME (Bug 522599): Add tests for this.
190 for (uint32_t i
= 0, i_end
= mPropertyValuePairs
.Length(); i
< i_end
; ++i
) {
191 PropertyValuePair
&cv
= mPropertyValuePairs
[i
];
192 if (aRuleData
->mSIDs
& nsCachedStyleData::GetBitForSID(
193 nsCSSProps::kSIDTable
[cv
.mProperty
]))
195 nsCSSValue
*prop
= aRuleData
->ValueFor(cv
.mProperty
);
196 if (prop
->GetUnit() == eCSSUnit_Null
) {
200 nsStyleAnimation::UncomputeValue(cv
.mProperty
, cv
.mValue
, *prop
);
201 NS_ABORT_IF_FALSE(ok
, "could not store computed value");
209 AnimValuesStyleRule::List(FILE* out
, int32_t aIndent
) const
211 for (int32_t index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
212 fputs("[anim values] { ", out
);
213 for (uint32_t i
= 0, i_end
= mPropertyValuePairs
.Length(); i
< i_end
; ++i
) {
214 const PropertyValuePair
&pair
= mPropertyValuePairs
[i
];
216 nsStyleAnimation::UncomputeValue(pair
.mProperty
, pair
.mValue
, value
);
217 fprintf(out
, "%s: %s; ", nsCSSProps::GetStringValue(pair
.mProperty
).get(),
218 NS_ConvertUTF16toUTF8(value
).get());
225 ComputedTimingFunction::Init(const nsTimingFunction
&aFunction
)
227 mType
= aFunction
.mType
;
228 if (mType
== nsTimingFunction::Function
) {
229 mTimingFunction
.Init(aFunction
.mFunc
.mX1
, aFunction
.mFunc
.mY1
,
230 aFunction
.mFunc
.mX2
, aFunction
.mFunc
.mY2
);
232 mSteps
= aFunction
.mSteps
;
237 StepEnd(uint32_t aSteps
, double aPortion
)
239 NS_ABORT_IF_FALSE(0.0 <= aPortion
&& aPortion
<= 1.0, "out of range");
240 uint32_t step
= uint32_t(aPortion
* aSteps
); // floor
241 return double(step
) / double(aSteps
);
245 ComputedTimingFunction::GetValue(double aPortion
) const
248 case nsTimingFunction::Function
:
249 return mTimingFunction
.GetSplineValue(aPortion
);
250 case nsTimingFunction::StepStart
:
251 // There are diagrams in the spec that seem to suggest this check
252 // and the bounds point should not be symmetric with StepEnd, but
253 // should actually step up at rather than immediately after the
254 // fraction points. However, we rely on rounding negative values
255 // up to zero, so we can't do that. And it's not clear the spec
257 return 1.0 - StepEnd(mSteps
, 1.0 - aPortion
);
259 NS_ABORT_IF_FALSE(false, "bad type");
261 case nsTimingFunction::StepEnd
:
262 return StepEnd(mSteps
, aPortion
);
267 CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element
*aElement
,
268 nsCSSProperty aProperty
,
269 CanAnimateFlags aFlags
)
271 bool shouldLog
= nsLayoutUtils::IsAnimationLoggingEnabled();
272 if (shouldLog
&& !gfxPlatform::OffMainThreadCompositingEnabled()) {
274 message
.AppendLiteral("Performance warning: Compositor disabled");
275 LogAsyncAnimationFailure(message
);
279 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
280 if (IsGeometricProperty(aProperty
)) {
283 message
.AppendLiteral("Performance warning: Async animation of geometric property '");
284 message
.Append(nsCSSProps::GetStringValue(aProperty
));
285 message
.AppendLiteral("' is disabled");
286 LogAsyncAnimationFailure(message
, aElement
);
290 if (aProperty
== eCSSProperty_opacity
) {
291 bool enabled
= nsLayoutUtils::AreOpacityAnimationsEnabled();
292 if (!enabled
&& shouldLog
) {
294 message
.AppendLiteral("Performance warning: Async animation of 'opacity' is disabled");
295 LogAsyncAnimationFailure(message
);
299 if (aProperty
== eCSSProperty_transform
) {
300 if (frame
->Preserves3D() &&
301 frame
->Preserves3DChildren()) {
304 message
.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");
305 LogAsyncAnimationFailure(message
, aElement
);
309 if (frame
->IsSVGTransformed()) {
312 message
.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");
313 LogAsyncAnimationFailure(message
, aElement
);
317 if (aFlags
& CanAnimate_HasGeometricProperty
) {
320 message
.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");
321 LogAsyncAnimationFailure(message
, aElement
);
325 bool enabled
= nsLayoutUtils::AreTransformAnimationsEnabled();
326 if (!enabled
&& shouldLog
) {
328 message
.AppendLiteral("Performance warning: Async animation of 'transform' is disabled");
329 LogAsyncAnimationFailure(message
);
333 return aFlags
& CanAnimate_AllowPartial
;
337 CommonElementAnimationData::LogAsyncAnimationFailure(nsCString
& aMessage
,
338 const nsIContent
* aContent
)
341 aMessage
.AppendLiteral(" [");
342 aMessage
.Append(nsAtomCString(aContent
->Tag()));
344 nsIAtom
* id
= aContent
->GetID();
346 aMessage
.AppendLiteral(" with id '");
347 aMessage
.Append(nsAtomCString(aContent
->GetID()));
348 aMessage
.AppendLiteral("'");
350 aMessage
.AppendLiteral("]");
352 aMessage
.AppendLiteral("\n");
353 printf_stderr(aMessage
.get());
357 CommonElementAnimationData::CanThrottleTransformChanges(TimeStamp aTime
)
359 if (!CommonAnimationManager::ThrottlingEnabled()) {
363 // If we know that the animation cannot cause overflow,
364 // we can just disable flushes for this animation.
366 // If we don't show scrollbars, we don't care about overflow.
367 if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars
) == 0) {
371 // If this animation can cause overflow, we can throttle some of the ticks.
372 if ((aTime
- mStyleRuleRefreshTime
) < TimeDuration::FromMilliseconds(200)) {
376 // If the nearest scrollable ancestor has overflow:hidden,
377 // we don't care about overflow.
378 nsIScrollableFrame
* scrollable
=
379 nsLayoutUtils::GetNearestScrollableFrame(mElement
->GetPrimaryFrame());
384 nsPresContext::ScrollbarStyles ss
= scrollable
->GetScrollbarStyles();
385 if (ss
.mVertical
== NS_STYLE_OVERFLOW_HIDDEN
&&
386 ss
.mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
&&
387 scrollable
->GetLogicalScrollPosition() == nsPoint(0, 0)) {
395 CommonElementAnimationData::CanThrottleAnimation(TimeStamp aTime
)
397 nsIFrame
* frame
= mElement
->GetPrimaryFrame();
402 bool hasTransform
= HasAnimationOfProperty(eCSSProperty_transform
);
403 bool hasOpacity
= HasAnimationOfProperty(eCSSProperty_opacity
);
405 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(
406 frame
, nsDisplayItem::TYPE_OPACITY
);
407 if (!layer
|| mAnimationGeneration
> layer
->GetAnimationGeneration()) {
416 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(
417 frame
, nsDisplayItem::TYPE_TRANSFORM
);
418 if (!layer
|| mAnimationGeneration
> layer
->GetAnimationGeneration()) {
422 return CanThrottleTransformChanges(aTime
);
426 CommonElementAnimationData::UpdateAnimationGeneration(nsPresContext
* aPresContext
)
428 mAnimationGeneration
=
429 aPresContext
->PresShell()->FrameConstructor()->GetAnimationGeneration();