Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / layout / style / AnimationCommon.cpp
blob0abc42fa61f9e15ad736b95c6ca9110d3797398e
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"
12 #include "nsIFrame.h"
13 #include "nsLayoutUtils.h"
14 #include "mozilla/LookAndFeel.h"
15 #include "Layers.h"
16 #include "FrameLayerBuilder.h"
17 #include "nsDisplayList.h"
18 #include "mozilla/Preferences.h"
20 using namespace mozilla::layers;
22 namespace mozilla {
23 namespace css {
25 /* static */ bool
26 IsGeometricProperty(nsCSSProperty aProperty)
28 switch (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:
35 return true;
36 default:
37 return false;
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");
52 void
53 CommonAnimationManager::Disconnect()
55 // Content nodes might outlive the transition or animation manager.
56 RemoveAllElementData();
58 mPresContext = nullptr;
61 void
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);
73 void
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);
83 void
84 CommonAnimationManager::RemoveAllElementData()
86 while (!PR_CLIST_IS_EMPTY(&mElementData)) {
87 CommonElementAnimationData *head =
88 static_cast<CommonElementAnimationData*>(PR_LIST_HEAD(&mElementData));
89 head->Destroy();
94 * nsISupports implementation
97 NS_IMPL_ISUPPORTS1(CommonAnimationManager, nsIStyleRuleProcessor)
99 nsRestyleHint
100 CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData* aData)
102 return nsRestyleHint(0);
105 bool
106 CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
108 return false;
111 nsRestyleHint
112 CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
114 return nsRestyleHint(0);
117 /* virtual */ bool
118 CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
120 return false;
123 /* virtual */ size_t
124 CommonAnimationManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
126 // Measurement of the following members may be added later if DMD finds it is
127 // worthwhile:
128 // - mElementData
130 // The following members are not measured
131 // - mPresContext, because it's non-owning
133 return 0;
136 /* virtual */ size_t
137 CommonAnimationManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
139 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
142 /* static */ bool
143 CommonAnimationManager::ExtractComputedValueForTransition(
144 nsCSSProperty aProperty,
145 nsStyleContext* aStyleContext,
146 nsStyleAnimation::Value& aComputedValue)
148 bool result =
149 nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext,
150 aComputedValue);
151 if (aProperty == eCSSProperty_visibility) {
152 NS_ABORT_IF_FALSE(aComputedValue.GetUnit() ==
153 nsStyleAnimation::eUnit_Enumerated,
154 "unexpected unit");
155 aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
156 nsStyleAnimation::eUnit_Visibility);
158 return result;
161 /* static */ bool
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)
179 /* virtual */ void
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
185 // pseudo-elements.
186 // FIXME (Bug 522599): Add tests for this.
187 return;
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) {
197 #ifdef DEBUG
198 bool ok =
199 #endif
200 nsStyleAnimation::UncomputeValue(cv.mProperty, cv.mValue, *prop);
201 NS_ABORT_IF_FALSE(ok, "could not store computed value");
207 #ifdef DEBUG
208 /* virtual */ void
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];
215 nsAutoString value;
216 nsStyleAnimation::UncomputeValue(pair.mProperty, pair.mValue, value);
217 fprintf(out, "%s: %s; ", nsCSSProps::GetStringValue(pair.mProperty).get(),
218 NS_ConvertUTF16toUTF8(value).get());
220 fputs("}\n", out);
222 #endif
224 void
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);
231 } else {
232 mSteps = aFunction.mSteps;
236 static inline double
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);
244 double
245 ComputedTimingFunction::GetValue(double aPortion) const
247 switch (mType) {
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
256 // really meant it.
257 return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
258 default:
259 NS_ABORT_IF_FALSE(false, "bad type");
260 // fall through
261 case nsTimingFunction::StepEnd:
262 return StepEnd(mSteps, aPortion);
266 bool
267 CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element *aElement,
268 nsCSSProperty aProperty,
269 CanAnimateFlags aFlags)
271 bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
272 if (shouldLog && !gfxPlatform::OffMainThreadCompositingEnabled()) {
273 nsCString message;
274 message.AppendLiteral("Performance warning: Compositor disabled");
275 LogAsyncAnimationFailure(message);
276 return false;
279 nsIFrame* frame = aElement->GetPrimaryFrame();
280 if (IsGeometricProperty(aProperty)) {
281 if (shouldLog) {
282 nsCString message;
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);
288 return false;
290 if (aProperty == eCSSProperty_opacity) {
291 bool enabled = nsLayoutUtils::AreOpacityAnimationsEnabled();
292 if (!enabled && shouldLog) {
293 nsCString message;
294 message.AppendLiteral("Performance warning: Async animation of 'opacity' is disabled");
295 LogAsyncAnimationFailure(message);
297 return enabled;
299 if (aProperty == eCSSProperty_transform) {
300 if (frame->Preserves3D() &&
301 frame->Preserves3DChildren()) {
302 if (shouldLog) {
303 nsCString message;
304 message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");
305 LogAsyncAnimationFailure(message, aElement);
307 return false;
309 if (frame->IsSVGTransformed()) {
310 if (shouldLog) {
311 nsCString message;
312 message.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");
313 LogAsyncAnimationFailure(message, aElement);
315 return false;
317 if (aFlags & CanAnimate_HasGeometricProperty) {
318 if (shouldLog) {
319 nsCString message;
320 message.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");
321 LogAsyncAnimationFailure(message, aElement);
323 return false;
325 bool enabled = nsLayoutUtils::AreTransformAnimationsEnabled();
326 if (!enabled && shouldLog) {
327 nsCString message;
328 message.AppendLiteral("Performance warning: Async animation of 'transform' is disabled");
329 LogAsyncAnimationFailure(message);
331 return enabled;
333 return aFlags & CanAnimate_AllowPartial;
336 /* static */ void
337 CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage,
338 const nsIContent* aContent)
340 if (aContent) {
341 aMessage.AppendLiteral(" [");
342 aMessage.Append(nsAtomCString(aContent->Tag()));
344 nsIAtom* id = aContent->GetID();
345 if (id) {
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());
356 bool
357 CommonElementAnimationData::CanThrottleTransformChanges(TimeStamp aTime)
359 if (!CommonAnimationManager::ThrottlingEnabled()) {
360 return false;
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) {
368 return true;
371 // If this animation can cause overflow, we can throttle some of the ticks.
372 if ((aTime - mStyleRuleRefreshTime) < TimeDuration::FromMilliseconds(200)) {
373 return true;
376 // If the nearest scrollable ancestor has overflow:hidden,
377 // we don't care about overflow.
378 nsIScrollableFrame* scrollable =
379 nsLayoutUtils::GetNearestScrollableFrame(mElement->GetPrimaryFrame());
380 if (!scrollable) {
381 return true;
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)) {
388 return true;
391 return false;
394 bool
395 CommonElementAnimationData::CanThrottleAnimation(TimeStamp aTime)
397 nsIFrame* frame = mElement->GetPrimaryFrame();
398 if (!frame) {
399 return false;
402 bool hasTransform = HasAnimationOfProperty(eCSSProperty_transform);
403 bool hasOpacity = HasAnimationOfProperty(eCSSProperty_opacity);
404 if (hasOpacity) {
405 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
406 frame, nsDisplayItem::TYPE_OPACITY);
407 if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
408 return false;
412 if (!hasTransform) {
413 return true;
416 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
417 frame, nsDisplayItem::TYPE_TRANSFORM);
418 if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
419 return false;
422 return CanThrottleTransformChanges(aTime);
425 void
426 CommonElementAnimationData::UpdateAnimationGeneration(nsPresContext* aPresContext)
428 mAnimationGeneration =
429 aPresContext->PresShell()->FrameConstructor()->GetAnimationGeneration();