Bug 1171817 part 2 - Add CSSAnimation::GetOwningElement; r=dbaron

In order to sort CSS animation objects correctly, we need to know which
element's animation-name property they appear in, if any. Normally that's
simply the target element of the animation's keyframe effect but it can differ
in the following cases:

1) When script modifies a CSSAnimation's effect to target a different element
   (or simply removes the effect altogether). In this case we use the
   *owning* element to determine the priority of the animation, not the target
   element.

   This scenario does not yet occur (bug 1049975).

2) When script creates a CSSAnimation object using the CSSAnimation constructor.
   In this case, the owning element should be empty (null) and we should
   determine the priority of the animation in the same way as any other
   Animation object.

   Again, this is not yet supported (or even specced) but will be eventually.

3) When script holds a reference to a CSSAnimation object but then updates the
   animation-name property such that the animation object is cancelled. In this
   case the owning element should be cleared (null) so we know to not to try and
   sort this with regard to any animation-name property.

   This is possible using code such as the following:

     elem.style.animation = 'a 5s';
     var a = elem.getAnimations()[0];
     elem.style.animation = 'b 5s';
     a.play(); // Bring a back to life
     document.timeline.getAnimations();
     // ^ At this point we need to know how to sort 'a' and 'b' which depends
     // on recognizing that a is no longer part of an animation-name list.

Until we implement bug 1049975, we could support sorting animations without
adding the reference to the owning element by setting a flag on the CSSAnimation
object but (having tried this) it turns out to be cleaner to just introduce this
reference now, particularly since we know we will need it later.

Note that we will also need this information in future to dispatch events to the
correct element in circumstances such as (1) once we separate updating timing
information (including events) from applying animation values.
This commit is contained in:
Brian Birtles 2015-06-09 11:13:53 +09:00
parent 8bc8dcf737
commit 0aad11786e
3 changed files with 67 additions and 7 deletions

View File

@ -127,11 +127,10 @@ public:
* CSSAnimation::PauseFromJS so we leave it for now.
*/
void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
// Wrapper functions for Animation DOM methods when called from style.
//
// Typically these DOM methods also notify style of changes but when
// we are calling from style we don't need to do this.
void CancelFromStyle() { DoCancel(); }
virtual void CancelFromStyle() { DoCancel(); }
void Tick();

View File

@ -552,6 +552,7 @@ nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
}
nsRefPtr<CSSAnimation> dest = new CSSAnimation(aTimeline, src.GetName());
dest->SetOwningElement(*aTarget, aStyleContext->GetPseudoType());
aAnimations.AppendElement(dest);
AnimationTiming timing;

View File

@ -56,10 +56,12 @@ namespace dom {
class CSSAnimation final : public Animation
{
public:
explicit CSSAnimation(DocumentTimeline* aTimeline,
explicit CSSAnimation(dom::DocumentTimeline* aTimeline,
const nsSubstring& aAnimationName)
: Animation(aTimeline)
: dom::Animation(aTimeline)
, mAnimationName(aAnimationName)
, mOwningElement(nullptr)
, mOwningPseudoType(nsCSSPseudoElements::ePseudo_NotPseudoElement)
, mIsStylePaused(false)
, mPauseShouldStick(false)
, mPreviousPhaseOrIteration(PREVIOUS_PHASE_BEFORE)
@ -92,11 +94,57 @@ public:
void PlayFromStyle();
void PauseFromStyle();
void CancelFromStyle() override
{
mOwningElement = nullptr;
mOwningPseudoType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
Animation::CancelFromStyle();
}
bool IsStylePaused() const { return mIsStylePaused; }
void QueueEvents(EventArray& aEventsToDispatch);
// Returns the element or pseudo-element whose animation-name property
// this CSSAnimation corresponds to (if any). This is used for determining
// the relative composite order of animations generated from CSS markup.
//
// Typically this will be the same as the target element of the keyframe
// effect associated with this animation. However, it can differ in the
// following circumstances:
//
// a) If script removes or replaces the effect of this animation,
// b) If this animation is cancelled (e.g. by updating the
// animation-name property or removing the owning element from the
// document),
// c) If this object is generated from script using the CSSAnimation
// constructor.
//
// For (b) and (c) the returned owning element will by nullptr and the
// pseudo-type will be nsCSSPseudoElements::ePseudo_NotPseudoElement.
void GetOwningElement(dom::Element*& aElement,
nsCSSPseudoElements::Type& aPseudoType) const {
MOZ_ASSERT(mOwningElement != nullptr ||
mOwningPseudoType ==
nsCSSPseudoElements::ePseudo_NotPseudoElement,
"When there is no owning element there should be no "
"pseudo-type");
aElement = mOwningElement;
aPseudoType = mOwningPseudoType;
}
// Sets the owning element and pseudo-type which is used for determining
// the composite order of CSSAnimation objects generated from CSS markup.
//
// @see GetOwningElement.
void SetOwningElement(dom::Element& aElement,
nsCSSPseudoElements::Type aPseudoType)
{
mOwningElement = &aElement;
mOwningPseudoType = aPseudoType;
}
// Is this animation currently in effect for the purposes of computing
// mWinsInCascade. (In general, this can be computed from the timing
// function. This boolean remembers the state as of the last time we
@ -105,13 +153,25 @@ public:
bool mInEffectForCascadeResults;
protected:
virtual ~CSSAnimation() { }
virtual ~CSSAnimation()
{
MOZ_ASSERT(!mOwningElement, "Owning element should be cleared before a "
"CSS animation is destroyed");
}
virtual css::CommonAnimationManager* GetAnimationManager() const override;
static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
nsString mAnimationName;
// The (pseudo-)element whose computed animation-name refers to this
// animation (if any).
//
// Raw pointer because this is only ever set when this object is part
// of mOwningElement's AnimationCollection.
dom::Element* MOZ_NON_OWNING_REF mOwningElement;
nsCSSPseudoElements::Type mOwningPseudoType;
// When combining animation-play-state with play() / pause() the following
// behavior applies:
// 1. pause() is sticky and always overrides the underlying