gecko-dev/layout/style/AnimationCommon.cpp

976 строки
29 KiB
C++
Исходник Обычный вид История

/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
2012-05-21 15:12:37 +04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AnimationCommon.h"
#include "nsTransitionManager.h"
#include "nsAnimationManager.h"
#include "ActiveLayerTracker.h"
#include "gfxPlatform.h"
#include "nsRuleData.h"
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
#include "nsCSSPropertySet.h"
#include "nsCSSValue.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMMutationObserver.h"
#include "nsStyleContext.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "mozilla/LookAndFeel.h"
#include "Layers.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "RestyleManager.h"
#include "nsRuleProcessorData.h"
#include "nsStyleSet.h"
#include "nsStyleChangeList.h"
using mozilla::layers::Layer;
using mozilla::dom::Animation;
using mozilla::dom::KeyframeEffectReadOnly;
namespace mozilla {
/* static */ bool
IsGeometricProperty(nsCSSProperty aProperty)
{
switch (aProperty) {
case eCSSProperty_bottom:
case eCSSProperty_height:
case eCSSProperty_left:
case eCSSProperty_right:
case eCSSProperty_top:
case eCSSProperty_width:
return true;
default:
return false;
}
}
namespace css {
CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext)
: mPresContext(aPresContext)
, mIsObservingRefreshDriver(false)
{
PR_INIT_CLIST(&mElementCollections);
}
CommonAnimationManager::~CommonAnimationManager()
{
MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
}
void
CommonAnimationManager::Disconnect()
{
// Content nodes might outlive the transition or animation manager.
RemoveAllElementCollections();
mPresContext = nullptr;
}
void
CommonAnimationManager::AddElementCollection(AnimationCollection* aCollection)
{
if (!mIsObservingRefreshDriver) {
NS_ASSERTION(aCollection->mNeedsRefreshes,
"Added data which doesn't need refreshing?");
// We need to observe the refresh driver.
mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style);
mIsObservingRefreshDriver = true;
}
PR_INSERT_BEFORE(aCollection, &mElementCollections);
}
void
CommonAnimationManager::RemoveAllElementCollections()
{
while (!PR_CLIST_IS_EMPTY(&mElementCollections)) {
AnimationCollection* head =
static_cast<AnimationCollection*>(PR_LIST_HEAD(&mElementCollections));
head->Destroy();
}
}
void
CommonAnimationManager::MaybeStartObservingRefreshDriver()
{
if (mIsObservingRefreshDriver || !NeedsRefresh()) {
return;
}
mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style);
mIsObservingRefreshDriver = true;
}
void
CommonAnimationManager::MaybeStartOrStopObservingRefreshDriver()
{
bool needsRefresh = NeedsRefresh();
if (needsRefresh && !mIsObservingRefreshDriver) {
mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style);
} else if (!needsRefresh && mIsObservingRefreshDriver) {
mPresContext->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style);
}
mIsObservingRefreshDriver = needsRefresh;
}
bool
CommonAnimationManager::NeedsRefresh() const
{
for (PRCList *l = PR_LIST_HEAD(&mElementCollections);
l != &mElementCollections;
l = PR_NEXT_LINK(l)) {
if (static_cast<AnimationCollection*>(l)->mNeedsRefreshes) {
return true;
}
}
return false;
}
AnimationCollection*
CommonAnimationManager::GetAnimationCollection(const nsIFrame* aFrame)
{
nsIContent* content = aFrame->GetContent();
if (!content) {
return nullptr;
}
nsIAtom* animProp;
if (aFrame->IsGeneratedContentFrame()) {
nsIFrame* parent = aFrame->GetParent();
if (parent->IsGeneratedContentFrame()) {
return nullptr;
}
nsIAtom* name = content->NodeInfo()->NameAtom();
if (name == nsGkAtoms::mozgeneratedcontentbefore) {
animProp = GetAnimationsBeforeAtom();
} else if (name == nsGkAtoms::mozgeneratedcontentafter) {
animProp = GetAnimationsAfterAtom();
} else {
return nullptr;
}
content = content->GetParent();
if (!content) {
return nullptr;
}
} else {
if (!content->MayHaveAnimations()) {
return nullptr;
}
animProp = GetAnimationsAtom();
}
return static_cast<AnimationCollection*>(content->GetProperty(animProp));
}
AnimationCollection*
CommonAnimationManager::GetAnimationsForCompositor(const nsIFrame* aFrame,
nsCSSProperty aProperty)
{
AnimationCollection* collection = GetAnimationCollection(aFrame);
if (!collection ||
!collection->HasAnimationOfProperty(aProperty) ||
!collection->CanPerformOnCompositorThread(
AnimationCollection::CanAnimate_AllowPartial)) {
return nullptr;
}
// This animation can be done on the compositor.
return collection;
}
/*
* nsISupports implementation
*/
NS_IMPL_ISUPPORTS(CommonAnimationManager, nsIStyleRuleProcessor)
nsRestyleHint
CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
nsRestyleHint
CommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
bool
CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
{
return false;
}
nsRestyleHint
CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
/* virtual */ bool
CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
{
return false;
}
/* virtual */ void
CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
{
MOZ_ASSERT(aData->mPresContext == mPresContext,
"pres context mismatch");
nsIStyleRule *rule =
GetAnimationRule(aData->mElement,
nsCSSPseudoElements::ePseudo_NotPseudoElement);
if (rule) {
aData->mRuleWalker->Forward(rule);
}
}
/* virtual */ void
CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
{
MOZ_ASSERT(aData->mPresContext == mPresContext,
"pres context mismatch");
if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
return;
}
// FIXME: Do we really want to be the only thing keeping a
// pseudo-element alive? I *think* the non-animation restyle should
// handle that, but should add a test.
nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
if (rule) {
aData->mRuleWalker->Forward(rule);
}
}
/* virtual */ void
CommonAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
{
}
#ifdef MOZ_XUL
/* virtual */ void
CommonAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
{
}
#endif
/* virtual */ size_t
CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mElementCollections
//
// The following members are not measured
// - mPresContext, because it's non-owning
return 0;
}
/* virtual */ size_t
CommonAnimationManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
void
CommonAnimationManager::AddStyleUpdatesTo(RestyleTracker& aTracker)
{
TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
PRCList* next = PR_LIST_HEAD(&mElementCollections);
while (next != &mElementCollections) {
AnimationCollection* collection = static_cast<AnimationCollection*>(next);
next = PR_NEXT_LINK(next);
collection->EnsureStyleRuleFor(now, EnsureStyleRule_IsNotThrottled);
dom::Element* elementToRestyle = collection->GetElementToRestyle();
if (elementToRestyle) {
nsRestyleHint rshint = collection->IsForTransitions()
? eRestyle_CSSTransitions : eRestyle_CSSAnimations;
aTracker.AddPendingRestyle(elementToRestyle, rshint, nsChangeHint(0));
}
}
}
void
CommonAnimationManager::NotifyCollectionUpdated(AnimationCollection&
aCollection)
{
MaybeStartObservingRefreshDriver();
mPresContext->ClearLastStyleUpdateForAllAnimations();
mPresContext->RestyleManager()->IncrementAnimationGeneration();
aCollection.UpdateAnimationGeneration(mPresContext);
aCollection.PostRestyleForAnimation(mPresContext);
}
/* static */ bool
CommonAnimationManager::ExtractComputedValueForTransition(
nsCSSProperty aProperty,
nsStyleContext* aStyleContext,
StyleAnimationValue& aComputedValue)
{
bool result = StyleAnimationValue::ExtractComputedValue(aProperty,
aStyleContext,
aComputedValue);
if (aProperty == eCSSProperty_visibility) {
MOZ_ASSERT(aComputedValue.GetUnit() ==
StyleAnimationValue::eUnit_Enumerated,
"unexpected unit");
aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
StyleAnimationValue::eUnit_Visibility);
}
return result;
}
AnimationCollection*
CommonAnimationManager::GetAnimations(dom::Element *aElement,
nsCSSPseudoElements::Type aPseudoType,
bool aCreateIfNeeded)
{
if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
// Early return for the most common case.
return nullptr;
}
nsIAtom *propName;
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
propName = GetAnimationsAtom();
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
propName = GetAnimationsBeforeAtom();
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
propName = GetAnimationsAfterAtom();
} else {
NS_ASSERTION(!aCreateIfNeeded,
"should never try to create transitions for pseudo "
"other than :before or :after");
return nullptr;
}
AnimationCollection* collection =
static_cast<AnimationCollection*>(aElement->GetProperty(propName));
if (!collection && aCreateIfNeeded) {
// FIXME: Consider arena-allocating?
collection = new AnimationCollection(aElement, propName, this);
nsresult rv =
aElement->SetProperty(propName, collection,
&AnimationCollection::PropertyDtor, false);
if (NS_FAILED(rv)) {
NS_WARNING("SetProperty failed");
// The collection must be destroyed via PropertyDtor, otherwise
// mCalledPropertyDtor assertion is triggered in destructor.
AnimationCollection::PropertyDtor(aElement, propName, collection, nullptr);
return nullptr;
}
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
aElement->SetMayHaveAnimations();
}
AddElementCollection(collection);
}
return collection;
}
nsIStyleRule*
CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType)
{
MOZ_ASSERT(
aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
aPseudoType == nsCSSPseudoElements::ePseudo_before ||
aPseudoType == nsCSSPseudoElements::ePseudo_after,
"forbidden pseudo type");
if (!mPresContext->IsDynamic()) {
// For print or print preview, ignore animations.
return nullptr;
}
AnimationCollection* collection =
GetAnimations(aElement, aPseudoType, false);
if (!collection) {
return nullptr;
}
RestyleManager* restyleManager = mPresContext->RestyleManager();
if (restyleManager->SkipAnimationRules()) {
return nullptr;
}
collection->EnsureStyleRuleFor(
mPresContext->RefreshDriver()->MostRecentRefresh(),
EnsureStyleRule_IsNotThrottled);
return collection->mStyleRule;
}
/* static */ const CommonAnimationManager::LayerAnimationRecord
CommonAnimationManager::sLayerAnimationInfo[] =
{ { eCSSProperty_transform,
nsDisplayItem::TYPE_TRANSFORM,
nsChangeHint_UpdateTransformLayer },
{ eCSSProperty_opacity,
nsDisplayItem::TYPE_OPACITY,
nsChangeHint_UpdateOpacityLayer } };
/* static */ const CommonAnimationManager::LayerAnimationRecord*
CommonAnimationManager::LayerAnimationRecordFor(nsCSSProperty aProperty)
{
MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
"unexpected property");
const auto& info = sLayerAnimationInfo;
for (size_t i = 0; i < ArrayLength(info); ++i) {
if (aProperty == info[i].mProperty) {
return &info[i];
}
}
return nullptr;
}
#ifdef DEBUG
/* static */ void
CommonAnimationManager::Initialize()
{
const auto& info = css::CommonAnimationManager::sLayerAnimationInfo;
for (size_t i = 0; i < ArrayLength(info); i++) {
auto record = info[i];
MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
"CSS property with entry in sLayerAnimationInfo does not "
"have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag");
}
// Check that every property with the flag for animating on the
// compositor has an entry in sLayerAnimationInfo.
for (nsCSSProperty prop = nsCSSProperty(0);
prop < eCSSProperty_COUNT;
prop = nsCSSProperty(prop + 1)) {
if (nsCSSProps::PropHasFlags(prop,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)) {
bool found = false;
for (size_t i = 0; i < ArrayLength(info); i++) {
auto record = info[i];
if (record.mProperty == prop) {
found = true;
break;
}
}
MOZ_ASSERT(found,
"CSS property with the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR "
"flag does not have an entry in sLayerAnimationInfo");
}
}
}
#endif
NS_IMPL_ISUPPORTS(AnimValuesStyleRule, nsIStyleRule)
/* virtual */ void
AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
nsStyleContext *contextParent = aRuleData->mStyleContext->GetParent();
if (contextParent && contextParent->HasPseudoElementData()) {
// Don't apply transitions or animations to things inside of
// pseudo-elements.
// FIXME (Bug 522599): Add tests for this.
return;
}
for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
PropertyValuePair &cv = mPropertyValuePairs[i];
if (aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(
nsCSSProps::kSIDTable[cv.mProperty]))
{
nsCSSValue *prop = aRuleData->ValueFor(cv.mProperty);
if (prop->GetUnit() == eCSSUnit_Null) {
#ifdef DEBUG
bool ok =
#endif
StyleAnimationValue::UncomputeValue(cv.mProperty, cv.mValue, *prop);
MOZ_ASSERT(ok, "could not store computed value");
}
}
}
}
#ifdef DEBUG
/* virtual */ void
AnimValuesStyleRule::List(FILE* out, int32_t aIndent) const
{
nsAutoCString str;
for (int32_t index = aIndent; --index >= 0; ) {
str.AppendLiteral(" ");
}
str.AppendLiteral("[anim values] { ");
for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
const PropertyValuePair &pair = mPropertyValuePairs[i];
str.Append(nsCSSProps::GetStringValue(pair.mProperty));
str.AppendLiteral(": ");
nsAutoString value;
StyleAnimationValue::UncomputeValue(pair.mProperty, pair.mValue, value);
AppendUTF16toUTF8(value, str);
str.AppendLiteral("; ");
}
str.AppendLiteral("}\n");
fprintf_stderr(out, "%s", str.get());
}
#endif
} /* end sub-namespace css */
bool
AnimationCollection::CanAnimatePropertyOnCompositor(
const dom::Element *aElement,
nsCSSProperty aProperty,
CanAnimateFlags aFlags)
{
bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
if (!gfxPlatform::OffMainThreadCompositingEnabled()) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Performance warning: Compositor disabled");
LogAsyncAnimationFailure(message);
}
return false;
}
nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement);
if (IsGeometricProperty(aProperty)) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Performance warning: Async animation of geometric property '");
message.Append(nsCSSProps::GetStringValue(aProperty));
message.AppendLiteral("' is disabled");
LogAsyncAnimationFailure(message, aElement);
}
return false;
}
if (aProperty == eCSSProperty_transform) {
Bug 1176969 - Disable OMT animation for any frame in a preserve-3d scene rather than only frames whose parent and child are in a preserve-3d scene. r=mattwoodrow frame->Preserves3D() is whether the frame's parent has transform-style: preserve-3d, which means that the frame is part of the same 3-D scene as its parent. frame->Preserves3DChildren() is whether the frame itself has transform-style: preserve-3d, which means that the frame is part of the same 3-D scene as its children. Neither of these are valid cases for doing off-main-thread (OMT) animation because all of the layers in a preserve-3d scene are currently siblings of each other, rather than preserving ancestor/descendant relationships. This means that it's not valid to animate transform of the parent on the compositor because the compositor animation won't update any of its children that have layers. Likewise, it's not valid to animate transform of the child on the compositor because the code that sends transform information to the compositor doesn't handle the accumulation of transforms needed to get the "right" transform for the child (i.e., with the transforms of its ancestors up to the top of the 3-D scene merged in). This means that we do OMT animation for slightly fewer cases with the patch than we did without the patch. This means it's pretty low risk in terms of correctness, although there's a chance it might regress performance on one of the (somewhat limited) set of cases where the optimization was valid. (Bug 779598 covers doing OMT animation for preserve-3d cases, and depends on the work ongoing in bug 1097464.) The animate-preserve3d-parent.html reftest doesn't fail without the patch, since something seems to invalidate in the test; it was testing the testcase that showed correct behavior when the mouse was moving, so this isn't incredibly surprising (although that invalidation from mouse movement is itself worth debugging). The animate-preserve3d-child.html test does fail without the patch, though. (With an initial transform of none instead of the 30deg transform, both tests also show an invalidation bug without the patch.)
2015-06-30 21:29:55 +03:00
if (frame->Preserves3D() ||
frame->Preserves3DChildren()) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported. See bug 779598");
LogAsyncAnimationFailure(message, aElement);
}
return false;
}
if (frame->IsSVGTransformed()) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Gecko bug: Async 'transform' animations of frames with SVG transforms is not supported. See bug 779599");
LogAsyncAnimationFailure(message, aElement);
}
return false;
}
if (aFlags & CanAnimate_HasGeometricProperty) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Performance warning: Async animation of 'transform' not possible due to presence of geometric properties");
LogAsyncAnimationFailure(message, aElement);
}
return false;
}
}
2013-05-23 11:09:26 +04:00
bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled();
if (!enabled && shouldLog) {
nsCString message;
message.AppendLiteral("Performance warning: Async animations are disabled");
LogAsyncAnimationFailure(message);
}
bool propertyAllowed = (aProperty == eCSSProperty_transform) ||
(aProperty == eCSSProperty_opacity) ||
(aFlags & CanAnimate_AllowPartial);
return enabled && propertyAllowed;
}
/* static */ bool
AnimationCollection::IsCompositorAnimationDisabledForFrame(
nsIFrame* aFrame)
{
void* prop = aFrame->Properties().Get(nsIFrame::RefusedAsyncAnimation());
return bool(reinterpret_cast<intptr_t>(prop));
}
bool
AnimationCollection::CanPerformOnCompositorThread(
CanAnimateFlags aFlags) const
{
dom::Element* element = GetElementToRestyle();
if (!element) {
return false;
}
nsIFrame* frame = nsLayoutUtils::GetStyleFrame(element);
if (!frame) {
return false;
}
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
const Animation* anim = mAnimations[animIdx];
if (!anim->IsPlaying()) {
continue;
}
const KeyframeEffectReadOnly* effect = anim->GetEffect();
MOZ_ASSERT(effect, "A playing animation should have an effect");
for (size_t propIdx = 0, propEnd = effect->Properties().Length();
propIdx != propEnd; ++propIdx) {
if (IsGeometricProperty(effect->Properties()[propIdx].mProperty)) {
aFlags = CanAnimateFlags(aFlags | CanAnimate_HasGeometricProperty);
break;
}
}
}
bool existsProperty = false;
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
const Animation* anim = mAnimations[animIdx];
if (!anim->IsPlaying()) {
continue;
}
const KeyframeEffectReadOnly* effect = anim->GetEffect();
MOZ_ASSERT(effect, "A playing animation should have an effect");
existsProperty = existsProperty || effect->Properties().Length() > 0;
for (size_t propIdx = 0, propEnd = effect->Properties().Length();
propIdx != propEnd; ++propIdx) {
const AnimationProperty& prop = effect->Properties()[propIdx];
if (!CanAnimatePropertyOnCompositor(element,
prop.mProperty,
aFlags) ||
IsCompositorAnimationDisabledForFrame(frame)) {
return false;
}
}
}
// No properties to animate
if (!existsProperty) {
return false;
}
return true;
}
void
AnimationCollection::PostUpdateLayerAnimations()
{
nsCSSPropertySet propsHandled;
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
const auto& properties = mAnimations[animIdx]->GetEffect()->Properties();
for (size_t propIdx = properties.Length(); propIdx-- != 0; ) {
nsCSSProperty prop = properties[propIdx].mProperty;
if (nsCSSProps::PropHasFlags(prop,
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
!propsHandled.HasProperty(prop)) {
propsHandled.AddProperty(prop);
nsChangeHint changeHint = css::CommonAnimationManager::
LayerAnimationRecordFor(prop)->mChangeHint;
dom::Element* element = GetElementToRestyle();
if (element) {
mManager->mPresContext->RestyleManager()->
PostRestyleEvent(element, nsRestyleHint(0), changeHint);
}
}
}
}
}
bool
AnimationCollection::HasAnimationOfProperty(nsCSSProperty aProperty) const
{
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
const KeyframeEffectReadOnly* effect = mAnimations[animIdx]->GetEffect();
if (effect && effect->HasAnimationOfProperty(aProperty) &&
!effect->IsFinishedTransition()) {
return true;
}
}
return false;
}
mozilla::dom::Element*
AnimationCollection::GetElementToRestyle() const
{
if (IsForElement()) {
return mElement;
}
nsIFrame* primaryFrame = mElement->GetPrimaryFrame();
if (!primaryFrame) {
return nullptr;
}
nsIFrame* pseudoFrame;
if (IsForBeforePseudo()) {
pseudoFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame);
} else if (IsForAfterPseudo()) {
pseudoFrame = nsLayoutUtils::GetAfterFrame(primaryFrame);
} else {
MOZ_ASSERT(false, "unknown mElementProperty");
return nullptr;
}
if (!pseudoFrame) {
return nullptr;
}
return pseudoFrame->GetContent()->AsElement();
}
void
AnimationCollection::NotifyAnimationUpdated()
{
// On the next flush, force us to update the style rule
mNeedsRefreshes = true;
mStyleRuleRefreshTime = TimeStamp();
mManager->NotifyCollectionUpdated(*this);
}
/* static */ void
AnimationCollection::LogAsyncAnimationFailure(nsCString& aMessage,
const nsIContent* aContent)
{
if (aContent) {
aMessage.AppendLiteral(" [");
aMessage.Append(nsAtomCString(aContent->NodeInfo()->NameAtom()));
nsIAtom* id = aContent->GetID();
if (id) {
aMessage.AppendLiteral(" with id '");
aMessage.Append(nsAtomCString(aContent->GetID()));
aMessage.Append('\'');
}
aMessage.Append(']');
}
aMessage.Append('\n');
printf_stderr("%s", aMessage.get());
}
/*static*/ void
AnimationCollection::PropertyDtor(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData)
{
AnimationCollection* collection =
static_cast<AnimationCollection*>(aPropertyValue);
#ifdef DEBUG
MOZ_ASSERT(!collection->mCalledPropertyDtor, "can't call dtor twice");
collection->mCalledPropertyDtor = true;
#endif
{
nsAutoAnimationMutationBatch mb(collection->mElement);
for (size_t animIdx = collection->mAnimations.Length(); animIdx-- != 0; ) {
collection->mAnimations[animIdx]->CancelFromStyle();
}
}
delete collection;
}
void
AnimationCollection::Tick()
{
for (size_t animIdx = 0, animEnd = mAnimations.Length();
animIdx != animEnd; animIdx++) {
mAnimations[animIdx]->Tick();
}
}
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
void
AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime,
EnsureStyleRuleFlags aFlags)
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
{
if (!mNeedsRefreshes) {
mStyleRuleRefreshTime = aRefreshTime;
return;
}
if (!mStyleRuleRefreshTime.IsNull() &&
mStyleRuleRefreshTime == aRefreshTime) {
// mStyleRule may be null and valid, if we have no style to apply.
return;
}
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
// If we're performing animations on the compositor thread, then we can skip
// most of the work in this method. But even if we are throttled, then we
// have to do the work if an animation is ending in order to get correct end
// of animation behavior (the styles of the animation disappear, or the fill
// mode behavior). CanThrottle returns false for any finishing animations
// so we can force style recalculation in that case.
if (aFlags == EnsureStyleRule_IsThrottled) {
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
if (!mAnimations[animIdx]->CanThrottle()) {
aFlags = EnsureStyleRule_IsNotThrottled;
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
break;
}
}
}
if (aFlags == EnsureStyleRule_IsThrottled) {
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
return;
}
if (mManager->IsAnimationManager()) {
// Update cascade results before updating the style rule, since the
// cascade results can influence the style rule.
static_cast<nsAnimationManager*>(mManager)->MaybeUpdateCascadeResults(this);
}
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
mStyleRuleRefreshTime = aRefreshTime;
mStyleRule = nullptr;
// We'll set mNeedsRefreshes to true below in all cases where we need them.
mNeedsRefreshes = false;
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
// If multiple animations specify behavior for the same property the
// animation which occurs last in the value of animation-name wins.
// As a result, we iterate from last animation to first and, if a
// property has already been set, we don't leave it.
nsCSSPropertySet properties;
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
mAnimations[animIdx]->ComposeStyle(mStyleRule, properties, mNeedsRefreshes);
}
mManager->MaybeStartObservingRefreshDriver();
}
Bug 1025709 part 4 - Move EnsureStyleRuleFor from ElementTransitions and ElementAnimations to CommonElementAnimationData; r=heycam Both ElementAnimations and ElementTransitions have an EnsureStyleRuleFor method. The ElementAnimations version is a more general of the ElementTransitions one with the exception that the ElementTransitions version checks for finished transitions. This patch moves the code from ElementAnimations to CommonElementAnimationData with one minor change: adding the checks for finished transitions. The ElementTransitions version is removed. Since the ElementAnimations version contains a second parameter, aIsThrottled, callers of ElementTransitions must include this extra parameter. In a subsequent patch we add an enum for this parameter to make call sites easier to read. The ElementAnimations version also sets the mNeedsRefreshes member so at the same time we move mNeedsRefreshes to CommonElementAnimationData. Furthermore, since the ElementAnimations version which we have adopted returns early if mNeedsRefreshes is false, this patch ensures that when we call EnsureStyleRuleFor from ElementTransitions::WalkTransitionRule, we set mNeedsRefreshes to true first. Another difference to account for is that the ElementTransitions version of EnsureStyleRuleFor *always* sets mStyleRule (even if it doesn't add anything to it) where as the ElementAnimations version only creates the rule when necessary so we need to add a check to ElementTransitions::WalkTransitionRule that mStyleRule is actually set before using it.
2014-06-20 07:39:24 +04:00
bool
AnimationCollection::CanThrottleTransformChanges(TimeStamp aTime)
{
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
return false;
}
// If we know that the animation cannot cause overflow,
// we can just disable flushes for this animation.
// If we don't show scrollbars, we don't care about overflow.
if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0) {
return true;
}
// If this animation can cause overflow, we can throttle some of the ticks.
if (!mStyleRuleRefreshTime.IsNull() &&
(aTime - mStyleRuleRefreshTime) < TimeDuration::FromMilliseconds(200)) {
return true;
}
dom::Element* element = GetElementToRestyle();
if (!element) {
return false;
}
// If the nearest scrollable ancestor has overflow:hidden,
// we don't care about overflow.
nsIScrollableFrame* scrollable = nsLayoutUtils::GetNearestScrollableFrame(
nsLayoutUtils::GetStyleFrame(element));
if (!scrollable) {
return true;
}
ScrollbarStyles ss = scrollable->GetScrollbarStyles();
if (ss.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
ss.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
scrollable->GetLogicalScrollPosition() == nsPoint(0, 0)) {
return true;
}
return false;
}
bool
AnimationCollection::CanThrottleAnimation(TimeStamp aTime)
{
dom::Element* element = GetElementToRestyle();
if (!element) {
return false;
}
nsIFrame* frame = nsLayoutUtils::GetStyleFrame(element);
if (!frame) {
return false;
}
const auto& info = css::CommonAnimationManager::sLayerAnimationInfo;
for (size_t i = 0; i < ArrayLength(info); i++) {
auto record = info[i];
if (!HasAnimationOfProperty(record.mProperty)) {
continue;
}
Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
frame, record.mLayerType);
if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
return false;
}
if (record.mProperty == eCSSProperty_transform &&
!CanThrottleTransformChanges(aTime)) {
return false;
}
}
return true;
}
void
AnimationCollection::UpdateAnimationGeneration(nsPresContext* aPresContext)
{
mAnimationGeneration =
aPresContext->RestyleManager()->GetAnimationGeneration();
}
void
AnimationCollection::UpdateCheckGeneration(
nsPresContext* aPresContext)
{
mCheckGeneration =
aPresContext->RestyleManager()->GetAnimationGeneration();
}
bool
AnimationCollection::HasCurrentAnimations() const
{
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
if (mAnimations[animIdx]->HasCurrentEffect()) {
return true;
}
}
return false;
}
bool
AnimationCollection::HasCurrentAnimationsForProperties(
const nsCSSProperty* aProperties,
size_t aPropertyCount) const
{
for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
const Animation& anim = *mAnimations[animIdx];
const KeyframeEffectReadOnly* effect = anim.GetEffect();
if (effect &&
effect->IsCurrent(anim) &&
effect->HasAnimationOfProperties(aProperties, aPropertyCount)) {
return true;
}
}
return false;
}
}