layout/style/AnimationCommon.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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 }

mercurial