diff options
Diffstat (limited to 'dom/animation/EffectCompositor.h')
-rw-r--r-- | dom/animation/EffectCompositor.h | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/dom/animation/EffectCompositor.h b/dom/animation/EffectCompositor.h new file mode 100644 index 000000000..732fbb333 --- /dev/null +++ b/dom/animation/EffectCompositor.h @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_EffectCompositor_h +#define mozilla_EffectCompositor_h + +#include "mozilla/EnumeratedArray.h" +#include "mozilla/Maybe.h" +#include "mozilla/OwningNonNull.h" +#include "mozilla/PseudoElementHashEntry.h" +#include "mozilla/RefPtr.h" +#include "nsCSSPropertyID.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDataHashtable.h" +#include "nsIStyleRuleProcessor.h" +#include "nsTArray.h" + +class nsCSSPropertyIDSet; +class nsIFrame; +class nsIStyleRule; +class nsPresContext; +class nsStyleContext; + +namespace mozilla { + +class EffectSet; +class RestyleTracker; +struct AnimationPerformanceWarning; +struct NonOwningAnimationTarget; + +namespace dom { +class Animation; +class Element; +} + +class EffectCompositor +{ +public: + explicit EffectCompositor(nsPresContext* aPresContext) + : mPresContext(aPresContext) + { + for (size_t i = 0; i < kCascadeLevelCount; i++) { + CascadeLevel cascadeLevel = CascadeLevel(i); + mRuleProcessors[cascadeLevel] = + new AnimationStyleRuleProcessor(this, cascadeLevel); + } + } + + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EffectCompositor) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EffectCompositor) + + void Disconnect() { + mPresContext = nullptr; + } + + // Animations can be applied at two different levels in the CSS cascade: + enum class CascadeLevel { + // The animations sheet (CSS animations, script-generated animations, + // and CSS transitions that are no longer tied to CSS markup) + Animations, + // The transitions sheet (CSS transitions that are tied to CSS markup) + Transitions + }; + // We don't define this as part of CascadeLevel as then we'd have to add + // explicit checks for the Count enum value everywhere CascadeLevel is used. + static const size_t kCascadeLevelCount = + static_cast<size_t>(CascadeLevel::Transitions) + 1; + + // NOTE: This can return null after Disconnect(). + nsPresContext* PresContext() const { return mPresContext; } + + enum class RestyleType { + // Animation style has changed but the compositor is applying the same + // change so we might be able to defer updating the main thread until it + // becomes necessary. + Throttled, + // Animation style has changed and needs to be updated on the main thread. + Standard, + // Animation style has changed and needs to be updated on the main thread + // as well as forcing animations on layers to be updated. + // This is needed in cases such as when an animation becomes paused or has + // its playback rate changed. In such cases, although the computed style + // and refresh driver time might not change, we still need to ensure the + // corresponding animations on layers are updated to reflect the new + // configuration of the animation. + Layer + }; + + // Notifies the compositor that the animation rule for the specified + // (pseudo-)element at the specified cascade level needs to be updated. + // The specified steps taken to update the animation rule depend on + // |aRestyleType| whose values are described above. + void RequestRestyle(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + RestyleType aRestyleType, + CascadeLevel aCascadeLevel); + + // Schedule an animation restyle. This is called automatically by + // RequestRestyle when necessary. However, it is exposed here since we also + // need to perform this step when triggering transitions *without* also + // invalidating the animation style rule (which RequestRestyle would do). + void PostRestyleForAnimation(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + CascadeLevel aCascadeLevel); + + // Posts an animation restyle for any elements whose animation style rule + // is out of date but for which an animation restyle has not yet been + // posted because updates on the main thread are throttled. + void PostRestyleForThrottledAnimations(); + + // Called when the style context on the specified (pseudo-) element might + // have changed so that any context-sensitive values stored within + // animation effects (e.g. em-based endpoints used in keyframe effects) + // can be re-resolved to computed values. + void UpdateEffectProperties(nsStyleContext* aStyleContext, + dom::Element* aElement, + CSSPseudoElementType aPseudoType); + + // Updates the animation rule stored on the EffectSet for the + // specified (pseudo-)element for cascade level |aLevel|. + // If the animation rule is not marked as needing an update, + // no work is done. + // |aStyleContext| is used for UpdateCascadingResults. + // |aStyleContext| can be nullptr if style context, which is associated with + // the primary frame of the specified (pseudo-)element, is the current style + // context. + // If we are resolving a new style context, we shoud pass the newly created + // style context, otherwise we may use an old style context, it will result + // unexpected cascading results. + void MaybeUpdateAnimationRule(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + CascadeLevel aCascadeLevel, + nsStyleContext *aStyleContext); + + // We need to pass the newly resolved style context as |aStyleContext| when + // we call this function during resolving style context because this function + // calls UpdateCascadingResults with a style context if necessary, at the + // time, we end up using the previous style context if we don't pass the new + // style context. + // When we are not resolving style context, |aStyleContext| can be nullptr, we + // will use a style context associated with the primary frame of the specified + // (pseudo-)element. + nsIStyleRule* GetAnimationRule(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + CascadeLevel aCascadeLevel, + nsStyleContext* aStyleContext); + + bool HasPendingStyleUpdates() const; + bool HasThrottledStyleUpdates() const; + + // Tell the restyle tracker about all the animated styles that have + // pending updates so that it can update the animation rule for these + // elements. + void AddStyleUpdatesTo(RestyleTracker& aTracker); + + nsIStyleRuleProcessor* RuleProcessor(CascadeLevel aCascadeLevel) const + { + return mRuleProcessors[aCascadeLevel]; + } + + static bool HasAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSPropertyID aProperty); + + static nsTArray<RefPtr<dom::Animation>> + GetAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSPropertyID aProperty); + + static void ClearIsRunningOnCompositor(const nsIFrame* aFrame, + nsCSSPropertyID aProperty); + + // Update animation cascade results for the specified (pseudo-)element + // but only if we have marked the cascade as needing an update due a + // the change in the set of effects or a change in one of the effects' + // "in effect" state. + // |aStyleContext| may be nullptr in which case we will use the + // nsStyleContext of the primary frame of the specified (pseudo-)element. + // + // This method does NOT detect if other styles that apply above the + // animation level of the cascade have changed. + static void + MaybeUpdateCascadeResults(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + nsStyleContext* aStyleContext); + + // Update the mPropertiesWithImportantRules and + // mPropertiesForAnimationsLevel members of the corresponding EffectSet. + // + // This can be expensive so we should only call it if styles that apply + // above the animation level of the cascade might have changed. For all + // other cases we should call MaybeUpdateCascadeResults. + static void + UpdateCascadeResults(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + nsStyleContext* aStyleContext); + + // Helper to fetch the corresponding element and pseudo-type from a frame. + // + // For frames corresponding to pseudo-elements, the returned element is the + // element on which we store the animations (i.e. the EffectSet and/or + // AnimationCollection), *not* the generated content. + // + // Returns an empty result when a suitable element cannot be found including + // when the frame represents a pseudo-element on which we do not support + // animations. + static Maybe<NonOwningAnimationTarget> + GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame); + + // Associates a performance warning with effects on |aFrame| that animates + // |aProperty|. + static void SetPerformanceWarning( + const nsIFrame* aFrame, + nsCSSPropertyID aProperty, + const AnimationPerformanceWarning& aWarning); + +private: + ~EffectCompositor() = default; + + // Rebuilds the animation rule corresponding to |aCascadeLevel| on the + // EffectSet associated with the specified (pseudo-)element. + static void ComposeAnimationRule(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + CascadeLevel aCascadeLevel, + TimeStamp aRefreshTime); + + static dom::Element* GetElementToRestyle(dom::Element* aElement, + CSSPseudoElementType + aPseudoType); + + // Get the properties in |aEffectSet| that we are able to animate on the + // compositor but which are also specified at a higher level in the cascade + // than the animations level in |aStyleContext|. + static void + GetOverriddenProperties(nsStyleContext* aStyleContext, + EffectSet& aEffectSet, + nsCSSPropertyIDSet& aPropertiesOverridden); + + static void + UpdateCascadeResults(EffectSet& aEffectSet, + dom::Element* aElement, + CSSPseudoElementType aPseudoType, + nsStyleContext* aStyleContext); + + static nsPresContext* GetPresContext(dom::Element* aElement); + + nsPresContext* mPresContext; + + // Elements with a pending animation restyle. The associated bool value is + // true if a pending animation restyle has also been dispatched. For + // animations that can be throttled, we will add an entry to the hashtable to + // indicate that the style rule on the element is out of date but without + // posting a restyle to update it. + EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount), + nsDataHashtable<PseudoElementHashEntry, bool>> + mElementsToRestyle; + + class AnimationStyleRuleProcessor final : public nsIStyleRuleProcessor + { + public: + AnimationStyleRuleProcessor(EffectCompositor* aCompositor, + CascadeLevel aCascadeLevel) + : mCompositor(aCompositor) + , mCascadeLevel(aCascadeLevel) + { + MOZ_ASSERT(aCompositor); + } + + NS_DECL_ISUPPORTS + + // nsIStyleRuleProcessor (parts) + nsRestyleHint HasStateDependentStyle( + StateRuleProcessorData* aData) override; + nsRestyleHint HasStateDependentStyle( + PseudoElementStateRuleProcessorData* aData) override; + bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; + nsRestyleHint HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) override; + bool MediumFeaturesChanged(nsPresContext* aPresContext) override; + void RulesMatching(ElementRuleProcessorData* aData) override; + void RulesMatching(PseudoElementRuleProcessorData* aData) override; + void RulesMatching(AnonBoxRuleProcessorData* aData) override; +#ifdef MOZ_XUL + void RulesMatching(XULTreeRuleProcessorData* aData) override; +#endif + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) + const MOZ_MUST_OVERRIDE override; + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) + const MOZ_MUST_OVERRIDE override; + + private: + ~AnimationStyleRuleProcessor() = default; + + EffectCompositor* mCompositor; + CascadeLevel mCascadeLevel; + }; + + EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount), + OwningNonNull<AnimationStyleRuleProcessor>> + mRuleProcessors; +}; + +} // namespace mozilla + +#endif // mozilla_EffectCompositor_h |