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: michael@0: #ifndef mozilla_css_AnimationCommon_h michael@0: #define mozilla_css_AnimationCommon_h michael@0: michael@0: #include "nsIStyleRuleProcessor.h" michael@0: #include "nsIStyleRule.h" michael@0: #include "nsRefreshDriver.h" michael@0: #include "prclist.h" michael@0: #include "nsStyleAnimation.h" michael@0: #include "nsCSSProperty.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsSMILKeySpline.h" michael@0: #include "nsStyleStruct.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: michael@0: class nsPresContext; michael@0: class nsIFrame; michael@0: michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: bool IsGeometricProperty(nsCSSProperty aProperty); michael@0: michael@0: struct CommonElementAnimationData; michael@0: michael@0: class CommonAnimationManager : public nsIStyleRuleProcessor, michael@0: public nsARefreshObserver { michael@0: public: michael@0: CommonAnimationManager(nsPresContext *aPresContext); michael@0: virtual ~CommonAnimationManager(); michael@0: michael@0: // nsISupports michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsIStyleRuleProcessor (parts) michael@0: virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual nsRestyleHint michael@0: HasAttributeDependentStyle(AttributeRuleProcessorData* aData) MOZ_OVERRIDE; michael@0: virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) MOZ_OVERRIDE; 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: /** michael@0: * Notify the manager that the pres context is going away. michael@0: */ michael@0: void Disconnect(); michael@0: michael@0: enum FlushFlags { michael@0: Can_Throttle, michael@0: Cannot_Throttle michael@0: }; michael@0: michael@0: static bool ExtractComputedValueForTransition( michael@0: nsCSSProperty aProperty, michael@0: nsStyleContext* aStyleContext, michael@0: nsStyleAnimation::Value& aComputedValue); michael@0: protected: michael@0: friend struct CommonElementAnimationData; // for ElementDataRemoved michael@0: michael@0: virtual void AddElementData(CommonElementAnimationData* aData) = 0; michael@0: virtual void ElementDataRemoved() = 0; michael@0: void RemoveAllElementData(); michael@0: michael@0: // Update the style on aElement from the transition stored in this manager and michael@0: // the new parent style - aParentStyle. aElement must be transitioning or michael@0: // animated. Returns the updated style. michael@0: nsStyleContext* UpdateThrottledStyle(mozilla::dom::Element* aElement, michael@0: nsStyleContext* aParentStyle, michael@0: nsStyleChangeList &aChangeList); michael@0: // Reparent the style of aContent and any :before and :after pseudo-elements. michael@0: already_AddRefed ReparentContent(nsIContent* aContent, michael@0: nsStyleContext* aParentStyle); michael@0: // reparent :before and :after pseudo elements of aElement michael@0: static void ReparentBeforeAndAfter(dom::Element* aElement, michael@0: nsIFrame* aPrimaryFrame, michael@0: nsStyleContext* aNewStyle, michael@0: nsStyleSet* aStyleSet); michael@0: michael@0: PRCList mElementData; michael@0: nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect) michael@0: }; michael@0: michael@0: // The internals of UpdateAllThrottledStyles, used by nsAnimationManager and michael@0: // nsTransitionManager, see the comments in the declaration of the latter. michael@0: #define IMPL_UPDATE_ALL_THROTTLED_STYLES_INTERNAL(class_, animations_getter_) \ michael@0: void \ michael@0: class_::UpdateAllThrottledStylesInternal() \ michael@0: { \ michael@0: TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); \ michael@0: \ michael@0: nsStyleChangeList changeList; \ michael@0: \ michael@0: /* update each transitioning element by finding its root-most ancestor michael@0: with a transition, and flushing the style on that ancestor and all michael@0: its descendants*/ \ michael@0: PRCList *next = PR_LIST_HEAD(&mElementData); \ michael@0: while (next != &mElementData) { \ michael@0: CommonElementAnimationData* ea = \ michael@0: static_cast(next); \ michael@0: next = PR_NEXT_LINK(next); \ michael@0: \ michael@0: if (ea->mFlushGeneration == now) { \ michael@0: /* this element has been ticked already */ \ michael@0: continue; \ michael@0: } \ michael@0: \ michael@0: /* element is initialised to the starting element (i.e., one we know has michael@0: an animation) and ends up with the root-most animated ancestor, michael@0: that is, the element where we begin updates. */ \ michael@0: dom::Element* element = ea->mElement; \ michael@0: /* make a list of ancestors */ \ michael@0: nsTArray ancestors; \ michael@0: do { \ michael@0: ancestors.AppendElement(element); \ michael@0: } while ((element = element->GetParentElement())); \ michael@0: \ michael@0: /* walk down the ancestors until we find one with a throttled transition */\ michael@0: for (int32_t i = ancestors.Length() - 1; i >= 0; --i) { \ michael@0: if (animations_getter_(ancestors[i], \ michael@0: nsCSSPseudoElements::ePseudo_NotPseudoElement, \ michael@0: false)) { \ michael@0: element = ancestors[i]; \ michael@0: break; \ michael@0: } \ michael@0: } \ michael@0: \ michael@0: nsIFrame* primaryFrame; \ michael@0: if (element && \ michael@0: (primaryFrame = nsLayoutUtils::GetStyleFrame(element))) { \ michael@0: UpdateThrottledStylesForSubtree(element, \ michael@0: primaryFrame->StyleContext()->GetParent(), changeList); \ michael@0: } \ michael@0: } \ michael@0: \ michael@0: RestyleManager* restyleManager = mPresContext->RestyleManager(); \ michael@0: restyleManager->ProcessRestyledFrames(changeList); \ michael@0: restyleManager->FlushOverflowChangedTracker(); \ michael@0: } michael@0: michael@0: /** michael@0: * A style rule that maps property-nsStyleAnimation::Value pairs. michael@0: */ michael@0: class AnimValuesStyleRule MOZ_FINAL : public nsIStyleRule michael@0: { michael@0: public: michael@0: // nsISupports implementation michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsIStyleRule implementation michael@0: virtual void MapRuleInfoInto(nsRuleData* aRuleData) MOZ_OVERRIDE; michael@0: #ifdef DEBUG michael@0: virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: void AddValue(nsCSSProperty aProperty, nsStyleAnimation::Value &aStartValue) michael@0: { michael@0: PropertyValuePair v = { aProperty, aStartValue }; michael@0: mPropertyValuePairs.AppendElement(v); michael@0: } michael@0: michael@0: // Caller must fill in returned value. michael@0: nsStyleAnimation::Value* AddEmptyValue(nsCSSProperty aProperty) michael@0: { michael@0: PropertyValuePair *p = mPropertyValuePairs.AppendElement(); michael@0: p->mProperty = aProperty; michael@0: return &p->mValue; michael@0: } michael@0: michael@0: struct PropertyValuePair { michael@0: nsCSSProperty mProperty; michael@0: nsStyleAnimation::Value mValue; michael@0: }; michael@0: michael@0: private: michael@0: InfallibleTArray mPropertyValuePairs; michael@0: }; michael@0: michael@0: class ComputedTimingFunction { michael@0: public: michael@0: typedef nsTimingFunction::Type Type; michael@0: void Init(const nsTimingFunction &aFunction); michael@0: double GetValue(double aPortion) const; michael@0: const nsSMILKeySpline* GetFunction() const { michael@0: NS_ASSERTION(mType == nsTimingFunction::Function, "Type mismatch"); michael@0: return &mTimingFunction; michael@0: } michael@0: Type GetType() const { return mType; } michael@0: uint32_t GetSteps() const { return mSteps; } michael@0: private: michael@0: Type mType; michael@0: nsSMILKeySpline mTimingFunction; michael@0: uint32_t mSteps; michael@0: }; michael@0: michael@0: } /* end css sub-namespace */ michael@0: michael@0: struct AnimationPropertySegment michael@0: { michael@0: float mFromKey, mToKey; michael@0: nsStyleAnimation::Value mFromValue, mToValue; michael@0: mozilla::css::ComputedTimingFunction mTimingFunction; michael@0: }; michael@0: michael@0: struct AnimationProperty michael@0: { michael@0: nsCSSProperty mProperty; michael@0: InfallibleTArray mSegments; michael@0: }; michael@0: michael@0: /** michael@0: * Data about one animation (i.e., one of the values of michael@0: * 'animation-name') running on an element. michael@0: */ michael@0: struct StyleAnimation michael@0: { michael@0: StyleAnimation() michael@0: : mIsRunningOnCompositor(false) michael@0: , mLastNotification(LAST_NOTIFICATION_NONE) michael@0: { michael@0: } michael@0: michael@0: nsString mName; // empty string for 'none' michael@0: float mIterationCount; // NS_IEEEPositiveInfinity() means infinite michael@0: uint8_t mDirection; michael@0: uint8_t mFillMode; michael@0: uint8_t mPlayState; michael@0: michael@0: bool FillsForwards() const { michael@0: return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH || michael@0: mFillMode == NS_STYLE_ANIMATION_FILL_MODE_FORWARDS; michael@0: } michael@0: bool FillsBackwards() const { michael@0: return mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BOTH || michael@0: mFillMode == NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS; michael@0: } michael@0: michael@0: bool IsPaused() const { michael@0: return mPlayState == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED; michael@0: } michael@0: michael@0: bool HasAnimationOfProperty(nsCSSProperty aProperty) const; michael@0: bool IsRunningAt(mozilla::TimeStamp aTime) const; michael@0: michael@0: // Return the duration, at aTime (or, if paused, mPauseStart), since michael@0: // the *end* of the delay period. May be negative. michael@0: mozilla::TimeDuration ElapsedDurationAt(mozilla::TimeStamp aTime) const { michael@0: NS_ABORT_IF_FALSE(!IsPaused() || aTime >= mPauseStart, michael@0: "if paused, aTime must be at least mPauseStart"); michael@0: return (IsPaused() ? mPauseStart : aTime) - mStartTime - mDelay; michael@0: } michael@0: michael@0: // The beginning of the delay period. This is also used by michael@0: // ElementPropertyTransition in its IsRemovedSentinel and michael@0: // SetRemovedSentinel methods. michael@0: mozilla::TimeStamp mStartTime; michael@0: mozilla::TimeStamp mPauseStart; michael@0: mozilla::TimeDuration mDelay; michael@0: mozilla::TimeDuration mIterationDuration; michael@0: bool mIsRunningOnCompositor; michael@0: michael@0: enum { michael@0: LAST_NOTIFICATION_NONE = uint32_t(-1), michael@0: LAST_NOTIFICATION_END = uint32_t(-2) michael@0: }; michael@0: // One of the above constants, or an integer for the iteration michael@0: // whose start we last notified on. michael@0: uint32_t mLastNotification; michael@0: michael@0: InfallibleTArray mProperties; michael@0: }; michael@0: michael@0: namespace css { michael@0: michael@0: struct CommonElementAnimationData : public PRCList michael@0: { michael@0: CommonElementAnimationData(dom::Element *aElement, nsIAtom *aElementProperty, michael@0: CommonAnimationManager *aManager, TimeStamp aNow) michael@0: : mElement(aElement) michael@0: , mElementProperty(aElementProperty) michael@0: , mManager(aManager) michael@0: , mAnimationGeneration(0) michael@0: , mFlushGeneration(aNow) michael@0: #ifdef DEBUG michael@0: , mCalledPropertyDtor(false) michael@0: #endif michael@0: { michael@0: MOZ_COUNT_CTOR(CommonElementAnimationData); michael@0: PR_INIT_CLIST(this); michael@0: } michael@0: ~CommonElementAnimationData() michael@0: { michael@0: NS_ABORT_IF_FALSE(mCalledPropertyDtor, michael@0: "must call destructor through element property dtor"); michael@0: MOZ_COUNT_DTOR(CommonElementAnimationData); michael@0: PR_REMOVE_LINK(this); michael@0: mManager->ElementDataRemoved(); michael@0: } michael@0: michael@0: void Destroy() michael@0: { michael@0: // This will call our destructor. michael@0: mElement->DeleteProperty(mElementProperty); michael@0: } michael@0: michael@0: bool CanThrottleTransformChanges(mozilla::TimeStamp aTime); michael@0: michael@0: bool CanThrottleAnimation(mozilla::TimeStamp aTime); michael@0: michael@0: enum CanAnimateFlags { michael@0: // Testing for width, height, top, right, bottom, or left. michael@0: CanAnimate_HasGeometricProperty = 1, michael@0: // Allow the case where OMTA is allowed in general, but not for the michael@0: // specified property. michael@0: CanAnimate_AllowPartial = 2 michael@0: }; michael@0: michael@0: static bool michael@0: CanAnimatePropertyOnCompositor(const dom::Element *aElement, michael@0: nsCSSProperty aProperty, michael@0: CanAnimateFlags aFlags); michael@0: michael@0: static bool IsCompositorAnimationDisabledForFrame(nsIFrame* aFrame); michael@0: michael@0: // True if this animation can be performed on the compositor thread. michael@0: // Do not pass CanAnimate_AllowPartial to make sure that all properties of this michael@0: // animation are supported by the compositor. michael@0: virtual bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const = 0; michael@0: virtual bool HasAnimationOfProperty(nsCSSProperty aProperty) const = 0; michael@0: michael@0: static void LogAsyncAnimationFailure(nsCString& aMessage, michael@0: const nsIContent* aContent = nullptr); michael@0: michael@0: dom::Element *mElement; michael@0: michael@0: // the atom we use in mElement's prop table (must be a static atom, michael@0: // i.e., in an atom list) michael@0: nsIAtom *mElementProperty; michael@0: michael@0: CommonAnimationManager *mManager; michael@0: michael@0: // This style rule contains the style data for currently animating michael@0: // values. It only matches when styling with animation. When we michael@0: // style without animation, we need to not use it so that we can michael@0: // detect any new changes; if necessary we restyle immediately michael@0: // afterwards with animation. michael@0: // NOTE: If we don't need to apply any styles, mStyleRule will be michael@0: // null, but mStyleRuleRefreshTime will still be valid. michael@0: nsRefPtr mStyleRule; michael@0: michael@0: // RestyleManager keeps track of the number of animation michael@0: // 'mini-flushes' (see nsTransitionManager::UpdateAllThrottledStyles()). michael@0: // mAnimationGeneration is the sequence number of the last flush where a michael@0: // transition/animation changed. We keep a similar count on the michael@0: // corresponding layer so we can check that the layer is up to date with michael@0: // the animation manager. michael@0: uint64_t mAnimationGeneration; michael@0: // Update mAnimationGeneration to nsCSSFrameConstructor's count michael@0: void UpdateAnimationGeneration(nsPresContext* aPresContext); michael@0: michael@0: // The refresh time associated with mStyleRule. michael@0: TimeStamp mStyleRuleRefreshTime; michael@0: michael@0: // Generation counter for flushes of throttled animations. michael@0: // Used to prevent updating the styles twice for a given element during michael@0: // UpdateAllThrottledStyles. michael@0: TimeStamp mFlushGeneration; michael@0: michael@0: #ifdef DEBUG michael@0: bool mCalledPropertyDtor; michael@0: #endif michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* !defined(mozilla_css_AnimationCommon_h) */