Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 "gfxPlatform.h"
11 #include "nsRuleData.h"
12 #include "nsCSSValue.h"
13 #include "nsStyleContext.h"
14 #include "nsIFrame.h"
15 #include "nsLayoutUtils.h"
16 #include "mozilla/LookAndFeel.h"
17 #include "Layers.h"
18 #include "FrameLayerBuilder.h"
19 #include "nsDisplayList.h"
20 #include "mozilla/MemoryReporting.h"
21 #include "RestyleManager.h"
22 #include "nsStyleSet.h"
23 #include "nsStyleChangeList.h"
26 using mozilla::layers::Layer;
28 namespace mozilla {
29 namespace css {
31 /* static */ bool
32 IsGeometricProperty(nsCSSProperty aProperty)
33 {
34 switch (aProperty) {
35 case eCSSProperty_bottom:
36 case eCSSProperty_height:
37 case eCSSProperty_left:
38 case eCSSProperty_right:
39 case eCSSProperty_top:
40 case eCSSProperty_width:
41 return true;
42 default:
43 return false;
44 }
45 }
47 CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext)
48 : mPresContext(aPresContext)
49 {
50 PR_INIT_CLIST(&mElementData);
51 }
53 CommonAnimationManager::~CommonAnimationManager()
54 {
55 NS_ABORT_IF_FALSE(!mPresContext, "Disconnect should have been called");
56 }
58 void
59 CommonAnimationManager::Disconnect()
60 {
61 // Content nodes might outlive the transition or animation manager.
62 RemoveAllElementData();
64 mPresContext = nullptr;
65 }
67 void
68 CommonAnimationManager::RemoveAllElementData()
69 {
70 while (!PR_CLIST_IS_EMPTY(&mElementData)) {
71 CommonElementAnimationData *head =
72 static_cast<CommonElementAnimationData*>(PR_LIST_HEAD(&mElementData));
73 head->Destroy();
74 }
75 }
77 /*
78 * nsISupports implementation
79 */
81 NS_IMPL_ISUPPORTS(CommonAnimationManager, nsIStyleRuleProcessor)
83 nsRestyleHint
84 CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData* aData)
85 {
86 return nsRestyleHint(0);
87 }
89 nsRestyleHint
90 CommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
91 {
92 return nsRestyleHint(0);
93 }
95 bool
96 CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
97 {
98 return false;
99 }
101 nsRestyleHint
102 CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
103 {
104 return nsRestyleHint(0);
105 }
107 /* virtual */ bool
108 CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
109 {
110 return false;
111 }
113 /* virtual */ size_t
114 CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
115 {
116 // Measurement of the following members may be added later if DMD finds it is
117 // worthwhile:
118 // - mElementData
119 //
120 // The following members are not measured
121 // - mPresContext, because it's non-owning
123 return 0;
124 }
126 /* virtual */ size_t
127 CommonAnimationManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
128 {
129 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
130 }
132 /* static */ bool
133 CommonAnimationManager::ExtractComputedValueForTransition(
134 nsCSSProperty aProperty,
135 nsStyleContext* aStyleContext,
136 nsStyleAnimation::Value& aComputedValue)
137 {
138 bool result =
139 nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext,
140 aComputedValue);
141 if (aProperty == eCSSProperty_visibility) {
142 NS_ABORT_IF_FALSE(aComputedValue.GetUnit() ==
143 nsStyleAnimation::eUnit_Enumerated,
144 "unexpected unit");
145 aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
146 nsStyleAnimation::eUnit_Visibility);
147 }
148 return result;
149 }
151 already_AddRefed<nsStyleContext>
152 CommonAnimationManager::ReparentContent(nsIContent* aContent,
153 nsStyleContext* aParentStyle)
154 {
155 nsStyleSet* styleSet = mPresContext->PresShell()->StyleSet();
156 nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aContent);
157 if (!primaryFrame) {
158 return nullptr;
159 }
161 dom::Element* element = aContent->IsElement()
162 ? aContent->AsElement()
163 : nullptr;
165 nsRefPtr<nsStyleContext> newStyle =
166 styleSet->ReparentStyleContext(primaryFrame->StyleContext(),
167 aParentStyle, element);
168 primaryFrame->SetStyleContext(newStyle);
169 ReparentBeforeAndAfter(element, primaryFrame, newStyle, styleSet);
171 return newStyle.forget();
172 }
174 /* static */ void
175 CommonAnimationManager::ReparentBeforeAndAfter(dom::Element* aElement,
176 nsIFrame* aPrimaryFrame,
177 nsStyleContext* aNewStyle,
178 nsStyleSet* aStyleSet)
179 {
180 if (nsIFrame* before = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
181 nsRefPtr<nsStyleContext> beforeStyle =
182 aStyleSet->ReparentStyleContext(before->StyleContext(),
183 aNewStyle, aElement);
184 before->SetStyleContext(beforeStyle);
185 }
186 if (nsIFrame* after = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
187 nsRefPtr<nsStyleContext> afterStyle =
188 aStyleSet->ReparentStyleContext(after->StyleContext(),
189 aNewStyle, aElement);
190 after->SetStyleContext(afterStyle);
191 }
192 }
194 nsStyleContext*
195 CommonAnimationManager::UpdateThrottledStyle(dom::Element* aElement,
196 nsStyleContext* aParentStyle,
197 nsStyleChangeList& aChangeList)
198 {
199 NS_ASSERTION(mPresContext->TransitionManager()->GetElementTransitions(
200 aElement,
201 nsCSSPseudoElements::ePseudo_NotPseudoElement,
202 false) ||
203 mPresContext->AnimationManager()->GetElementAnimations(
204 aElement,
205 nsCSSPseudoElements::ePseudo_NotPseudoElement,
206 false), "element not animated");
208 nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aElement);
209 if (!primaryFrame) {
210 return nullptr;
211 }
213 nsStyleContext* oldStyle = primaryFrame->StyleContext();
214 nsRuleNode* ruleNode = oldStyle->RuleNode();
215 nsTArray<nsStyleSet::RuleAndLevel> rules;
216 do {
217 if (ruleNode->IsRoot()) {
218 break;
219 }
221 nsStyleSet::RuleAndLevel curRule;
222 curRule.mLevel = ruleNode->GetLevel();
224 if (curRule.mLevel == nsStyleSet::eAnimationSheet) {
225 ElementAnimations* ea =
226 mPresContext->AnimationManager()->GetElementAnimations(
227 aElement,
228 oldStyle->GetPseudoType(),
229 false);
230 NS_ASSERTION(ea,
231 "Rule has level eAnimationSheet without animation on manager");
233 mPresContext->AnimationManager()->EnsureStyleRuleFor(ea);
234 curRule.mRule = ea->mStyleRule;
235 } else if (curRule.mLevel == nsStyleSet::eTransitionSheet) {
236 ElementTransitions *et =
237 mPresContext->TransitionManager()->GetElementTransitions(
238 aElement,
239 oldStyle->GetPseudoType(),
240 false);
241 NS_ASSERTION(et,
242 "Rule has level eTransitionSheet without transition on manager");
244 et->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh());
245 curRule.mRule = et->mStyleRule;
246 } else {
247 curRule.mRule = ruleNode->GetRule();
248 }
250 if (curRule.mRule) {
251 rules.AppendElement(curRule);
252 }
253 } while ((ruleNode = ruleNode->GetParent()));
255 nsRefPtr<nsStyleContext> newStyle = mPresContext->PresShell()->StyleSet()->
256 ResolveStyleForRules(aParentStyle, oldStyle, rules);
258 // We absolutely must call CalcStyleDifference in order to ensure the
259 // new context has all the structs cached that the old context had.
260 // We also need it for processing of the changes.
261 nsChangeHint styleChange =
262 oldStyle->CalcStyleDifference(newStyle, nsChangeHint(0));
263 aChangeList.AppendChange(primaryFrame, primaryFrame->GetContent(),
264 styleChange);
266 primaryFrame->SetStyleContext(newStyle);
268 ReparentBeforeAndAfter(aElement, primaryFrame, newStyle,
269 mPresContext->PresShell()->StyleSet());
271 return newStyle;
272 }
274 NS_IMPL_ISUPPORTS(AnimValuesStyleRule, nsIStyleRule)
276 /* virtual */ void
277 AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
278 {
279 nsStyleContext *contextParent = aRuleData->mStyleContext->GetParent();
280 if (contextParent && contextParent->HasPseudoElementData()) {
281 // Don't apply transitions or animations to things inside of
282 // pseudo-elements.
283 // FIXME (Bug 522599): Add tests for this.
284 return;
285 }
287 for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
288 PropertyValuePair &cv = mPropertyValuePairs[i];
289 if (aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(
290 nsCSSProps::kSIDTable[cv.mProperty]))
291 {
292 nsCSSValue *prop = aRuleData->ValueFor(cv.mProperty);
293 if (prop->GetUnit() == eCSSUnit_Null) {
294 #ifdef DEBUG
295 bool ok =
296 #endif
297 nsStyleAnimation::UncomputeValue(cv.mProperty, cv.mValue, *prop);
298 NS_ABORT_IF_FALSE(ok, "could not store computed value");
299 }
300 }
301 }
302 }
304 #ifdef DEBUG
305 /* virtual */ void
306 AnimValuesStyleRule::List(FILE* out, int32_t aIndent) const
307 {
308 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
309 fputs("[anim values] { ", out);
310 for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
311 const PropertyValuePair &pair = mPropertyValuePairs[i];
312 nsAutoString value;
313 nsStyleAnimation::UncomputeValue(pair.mProperty, pair.mValue, value);
314 fprintf(out, "%s: %s; ", nsCSSProps::GetStringValue(pair.mProperty).get(),
315 NS_ConvertUTF16toUTF8(value).get());
316 }
317 fputs("}\n", out);
318 }
319 #endif
321 void
322 ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
323 {
324 mType = aFunction.mType;
325 if (mType == nsTimingFunction::Function) {
326 mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
327 aFunction.mFunc.mX2, aFunction.mFunc.mY2);
328 } else {
329 mSteps = aFunction.mSteps;
330 }
331 }
333 static inline double
334 StepEnd(uint32_t aSteps, double aPortion)
335 {
336 NS_ABORT_IF_FALSE(0.0 <= aPortion && aPortion <= 1.0, "out of range");
337 uint32_t step = uint32_t(aPortion * aSteps); // floor
338 return double(step) / double(aSteps);
339 }
341 double
342 ComputedTimingFunction::GetValue(double aPortion) const
343 {
344 switch (mType) {
345 case nsTimingFunction::Function:
346 return mTimingFunction.GetSplineValue(aPortion);
347 case nsTimingFunction::StepStart:
348 // There are diagrams in the spec that seem to suggest this check
349 // and the bounds point should not be symmetric with StepEnd, but
350 // should actually step up at rather than immediately after the
351 // fraction points. However, we rely on rounding negative values
352 // up to zero, so we can't do that. And it's not clear the spec
353 // really meant it.
354 return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
355 default:
356 NS_ABORT_IF_FALSE(false, "bad type");
357 // fall through
358 case nsTimingFunction::StepEnd:
359 return StepEnd(mSteps, aPortion);
360 }
361 }
363 } /* end sub-namespace css */
365 bool
366 StyleAnimation::IsRunningAt(TimeStamp aTime) const
367 {
368 if (IsPaused() || mIterationDuration.ToMilliseconds() <= 0.0 ||
369 mStartTime.IsNull()) {
370 return false;
371 }
373 double iterationsElapsed = ElapsedDurationAt(aTime) / mIterationDuration;
374 return 0.0 <= iterationsElapsed && iterationsElapsed < mIterationCount;
375 }
377 bool
378 StyleAnimation::HasAnimationOfProperty(nsCSSProperty aProperty) const
379 {
380 for (uint32_t propIdx = 0, propEnd = mProperties.Length();
381 propIdx != propEnd; ++propIdx) {
382 if (aProperty == mProperties[propIdx].mProperty) {
383 return true;
384 }
385 }
386 return false;
387 }
389 namespace css {
391 bool
392 CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element *aElement,
393 nsCSSProperty aProperty,
394 CanAnimateFlags aFlags)
395 {
396 bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
397 if (!gfxPlatform::OffMainThreadCompositingEnabled()) {
398 if (shouldLog) {
399 nsCString message;
400 message.AppendLiteral("Performance warning: Compositor disabled");
401 LogAsyncAnimationFailure(message);
402 }
403 return false;
404 }
406 nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement);
407 if (IsGeometricProperty(aProperty)) {
408 if (shouldLog) {
409 nsCString message;
410 message.AppendLiteral("Performance warning: Async animation of geometric property '");
411 message.Append(nsCSSProps::GetStringValue(aProperty));
412 message.AppendLiteral("' is disabled");
413 LogAsyncAnimationFailure(message, aElement);
414 }
415 return false;
416 }
417 if (aProperty == eCSSProperty_transform) {
418 if (frame->Preserves3D() &&
419 frame->Preserves3DChildren()) {
420 if (shouldLog) {
421 nsCString message;
422 message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");
423 LogAsyncAnimationFailure(message, aElement);
424 }
425 return false;
426 }
427 if (frame->IsSVGTransformed()) {
428 if (shouldLog) {
429 nsCString message;
430 message.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");
431 LogAsyncAnimationFailure(message, aElement);
432 }
433 return false;
434 }
435 if (aFlags & CanAnimate_HasGeometricProperty) {
436 if (shouldLog) {
437 nsCString message;
438 message.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");
439 LogAsyncAnimationFailure(message, aElement);
440 }
441 return false;
442 }
443 }
444 bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled();
445 if (!enabled && shouldLog) {
446 nsCString message;
447 message.AppendLiteral("Performance warning: Async animations are disabled");
448 LogAsyncAnimationFailure(message);
449 }
450 bool propertyAllowed = (aProperty == eCSSProperty_transform) ||
451 (aProperty == eCSSProperty_opacity) ||
452 (aFlags & CanAnimate_AllowPartial);
453 return enabled && propertyAllowed;
454 }
456 /* static */ bool
457 CommonElementAnimationData::IsCompositorAnimationDisabledForFrame(nsIFrame* aFrame)
458 {
459 void* prop = aFrame->Properties().Get(nsIFrame::RefusedAsyncAnimation());
460 return bool(reinterpret_cast<intptr_t>(prop));
461 }
463 /* static */ void
464 CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage,
465 const nsIContent* aContent)
466 {
467 if (aContent) {
468 aMessage.AppendLiteral(" [");
469 aMessage.Append(nsAtomCString(aContent->Tag()));
471 nsIAtom* id = aContent->GetID();
472 if (id) {
473 aMessage.AppendLiteral(" with id '");
474 aMessage.Append(nsAtomCString(aContent->GetID()));
475 aMessage.AppendLiteral("'");
476 }
477 aMessage.AppendLiteral("]");
478 }
479 aMessage.AppendLiteral("\n");
480 printf_stderr(aMessage.get());
481 }
483 bool
484 CommonElementAnimationData::CanThrottleTransformChanges(TimeStamp aTime)
485 {
486 if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
487 return false;
488 }
490 // If we know that the animation cannot cause overflow,
491 // we can just disable flushes for this animation.
493 // If we don't show scrollbars, we don't care about overflow.
494 if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0) {
495 return true;
496 }
498 // If this animation can cause overflow, we can throttle some of the ticks.
499 if ((aTime - mStyleRuleRefreshTime) < TimeDuration::FromMilliseconds(200)) {
500 return true;
501 }
503 // If the nearest scrollable ancestor has overflow:hidden,
504 // we don't care about overflow.
505 nsIScrollableFrame* scrollable = nsLayoutUtils::GetNearestScrollableFrame(
506 nsLayoutUtils::GetStyleFrame(mElement));
507 if (!scrollable) {
508 return true;
509 }
511 ScrollbarStyles ss = scrollable->GetScrollbarStyles();
512 if (ss.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
513 ss.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
514 scrollable->GetLogicalScrollPosition() == nsPoint(0, 0)) {
515 return true;
516 }
518 return false;
519 }
521 bool
522 CommonElementAnimationData::CanThrottleAnimation(TimeStamp aTime)
523 {
524 nsIFrame* frame = nsLayoutUtils::GetStyleFrame(mElement);
525 if (!frame) {
526 return false;
527 }
529 bool hasTransform = HasAnimationOfProperty(eCSSProperty_transform);
530 bool hasOpacity = HasAnimationOfProperty(eCSSProperty_opacity);
531 if (hasOpacity) {
532 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
533 frame, nsDisplayItem::TYPE_OPACITY);
534 if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
535 return false;
536 }
537 }
539 if (!hasTransform) {
540 return true;
541 }
543 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
544 frame, nsDisplayItem::TYPE_TRANSFORM);
545 if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
546 return false;
547 }
549 return CanThrottleTransformChanges(aTime);
550 }
552 void
553 CommonElementAnimationData::UpdateAnimationGeneration(nsPresContext* aPresContext)
554 {
555 mAnimationGeneration =
556 aPresContext->RestyleManager()->GetAnimationGeneration();
557 }
559 }
560 }