/* -*- 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/AnimationPerformanceWarning.h" #include "mozilla/AnimationTarget.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/HashTable.h" #include "mozilla/Maybe.h" #include "mozilla/OwningNonNull.h" #include "mozilla/PseudoElementHashEntry.h" #include "mozilla/RefPtr.h" #include "mozilla/ServoTypes.h" #include "nsCSSPropertyID.h" #include "nsCycleCollectionParticipant.h" #include "nsTHashMap.h" #include "nsTArray.h" class nsCSSPropertyIDSet; class nsAtom; class nsIFrame; class nsPresContext; enum class DisplayItemType : uint8_t; namespace mozilla { class ComputedStyle; class EffectSet; class RestyleTracker; struct StyleAnimationValue; struct StyleAnimationValueMap; struct AnimationProperty; struct NonOwningAnimationTarget; namespace dom { class Animation; class Element; class KeyframeEffect; } // namespace dom class EffectCompositor { public: explicit EffectCompositor(nsPresContext* aPresContext) : mPresContext(aPresContext) {} 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 : uint32_t { // The animations sheet (CSS animations, script-generated animations, // and CSS transitions that are no longer tied to CSS markup) Animations = 0, // The transitions sheet (CSS transitions that are tied to CSS markup) Transitions = 1 }; // 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(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, PseudoStyleType 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, PseudoStyleType 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(); // Clear all pending restyle requests for the given (pseudo-) element (and its // ::before, ::after and ::marker elements if the given element is not // pseudo). void ClearRestyleRequestsFor(dom::Element* aElement); // Called when computed style 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(const ComputedStyle* aStyle, dom::Element* aElement, PseudoStyleType aPseudoType); // Get the animation rule for the appropriate level of the cascade for // a (pseudo-)element. Called from the Servo side. // // The animation rule is stored in |StyleAnimationValueMap|. // We need to be careful while doing any modification because it may cause // some thread-safe issues. bool GetServoAnimationRule(const dom::Element* aElement, PseudoStyleType aPseudoType, CascadeLevel aCascadeLevel, StyleAnimationValueMap* aAnimationValues); // A variant on GetServoAnimationRule that composes all the effects for an // element up to and including |aEffect|. // // Note that |aEffect| might not be in the EffectSet since we can use this for // committing the computed style of a removed Animation. bool ComposeServoAnimationRuleForEffect( dom::KeyframeEffect& aEffect, CascadeLevel aCascadeLevel, StyleAnimationValueMap* aAnimationValues); bool HasPendingStyleUpdates() const; static bool HasAnimationsForCompositor(const nsIFrame* aFrame, DisplayItemType aType); static nsTArray> GetAnimationsForCompositor( const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet); static void ClearIsRunningOnCompositor(const nsIFrame* aFrame, DisplayItemType aType); // 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. // // 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, PseudoStyleType aPseudoType); // Update the mPropertiesWithImportantRules and // mPropertiesForAnimationsLevel members of the given EffectSet, and also // request any restyles required by changes to the cascade result. // // NOTE: 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. // // This is typically reserved for internal callers but is public here since // when we detect changes to the cascade on the Servo side we can't call // MarkCascadeNeedsUpdate during the traversal so instead we call this as part // of a follow-up sequential task. static void UpdateCascadeResults(EffectSet& aEffectSet, dom::Element* aElement, PseudoStyleType aPseudoType); // 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. // // For display:table content, which maintains a distinction between primary // frame (table wrapper frame) and style frame (inner table frame), animations // are stored on the content associated with the _style_ frame even though // some (particularly transform-like animations) may be applied to the // _primary_ frame. As a result, callers will typically want to pass the style // frame to this function. // // 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 GetAnimationElementAndPseudoForFrame( const nsIFrame* aFrame); // Associates a performance warning with effects on |aFrame| that animate // properties in |aPropertySet|. static void SetPerformanceWarning( const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet, const AnimationPerformanceWarning& aWarning); // Do a bunch of stuff that we should avoid doing during the parallel // traversal (e.g. changing member variables) for all elements that we expect // to restyle on the next traversal. // // Returns true if there are elements needing a restyle for animation. bool PreTraverse(ServoTraversalFlags aFlags); // Similar to the above but for all elements in the subtree rooted // at aElement. bool PreTraverseInSubtree(ServoTraversalFlags aFlags, dom::Element* aRoot); // Record a (pseudo-)element that may have animations that can be removed. void NoteElementForReducing(const NonOwningAnimationTarget& aTarget); bool NeedsReducing() const { return !mElementsToReduce.empty(); } void ReduceAnimations(); // Returns true if any type of compositor animations on |aFrame| allow // runnning on the compositor. // Sets the reason in |aWarning| if the result is false. static bool AllowCompositorAnimationsOnFrame( const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aWarning /* out */); private: ~EffectCompositor() = default; // 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. static nsCSSPropertyIDSet GetOverriddenProperties( EffectSet& aEffectSet, dom::Element* aElement, PseudoStyleType aPseudoType); 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> mElementsToRestyle; bool mIsInPreTraverse = false; HashSet mElementsToReduce; }; } // namespace mozilla #endif // mozilla_EffectCompositor_h