gecko-dev/layout/style/AnimationCommon.h

421 строка
13 KiB
C
Исходник Обычный вид История

/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
2012-05-21 15:12:37 +04:00
/* 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_css_AnimationCommon_h
#define mozilla_css_AnimationCommon_h
#include <algorithm> // For <std::stable_sort>
#include "nsIStyleRuleProcessor.h"
#include "nsChangeHint.h"
#include "nsCSSProperty.h"
#include "nsDisplayList.h" // For nsDisplayItem::Type
#include "mozilla/AnimationComparator.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/Attributes.h"
#include "mozilla/Assertions.h"
#include "mozilla/FloatingPoint.h"
#include "nsContentUtils.h"
#include "nsCSSPseudoElements.h"
#include "nsCycleCollectionParticipant.h"
class nsIFrame;
class nsPresContext;
namespace mozilla {
struct AnimationCollection;
class CommonAnimationManager : public nsIStyleRuleProcessor {
public:
explicit CommonAnimationManager(nsPresContext *aPresContext);
// nsIStyleRuleProcessor (parts)
virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) override;
virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override;
virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
virtual nsRestyleHint
HasAttributeDependentStyle(AttributeRuleProcessorData* aData,
RestyleHintData& aRestyleHintDataResult) override;
virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
virtual void RulesMatching(ElementRuleProcessorData* aData) override;
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override;
virtual void RulesMatching(AnonBoxRuleProcessorData* aData) override;
#ifdef MOZ_XUL
virtual void RulesMatching(XULTreeRuleProcessorData* aData) override;
#endif
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
// NOTE: This can return null after Disconnect().
nsPresContext* PresContext() const { return mPresContext; }
/**
* Notify the manager that the pres context is going away.
*/
void Disconnect();
// Returns true if aContent or any of its ancestors has an animation
// or transition.
static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
do {
if (aContent->GetProperty(nsGkAtoms::animationsProperty) ||
aContent->GetProperty(nsGkAtoms::transitionsProperty)) {
return true;
}
} while ((aContent = aContent->GetParent()));
return false;
}
static bool ExtractComputedValueForTransition(
nsCSSProperty aProperty,
nsStyleContext* aStyleContext,
StyleAnimationValue& aComputedValue);
virtual bool IsAnimationManager() {
return false;
}
protected:
virtual ~CommonAnimationManager();
void AddElementCollection(AnimationCollection* aCollection);
void RemoveAllElementCollections();
bool NeedsRefresh() const;
virtual nsIAtom* GetAnimationsAtom() = 0;
virtual nsIAtom* GetAnimationsBeforeAtom() = 0;
virtual nsIAtom* GetAnimationsAfterAtom() = 0;
public:
// Get (and optionally create) the collection of animations managed
// by this class for the given |aElement| and |aPseudoType|.
AnimationCollection*
GetAnimationCollection(dom::Element *aElement,
nsCSSPseudoElements::Type aPseudoType,
bool aCreateIfNeeded);
// Given the frame |aFrame| with possibly animated content, finds its
// associated collection of animations. If it is a generated content
// frame, it may examine the parent frame to search for such animations.
AnimationCollection*
GetAnimationCollection(const nsIFrame* aFrame);
protected:
LinkedList<AnimationCollection> mElementCollections;
nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
};
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
typedef InfallibleTArray<RefPtr<dom::Animation>> AnimationPtrArray;
struct AnimationCollection : public LinkedListElement<AnimationCollection>
{
AnimationCollection(dom::Element *aElement, nsIAtom *aElementProperty,
CommonAnimationManager *aManager)
: mElement(aElement)
, mElementProperty(aElementProperty)
, mManager(aManager)
, mCheckGeneration(0)
#ifdef DEBUG
, mCalledPropertyDtor(false)
#endif
{
MOZ_COUNT_CTOR(AnimationCollection);
}
~AnimationCollection()
{
MOZ_ASSERT(mCalledPropertyDtor,
"must call destructor through element property dtor");
MOZ_COUNT_DTOR(AnimationCollection);
remove();
}
void Destroy()
{
// This will call our destructor.
mElement->DeleteProperty(mElementProperty);
}
static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData);
void Tick();
public:
// True if this animation can be performed on the compositor thread.
//
// Returns whether the state of this element's animations at the current
// refresh driver time contains animation data that can be done on the
// compositor thread. (This is used for determining whether a layer
// should be active, or whether to send data to the layer.)
//
// Note that this does not test whether the element's layer uses
// off-main-thread compositing, although it does check whether
// off-main-thread compositing is enabled as a whole.
bool CanPerformOnCompositorThread(const nsIFrame* aFrame) const;
Bug 1181392 part 5 - Remove use of IsFinishedTransition from AnimationCollection::HasAnimationOfProperty; r=dbaron AnimationCollection::HasAnimationOfProperty uses IsFinishedTransition to filter out transitions that should otherwise be ignored. This is used in the following places: 1. nsLayoutUtils::HasAnimations The is only used by nsIFrame::BuildDisplayListForStackingContext to see if there are any opacity animations For this case, simply returning *current* animations would be sufficient (since finished but filling animations should have already filled in the display opacity) 2. CommonAnimationManager::GetAnimationsForCompositor This should really only return *current* animations--that is, animations that are running or scheduled to run. Finished animations never run on the compositor. Indeed, only *playing* animations run on the compositor but, as we will see in some of the cases below, it is sometimes useful to know that an animation *will* run on the compositor in the near future (e.g. so we can pre-render content). The places where GetAnimationsForCompositor is used are: - When building layers to add animations to layers in nsDisplayList--in this case we skip any animations that aren't playing so if GetAnimationsForCompositor only returned current animations that would be more than sufficient. - In nsLayoutUtils::HasAnimationsForCompositor. This in turn is used: - In ChooseScaleAndSetTransform to see if the transform is being animated on the compositor. If so, it calls nsLayoutUtils::ComputeSuitableScaleForAnimation (which also calls GetAnimationsForCompositor) and passes the result to GetMinAndMaxScaleForAnimationProperty which we have already adjusted in part 4 of this patch series to only deal with *relevant* animations Relevant animations include both current animations and in effect animations but we don't run forwards-filling animations on the compositor so GetAnimationsForCompositor should NOT return them. Current animations should be enough. In fact, playing animations should be enough but we might want to pre-render layers at a suitable size during their delay phase so returning current animations is probably ok. - In nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay to add a fuzz factor to the overflow rect for frames undergoing a transform animation on the compositor. In this case too current animations should be sufficient. - In nsDisplayOpacity::NeedsActiveLayer to say "yes" if we are animating opacity on the compositor. Presumably in this case it would be good to say "yes" if the animation is in the delay phase too (as it currently does). After the animation is finished, we should drop the layer, i.e. current animations should be sufficient. - In nsDisplayTransform::ShouldPrerenderTransformedContent. As with nsDisplayOpacity::NeedsActiveLayer, we only need to pre-render transformed content for animations that are current. - In nsDisplayTransform::GetLayerState. As with nsDisplayOpacity::NeedsActiveLayer, we only need to return active here for current animations. - In nsIFrame::IsTransformed. Here we test the display style to see if there is a transform and also check if transform is being animated on the compositor. As a result, we really only need HasAnimationsForCompositor to return true for animations that are playing--otherwise the display style will tell us if we're transformed or not. Returning true for all current compositor animations (which is a superset of playing), however, should not cause problems (we already return true for even more than that). - In nsIFrame::HasOpacityInternal which is much the same as nsIFrame::IsTransformed and hence current should be fine. 3. AnimationCollection::CanThrottleAnimation Here, HasAnimationOfProperty is used when looking for animations that would disqualify us from throttling the animation by having an out-of-date layer generation or being a transform animation that affects scroll and so requires that we do the occasional main thread sample to update scrollbars. It would seem like current animations are enough here too. One interesting case is where we *had* a compositor animation but it has finished or been cancelled. In that case, the animation won't be current and we should not throttle the animation since we need to take it off its layer. It turns out checking for current animations is still ok in this case too. The reasoning is as follows: - If the animation is newly-finished, we'll pick that up in Animation::CanThrottle and return false then. - If the animation is newly-idle then there are two cases: If the cancelled animation was the only compositor animation then AnimationCollection::CanPerformOnCompositorThread will notice that there are no playing compositor animations and return false and AnimationCollection::CanThrottleAnimation will never be called. If there are other compositor animations running, then AnimationCollection::CanThrottleAnimation will still return false because whatever cancelled the animation will update the animation generation and we'll notice the mismatch between the layer animation generation and the animation generation on the collection. Based on the above analysis it appears that making AnimationCollection::HasAnimationOfProperty return only current animations (and simulatneously renaming it to HasCurrentAnimationOfProperty) is safe. Indeed, in effect, we already do this for transitions but not for animations. This patch generalizes this behavior to all animations. This patch also updates test_animations_omta.html since it was incorrectly testing that a finished opacity animation was still running on the compositor. Finished animations should not run on the compositor and the changes in this patch cause that to happen. The reason we don't just update this test to check for RunningOn.MainThread is that for opacity animations, unlike transform animations, we can't detect if an opacity on a layer was set by animation or not. As a result, for opacity animations we typically test the opacity on either the main thread or compositor in order to allow for the case where an animation-set opacity is still lingering on the compositor.
2015-08-07 06:29:36 +03:00
bool HasCurrentAnimationOfProperty(nsCSSProperty aProperty) const;
bool IsForElement() const { // rather than for a pseudo-element
return mElementProperty == nsGkAtoms::animationsProperty ||
mElementProperty == nsGkAtoms::transitionsProperty;
}
bool IsForBeforePseudo() const {
return mElementProperty == nsGkAtoms::animationsOfBeforeProperty ||
mElementProperty == nsGkAtoms::transitionsOfBeforeProperty;
}
bool IsForAfterPseudo() const {
return mElementProperty == nsGkAtoms::animationsOfAfterProperty ||
mElementProperty == nsGkAtoms::transitionsOfAfterProperty;
}
bool IsForTransitions() const {
return mElementProperty == nsGkAtoms::transitionsProperty ||
mElementProperty == nsGkAtoms::transitionsOfBeforeProperty ||
mElementProperty == nsGkAtoms::transitionsOfAfterProperty;
}
bool IsForAnimations() const {
return mElementProperty == nsGkAtoms::animationsProperty ||
mElementProperty == nsGkAtoms::animationsOfBeforeProperty ||
mElementProperty == nsGkAtoms::animationsOfAfterProperty;
}
nsCSSPseudoElements::Type PseudoElementType() const
{
if (IsForElement()) {
return nsCSSPseudoElements::ePseudo_NotPseudoElement;
}
if (IsForBeforePseudo()) {
return nsCSSPseudoElements::ePseudo_before;
}
MOZ_ASSERT(IsForAfterPseudo(),
"::before & ::after should be the only pseudo-elements here");
return nsCSSPseudoElements::ePseudo_after;
}
static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
dom::Element* GetElementToRestyle() const;
dom::Element *mElement;
// the atom we use in mElement's prop table (must be a static atom,
// i.e., in an atom list)
nsIAtom *mElementProperty;
CommonAnimationManager *mManager;
AnimationPtrArray mAnimations;
// For CSS transitions only, we record the most recent generation
// for which we've done the transition update, so that we avoid doing
// it more than once per style change.
// (Note that we also store an animation generation on each EffectSet in
// order to track when we need to update animations on layers.)
uint64_t mCheckGeneration;
// Update mCheckGeneration to RestyleManager's count
void UpdateCheckGeneration(nsPresContext* aPresContext);
Bug 1188251 part 3 - Add AnimationCollection::RequestRestyle; r=dholbert Ultimately we want to move throttling logic to AnimationCollection and Animation::Tick (and later to KeyframeEffect::SetParentTime). This is so that we can support script-generated animations without having to introduce yet another manager. To that end this patch introduces a method on AnimationCollection that can be called from Animation::Tick to perform the necessary notifications needed to update style. Later in this patch series we will extend RequestRestyle to incorporate more of the throttling logic and further extend it to cover some of the other notifications such as updating layers. This patch tracks whether or not we have already posted a restyle for animation to avoid making redundant calls. Calls to nsIDocument::SetNeedStyleFlush are cheap and more difficult to detect when they have completed so we don't filter redundant calls in the Restyle::Throttled case. If mHasPendingAnimationRestyle is set and AnimationCommon::EnsureStyleRuleFor is *never* called then we could arrive at situation where we fail to make post further restyles for animation. I have verified that if we fail to reset mHasPendingAnimationRestyle at the appropriate point (e.g. resetting it at the end of EnsureStyleRuleFor *after* the early-returns) then a number of existing tests fail. Furthermore, I have observed that it is reset by the beginning of each tick in almost every case except for a few instances of browser mochitests such as browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js. In this case, during the async cleanup of the test, we have an opacity transition on a vbox element that becomes display:none and appears to be skipped during restyling. However, even in this case, EnsureStyleRuleFor is called within one or at most two ticks and mHasPendingAnimationRestyle flag is cleared (i.e. it does not get stuck).
2015-08-17 07:59:44 +03:00
private:
#ifdef DEBUG
bool mCalledPropertyDtor;
#endif
};
/**
* Utility class for referencing the element that created a CSS animation or
* transition. It is non-owning (i.e. it uses a raw pointer) since it is only
* expected to be set by the owned animation while it actually being managed
* by the owning element.
*
* This class also abstracts the comparison of an element/pseudo-class pair
* for the sake of composite ordering since this logic is common to both CSS
* animations and transitions.
*
* (We call this OwningElementRef instead of just OwningElement so that we can
* call the getter on CSSAnimation/CSSTransition OwningElement() without
* clashing with this object's contructor.)
*/
class OwningElementRef final
{
public:
OwningElementRef()
: mElement(nullptr)
, mPseudoType(nsCSSPseudoElements::ePseudo_NotPseudoElement)
{ }
OwningElementRef(dom::Element& aElement,
nsCSSPseudoElements::Type aPseudoType)
: mElement(&aElement)
, mPseudoType(aPseudoType)
{ }
bool Equals(const OwningElementRef& aOther) const
{
return mElement == aOther.mElement &&
mPseudoType == aOther.mPseudoType;
}
bool LessThan(const OwningElementRef& aOther) const
{
MOZ_ASSERT(mElement && aOther.mElement,
"Elements to compare should not be null");
if (mElement != aOther.mElement) {
return nsContentUtils::PositionIsBefore(mElement, aOther.mElement);
}
return mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
(mPseudoType == nsCSSPseudoElements::ePseudo_before &&
aOther.mPseudoType == nsCSSPseudoElements::ePseudo_after);
}
bool IsSet() const { return !!mElement; }
void GetElement(dom::Element*& aElement,
nsCSSPseudoElements::Type& aPseudoType) const {
aElement = mElement;
aPseudoType = mPseudoType;
}
nsPresContext* GetRenderedPresContext() const;
private:
dom::Element* MOZ_NON_OWNING_REF mElement;
nsCSSPseudoElements::Type mPseudoType;
};
template <class EventInfo>
class DelayedEventDispatcher
{
public:
DelayedEventDispatcher() : mIsSorted(true) { }
void QueueEvent(EventInfo&& aEventInfo)
{
mPendingEvents.AppendElement(Forward<EventInfo>(aEventInfo));
mIsSorted = false;
}
// This is exposed as a separate method so that when we are dispatching
// *both* transition events and animation events we can sort both lists
// once using the current state of the document before beginning any
// dispatch.
void SortEvents()
{
if (mIsSorted) {
return;
}
// FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
// fixed.
std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
EventInfoLessThan());
mIsSorted = true;
}
// Takes a reference to the owning manager's pres context so it can
// detect if the pres context is destroyed while dispatching one of
// the events.
//
// This will call SortEvents automatically if it has not already been
// called.
void DispatchEvents(nsPresContext* const & aPresContext)
{
if (!aPresContext || mPendingEvents.IsEmpty()) {
return;
}
SortEvents();
EventArray events;
mPendingEvents.SwapElements(events);
// mIsSorted will be set to true by SortEvents above, and we leave it
// that way since mPendingEvents is now empty
for (EventInfo& info : events) {
EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
if (!aPresContext) {
break;
}
}
}
void ClearEventQueue()
{
mPendingEvents.Clear();
mIsSorted = true;
}
bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
// Methods for supporting cycle-collection
void Traverse(nsCycleCollectionTraversalCallback* aCallback,
const char* aName)
{
for (EventInfo& info : mPendingEvents) {
ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName);
}
}
void Unlink() { ClearEventQueue(); }
protected:
class EventInfoLessThan
{
public:
bool operator()(const EventInfo& a, const EventInfo& b) const
{
if (a.mTimeStamp != b.mTimeStamp) {
// Null timestamps sort first
if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) {
return a.mTimeStamp.IsNull();
} else {
return a.mTimeStamp < b.mTimeStamp;
}
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
AnimationPtrComparator<RefPtr<dom::Animation>> comparator;
return comparator.LessThan(a.mAnimation, b.mAnimation);
}
};
typedef nsTArray<EventInfo> EventArray;
EventArray mPendingEvents;
bool mIsSorted;
};
template <class EventInfo>
inline void
ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
{
aField.Unlink();
}
template <class EventInfo>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
DelayedEventDispatcher<EventInfo>& aField,
const char* aName,
uint32_t aFlags = 0)
{
aField.Traverse(&aCallback, aName);
}
} // namespace mozilla
#endif /* !defined(mozilla_css_AnimationCommon_h) */