michael@0: /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #ifndef nsAnimationManager_h_ michael@0: #define nsAnimationManager_h_ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "AnimationCommon.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: michael@0: class nsCSSKeyframesRule; michael@0: class nsStyleContext; michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: class Declaration; michael@0: } michael@0: } michael@0: michael@0: struct AnimationEventInfo { michael@0: nsRefPtr mElement; michael@0: mozilla::InternalAnimationEvent mEvent; michael@0: michael@0: AnimationEventInfo(mozilla::dom::Element *aElement, michael@0: const nsString& aAnimationName, michael@0: uint32_t aMessage, mozilla::TimeDuration aElapsedTime, michael@0: const nsAString& aPseudoElement) michael@0: : mElement(aElement), mEvent(true, aMessage) michael@0: { michael@0: // XXX Looks like nobody initialize WidgetEvent::time michael@0: mEvent.animationName = aAnimationName; michael@0: mEvent.elapsedTime = aElapsedTime.ToSeconds(); michael@0: mEvent.pseudoElement = aPseudoElement; michael@0: } michael@0: michael@0: // InternalAnimationEvent doesn't support copy-construction, so we need michael@0: // to ourselves in order to work with nsTArray michael@0: AnimationEventInfo(const AnimationEventInfo &aOther) michael@0: : mElement(aOther.mElement), mEvent(true, aOther.mEvent.message) michael@0: { michael@0: mEvent.AssignAnimationEventData(aOther.mEvent, false); michael@0: } michael@0: }; michael@0: michael@0: typedef InfallibleTArray EventArray; michael@0: michael@0: /** michael@0: * Data about all of the animations running on an element. michael@0: */ michael@0: struct ElementAnimations MOZ_FINAL michael@0: : public mozilla::css::CommonElementAnimationData michael@0: { michael@0: typedef mozilla::TimeStamp TimeStamp; michael@0: typedef mozilla::TimeDuration TimeDuration; michael@0: michael@0: ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty, michael@0: nsAnimationManager *aAnimationManager, TimeStamp aNow); michael@0: michael@0: // This function takes as input the start time, duration, and direction of an michael@0: // animation and returns the position in the current iteration. Note that michael@0: // this only works when we know that the animation is currently running. michael@0: // This way of calling the function can be used from the compositor. Note michael@0: // that if the animation has not started yet, has already ended, or is paused, michael@0: // it should not be run from the compositor. When this function is called michael@0: // from the main thread, we need the actual StyleAnimation* in order to michael@0: // get correct animation-fill behavior and to fire animation events. michael@0: // This function returns -1 for the position if the animation should not be michael@0: // run (because it is not currently active and has no fill behavior), but michael@0: // only does so if aAnimation is non-null; with a null aAnimation it is an michael@0: // error to give aElapsedDuration < 0, and fill-forwards is assumed. michael@0: // After calling GetPositionInIteration with non-null aAnimation and aEa, be michael@0: // sure to call CheckNeedsRefresh on the animation manager afterwards. michael@0: static double GetPositionInIteration(TimeDuration aElapsedDuration, michael@0: TimeDuration aIterationDuration, michael@0: double aIterationCount, michael@0: uint32_t aDirection, michael@0: mozilla::StyleAnimation* aAnimation = michael@0: nullptr, michael@0: ElementAnimations* aEa = nullptr, michael@0: EventArray* aEventsToDispatch = nullptr); michael@0: michael@0: void EnsureStyleRuleFor(TimeStamp aRefreshTime, michael@0: EventArray &aEventsToDispatch, michael@0: bool aIsThrottled); michael@0: michael@0: bool IsForElement() const { // rather than for a pseudo-element michael@0: return mElementProperty == nsGkAtoms::animationsProperty; michael@0: } michael@0: michael@0: nsString PseudoElement() michael@0: { michael@0: return mElementProperty == nsGkAtoms::animationsProperty ? michael@0: EmptyString() : michael@0: mElementProperty == nsGkAtoms::animationsOfBeforeProperty ? michael@0: NS_LITERAL_STRING("::before") : michael@0: NS_LITERAL_STRING("::after"); michael@0: } michael@0: michael@0: void PostRestyleForAnimation(nsPresContext *aPresContext) { michael@0: nsRestyleHint styleHint = IsForElement() ? eRestyle_Self : eRestyle_Subtree; michael@0: aPresContext->PresShell()->RestyleForAnimation(mElement, styleHint); michael@0: } michael@0: michael@0: // If aFlags contains CanAnimate_AllowPartial, returns whether the michael@0: // state of this element's animations at the current refresh driver michael@0: // time contains animation data that can be done on the compositor michael@0: // thread. (This is useful for determining whether a layer should be michael@0: // active, or whether to send data to the layer.) michael@0: // If aFlags does not contain CanAnimate_AllowPartial, returns whether michael@0: // the state of this element's animations at the current refresh driver michael@0: // time can be fully represented by data sent to the compositor. michael@0: // (This is useful for determining whether throttle the animation michael@0: // (suppress main-thread style updates).) michael@0: // Note that when CanPerformOnCompositorThread returns true, it also, michael@0: // as a side-effect, notifies the ActiveLayerTracker. FIXME: This michael@0: // should probably move to the relevant callers. michael@0: virtual bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const MOZ_OVERRIDE; michael@0: michael@0: virtual bool HasAnimationOfProperty(nsCSSProperty aProperty) const MOZ_OVERRIDE; michael@0: michael@0: // False when we know that our current style rule is valid michael@0: // indefinitely into the future (because all of our animations are michael@0: // either completed or paused). May be invalidated by a style change. michael@0: bool mNeedsRefreshes; michael@0: michael@0: InfallibleTArray mAnimations; michael@0: }; michael@0: michael@0: class nsAnimationManager MOZ_FINAL michael@0: : public mozilla::css::CommonAnimationManager michael@0: { michael@0: public: michael@0: nsAnimationManager(nsPresContext *aPresContext) michael@0: : mozilla::css::CommonAnimationManager(aPresContext) michael@0: , mObservingRefreshDriver(false) michael@0: { michael@0: } michael@0: michael@0: static ElementAnimations* GetAnimationsForCompositor(nsIContent* aContent, michael@0: nsCSSProperty aProperty) michael@0: { michael@0: if (!aContent->MayHaveAnimations()) michael@0: return nullptr; michael@0: ElementAnimations* animations = static_cast( michael@0: aContent->GetProperty(nsGkAtoms::animationsProperty)); michael@0: if (!animations) michael@0: return nullptr; michael@0: bool propertyMatches = animations->HasAnimationOfProperty(aProperty); michael@0: return (propertyMatches && michael@0: animations->CanPerformOnCompositorThread( michael@0: mozilla::css::CommonElementAnimationData::CanAnimate_AllowPartial)) michael@0: ? animations michael@0: : nullptr; michael@0: } michael@0: michael@0: // Returns true if aContent or any of its ancestors has an animation. michael@0: static bool ContentOrAncestorHasAnimation(nsIContent* aContent) { michael@0: do { michael@0: if (aContent->GetProperty(nsGkAtoms::animationsProperty)) { michael@0: return true; michael@0: } michael@0: } while ((aContent = aContent->GetParent())); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void EnsureStyleRuleFor(ElementAnimations* aET); michael@0: michael@0: // nsIStyleRuleProcessor (parts) michael@0: virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: #ifdef MOZ_XUL michael@0: virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: #endif michael@0: virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: const MOZ_MUST_OVERRIDE MOZ_OVERRIDE; michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: const MOZ_MUST_OVERRIDE MOZ_OVERRIDE; michael@0: michael@0: // nsARefreshObserver michael@0: virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE; michael@0: michael@0: void FlushAnimations(FlushFlags aFlags); michael@0: michael@0: /** michael@0: * Return the style rule that RulesMatching should add for michael@0: * aStyleContext. This might be different from what RulesMatching michael@0: * actually added during aStyleContext's construction because the michael@0: * element's animation-name may have changed. (However, this does michael@0: * return null during the non-animation restyling phase, as michael@0: * RulesMatching does.) michael@0: * michael@0: * aStyleContext may be a style context for aElement or for its michael@0: * :before or :after pseudo-element. michael@0: */ michael@0: nsIStyleRule* CheckAnimationRule(nsStyleContext* aStyleContext, michael@0: mozilla::dom::Element* aElement); michael@0: michael@0: /** michael@0: * Dispatch any pending events. We accumulate animationend and michael@0: * animationiteration events only during refresh driver notifications michael@0: * (and dispatch them at the end of such notifications), but we michael@0: * accumulate animationstart events at other points when style michael@0: * contexts are created. michael@0: */ michael@0: void DispatchEvents() { michael@0: // Fast-path the common case: no events michael@0: if (!mPendingEvents.IsEmpty()) { michael@0: DoDispatchEvents(); michael@0: } michael@0: } michael@0: michael@0: ElementAnimations* GetElementAnimations(mozilla::dom::Element *aElement, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: bool aCreateIfNeeded); michael@0: michael@0: // Updates styles on throttled animations. See note on nsTransitionManager michael@0: void UpdateAllThrottledStyles(); michael@0: michael@0: protected: michael@0: virtual void ElementDataRemoved() MOZ_OVERRIDE michael@0: { michael@0: CheckNeedsRefresh(); michael@0: } michael@0: virtual void AddElementData(mozilla::css::CommonElementAnimationData* aData) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Check to see if we should stop or start observing the refresh driver michael@0: */ michael@0: void CheckNeedsRefresh(); michael@0: michael@0: private: michael@0: void BuildAnimations(nsStyleContext* aStyleContext, michael@0: InfallibleTArray& aAnimations); michael@0: bool BuildSegment(InfallibleTArray& michael@0: aSegments, michael@0: nsCSSProperty aProperty, const nsAnimation& aAnimation, michael@0: float aFromKey, nsStyleContext* aFromContext, michael@0: mozilla::css::Declaration* aFromDeclaration, michael@0: float aToKey, nsStyleContext* aToContext); michael@0: nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement, michael@0: nsCSSPseudoElements::Type aPseudoType); michael@0: michael@0: // Update the animated styles of an element and its descendants. michael@0: // If the element has an animation, it is flushed back to its primary frame. michael@0: // If the element does not have an animation, then its style is reparented. michael@0: void UpdateThrottledStylesForSubtree(nsIContent* aContent, michael@0: nsStyleContext* aParentStyle, michael@0: nsStyleChangeList &aChangeList); michael@0: void UpdateAllThrottledStylesInternal(); michael@0: michael@0: // The guts of DispatchEvents michael@0: void DoDispatchEvents(); michael@0: michael@0: EventArray mPendingEvents; michael@0: michael@0: bool mObservingRefreshDriver; michael@0: }; michael@0: michael@0: #endif /* !defined(nsAnimationManager_h_) */