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: /* Code to start and animate CSS transitions. */ michael@0: michael@0: #ifndef nsTransitionManager_h_ michael@0: #define nsTransitionManager_h_ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "AnimationCommon.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: michael@0: class nsStyleContext; michael@0: class nsPresContext; michael@0: class nsCSSPropertySet; michael@0: struct nsTransition; michael@0: struct ElementDependentRuleProcessorData; michael@0: michael@0: /***************************************************************************** michael@0: * Per-Element data * michael@0: *****************************************************************************/ michael@0: michael@0: struct ElementPropertyTransition : public mozilla::StyleAnimation michael@0: { michael@0: // This is the start value to be used for a check for whether a michael@0: // transition is being reversed. Normally the same as michael@0: // mProperties[0].mSegments[0].mFromValue, except when this transition michael@0: // started as the reversal of another in-progress transition. michael@0: // Needed so we can handle two reverses in a row. michael@0: nsStyleAnimation::Value mStartForReversingTest; michael@0: // Likewise, the portion (in value space) of the "full" reversed michael@0: // transition that we're actually covering. For example, if a :hover michael@0: // effect has a transition that moves the element 10px to the right michael@0: // (by changing 'left' from 0px to 10px), and the mouse moves in to michael@0: // the element (starting the transition) but then moves out after the michael@0: // transition has advanced 4px, the second transition (from 10px/4px michael@0: // to 0px) will have mReversePortion of 0.4. (If the mouse then moves michael@0: // in again when the transition is back to 2px, the mReversePortion michael@0: // for the third transition (from 0px/2px to 10px) will be 0.8. michael@0: double mReversePortion; michael@0: michael@0: // Compute the portion of the *value* space that we should be through michael@0: // at the given time. (The input to the transition timing function michael@0: // has time units, the output has value units.) michael@0: double ValuePortionFor(mozilla::TimeStamp aRefreshTime) const; michael@0: michael@0: bool IsRemovedSentinel() const michael@0: { michael@0: // Note that mozilla::StyleAnimation::IsRunningAt depends on removed michael@0: // sentinels being represented by a null mStartTime. michael@0: return mStartTime.IsNull(); michael@0: } michael@0: michael@0: void SetRemovedSentinel() michael@0: { michael@0: // assign the null time stamp michael@0: mStartTime = mozilla::TimeStamp(); michael@0: } michael@0: }; michael@0: michael@0: struct ElementTransitions MOZ_FINAL michael@0: : public mozilla::css::CommonElementAnimationData michael@0: { michael@0: ElementTransitions(mozilla::dom::Element *aElement, nsIAtom *aElementProperty, michael@0: nsTransitionManager *aTransitionManager, michael@0: mozilla::TimeStamp aNow); michael@0: michael@0: void EnsureStyleRuleFor(mozilla::TimeStamp aRefreshTime); michael@0: michael@0: virtual bool HasAnimationOfProperty(nsCSSProperty aProperty) const MOZ_OVERRIDE; michael@0: michael@0: // If aFlags contains CanAnimate_AllowPartial, returns whether the michael@0: // state of this element's transitions at the current refresh driver michael@0: // time contains transition 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 transitions 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 transition 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: // Either zero or one for each CSS property: michael@0: nsTArray mPropertyTransitions; michael@0: }; michael@0: michael@0: michael@0: michael@0: class nsTransitionManager MOZ_FINAL michael@0: : public mozilla::css::CommonAnimationManager michael@0: { michael@0: public: michael@0: nsTransitionManager(nsPresContext *aPresContext) michael@0: : mozilla::css::CommonAnimationManager(aPresContext) michael@0: { michael@0: } michael@0: michael@0: static ElementTransitions* GetTransitions(nsIContent* aContent) { michael@0: return static_cast michael@0: (aContent->GetProperty(nsGkAtoms::transitionsProperty)); michael@0: } michael@0: michael@0: // Returns true if aContent or any of its ancestors has a transition. michael@0: static bool ContentOrAncestorHasTransition(nsIContent* aContent) { michael@0: do { michael@0: if (GetTransitions(aContent)) { michael@0: return true; michael@0: } michael@0: } while ((aContent = aContent->GetParent())); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: typedef mozilla::css::CommonElementAnimationData CommonElementAnimationData; michael@0: michael@0: static ElementTransitions* michael@0: GetTransitionsForCompositor(nsIContent* aContent, michael@0: nsCSSProperty aProperty) michael@0: { michael@0: if (!aContent->MayHaveAnimations()) { michael@0: return nullptr; michael@0: } michael@0: ElementTransitions* transitions = GetTransitions(aContent); michael@0: if (!transitions || michael@0: !transitions->HasAnimationOfProperty(aProperty) || michael@0: !transitions->CanPerformOnCompositorThread( michael@0: CommonElementAnimationData::CanAnimate_AllowPartial)) { michael@0: return nullptr; michael@0: } michael@0: return transitions; michael@0: } michael@0: michael@0: /** michael@0: * StyleContextChanged michael@0: * michael@0: * To be called from nsFrameManager::ReResolveStyleContext when the michael@0: * style of an element has changed, to initiate transitions from michael@0: * that style change. For style contexts with :before and :after michael@0: * pseudos, aElement is expected to be the generated before/after michael@0: * element. michael@0: * michael@0: * It may return a "cover rule" (see CoverTransitionStartStyleRule) to michael@0: * cover up some of the changes for the duration of the restyling of michael@0: * descendants. If it does, this function will take care of causing michael@0: * the necessary restyle afterwards, but the caller must restyle the michael@0: * element *again* with the original sequence of rules plus the michael@0: * returned cover rule as the most specific rule. michael@0: */ michael@0: already_AddRefed michael@0: StyleContextChanged(mozilla::dom::Element *aElement, michael@0: nsStyleContext *aOldStyleContext, michael@0: nsStyleContext *aNewStyleContext); 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) const michael@0: MOZ_MUST_OVERRIDE MOZ_OVERRIDE; michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: 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 FlushTransitions(FlushFlags aFlags); michael@0: michael@0: // Performs a 'mini-flush' to make styles from throttled transitions michael@0: // up-to-date prior to processing an unrelated style change, so that michael@0: // any transitions triggered by that style change produce correct michael@0: // results. michael@0: // michael@0: // In more detail: when we're able to run animations on the michael@0: // compositor, we sometimes "throttle" these animations by skipping michael@0: // updating style data on the main thread. However, whenever we michael@0: // process a normal (non-animation) style change, any changes in michael@0: // computed style on elements that have transition-* properties set michael@0: // may need to trigger new transitions; this process requires knowing michael@0: // both the old and new values of the property. To do this correctly, michael@0: // we need to have an up-to-date *old* value of the property on the michael@0: // primary frame. So the purpose of the mini-flush is to update the michael@0: // style for all throttled transitions and animations to the current michael@0: // animation state without making any other updates, so that when we michael@0: // process the queued style updates we'll have correct old data to michael@0: // compare against. When we do this, we don't bother touching frames michael@0: // other than primary frames. michael@0: void UpdateAllThrottledStyles(); michael@0: michael@0: ElementTransitions* GetElementTransitions(mozilla::dom::Element *aElement, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: bool aCreateIfNeeded); michael@0: michael@0: protected: michael@0: virtual void ElementDataRemoved() MOZ_OVERRIDE; michael@0: virtual void AddElementData(mozilla::css::CommonElementAnimationData* aData) MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: void ConsiderStartingTransition(nsCSSProperty aProperty, michael@0: const nsTransition& aTransition, michael@0: mozilla::dom::Element *aElement, michael@0: ElementTransitions *&aElementTransitions, michael@0: nsStyleContext *aOldStyleContext, michael@0: nsStyleContext *aNewStyleContext, michael@0: bool *aStartedAny, michael@0: nsCSSPropertySet *aWhichStarted); michael@0: void WalkTransitionRule(ElementDependentRuleProcessorData* aData, michael@0: nsCSSPseudoElements::Type aPseudoType); michael@0: // Update the animated styles of an element and its descendants. michael@0: // If the element has a transition, it is flushed back to its primary frame. michael@0: // If the element does not have a transition, 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: michael@0: #endif /* !defined(nsTransitionManager_h_) */