--- a/dom/animation/AnimationTarget.h
+++ b/dom/animation/AnimationTarget.h
@@ -3,30 +3,67 @@
/* 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_AnimationTarget_h
#define mozilla_AnimationTarget_h
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
#include "nsCSSPseudoElements.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
+struct OwningAnimationTarget
+{
+ OwningAnimationTarget(dom::Element* aElement, CSSPseudoElementType aType)
+ : mElement(aElement), mPseudoType(aType) { }
+
+ // mElement represents the parent element of a pseudo-element, not the
+ // generated content element.
+ RefPtr<dom::Element> mElement;
+ CSSPseudoElementType mPseudoType = CSSPseudoElementType::NotPseudo;
+};
+
struct NonOwningAnimationTarget
{
NonOwningAnimationTarget(dom::Element* aElement, CSSPseudoElementType aType)
: mElement(aElement), mPseudoType(aType) { }
+ explicit NonOwningAnimationTarget(const OwningAnimationTarget& aOther)
+ : mElement(aOther.mElement), mPseudoType(aOther.mPseudoType) { }
+
// mElement represents the parent element of a pseudo-element, not the
// generated content element.
dom::Element* MOZ_NON_OWNING_REF mElement = nullptr;
CSSPseudoElementType mPseudoType = CSSPseudoElementType::NotPseudo;
};
+
+// Helper functions for cycle-collecting Maybe<OwningAnimationTarget>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ Maybe<OwningAnimationTarget>& aTarget,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ if (aTarget) {
+ ImplCycleCollectionTraverse(aCallback, aTarget->mElement, aName, aFlags);
+ }
+}
+
+inline void
+ImplCycleCollectionUnlink(Maybe<OwningAnimationTarget>& aTarget)
+{
+ if (aTarget) {
+ ImplCycleCollectionUnlink(aTarget->mElement);
+ }
+}
+
} // namespace mozilla
#endif // mozilla_AnimationTarget_h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -86,22 +86,23 @@ KeyframeEffectReadOnly::KeyframeEffectRe
}
KeyframeEffectReadOnly::KeyframeEffectReadOnly(
nsIDocument* aDocument,
Element* aTarget,
CSSPseudoElementType aPseudoType,
AnimationEffectTimingReadOnly* aTiming)
: AnimationEffectReadOnly(aDocument)
- , mTarget(aTarget)
, mTiming(aTiming)
- , mPseudoType(aPseudoType)
, mInEffectOnLastAnimationTimingUpdate(false)
{
MOZ_ASSERT(aTiming);
+ if (aTarget) {
+ mTarget.emplace(aTarget, aPseudoType);
+ }
}
JSObject*
KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
}
@@ -156,17 +157,18 @@ KeyframeEffectReadOnly::NotifyAnimationT
ResetIsRunningOnCompositor();
}
// Detect changes to "in effect" status since we need to recalculate the
// animation cascade for this element whenever that changes.
bool inEffect = IsInEffect();
if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
if (mTarget) {
- EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
if (effectSet) {
effectSet->MarkCascadeNeedsUpdate();
}
}
mInEffectOnLastAnimationTimingUpdate = inEffect;
}
// Request restyle if necessary.
@@ -185,17 +187,17 @@ KeyframeEffectReadOnly::NotifyAnimationT
if (mAnimation && GetComputedTiming().mProgress != mProgressOnLastCompose) {
EffectCompositor::RestyleType restyleType =
CanThrottle() ?
EffectCompositor::RestyleType::Throttled :
EffectCompositor::RestyleType::Standard;
nsPresContext* presContext = GetPresContext();
if (presContext && mTarget) {
presContext->EffectCompositor()->
- RequestRestyle(mTarget, mPseudoType, restyleType,
+ RequestRestyle(mTarget->mElement, mTarget->mPseudoType, restyleType,
mAnimation->CascadeLevel());
}
// If we're not relevant, we will have been removed from the EffectSet.
// As a result, when the restyle we requested above is fulfilled, our
// ComposeStyle will not get called and mProgressOnLastCompose will not
// be updated. Instead, we need to manually clear it.
if (!isRelevant) {
@@ -454,20 +456,21 @@ KeyframeEffectReadOnly::SetFrames(JSCont
if (aRv.Failed()) {
return;
}
RefPtr<nsStyleContext> styleContext;
nsIPresShell* shell = doc->GetShell();
if (shell && mTarget) {
nsIAtom* pseudo =
- mPseudoType < CSSPseudoElementType::Count ?
- nsCSSPseudoElements::GetPseudoAtom(mPseudoType) : nullptr;
+ mTarget->mPseudoType < CSSPseudoElementType::Count ?
+ nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType) : nullptr;
styleContext =
- nsComputedDOMStyle::GetStyleContextForElement(mTarget, pseudo, shell);
+ nsComputedDOMStyle::GetStyleContextForElement(mTarget->mElement,
+ pseudo, shell);
}
SetFrames(Move(keyframes), styleContext);
}
void
KeyframeEffectReadOnly::SetFrames(nsTArray<Keyframe>&& aFrames,
nsStyleContext* aStyleContext)
@@ -521,18 +524,18 @@ void
KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
{
MOZ_ASSERT(aStyleContext);
nsTArray<AnimationProperty> properties;
if (mTarget) {
properties =
KeyframeUtils::GetAnimationPropertiesFromKeyframes(aStyleContext,
- mTarget,
- mPseudoType,
+ mTarget->mElement,
+ mTarget->mPseudoType,
mFrames);
}
if (mProperties == properties) {
return;
}
// Preserve the state of mWinsInCascade and mIsRunningOnCompositor flags.
@@ -553,26 +556,27 @@ KeyframeEffectReadOnly::UpdateProperties
for (AnimationProperty& property : mProperties) {
property.mWinsInCascade =
winningInCascadeProperties.HasProperty(property.mProperty);
property.mIsRunningOnCompositor =
runningOnCompositorProperties.HasProperty(property.mProperty);
}
if (mTarget) {
- EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
if (effectSet) {
effectSet->MarkCascadeNeedsUpdate();
}
if (mAnimation) {
nsPresContext* presContext = GetPresContext();
if (presContext) {
presContext->EffectCompositor()->
- RequestRestyle(mTarget, mPseudoType,
+ RequestRestyle(mTarget->mElement, mTarget->mPseudoType,
EffectCompositor::RestyleType::Layer,
mAnimation->CascadeLevel());
}
}
}
}
void
@@ -780,25 +784,26 @@ KeyframeEffectReadOnly::UpdateTargetRegi
// Animation::IsRelevant() returns a cached value. It only updates when
// something calls Animation::UpdateRelevance. Whenever our timing changes,
// we should be notifying our Animation before calling this, so
// Animation::IsRelevant() should be up-to-date by the time we get here.
MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
"Out of date Animation::IsRelevant value");
if (isRelevant) {
- EffectSet* effectSet = EffectSet::GetOrCreateEffectSet(mTarget,
- mPseudoType);
+ EffectSet* effectSet =
+ EffectSet::GetOrCreateEffectSet(mTarget->mElement, mTarget->mPseudoType);
effectSet->AddEffect(*this);
} else {
- EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
if (effectSet) {
effectSet->RemoveEffect(*this);
if (effectSet->IsEmpty()) {
- EffectSet::DestroyEffectSet(mTarget, mPseudoType);
+ EffectSet::DestroyEffectSet(mTarget->mElement, mTarget->mPseudoType);
}
}
}
}
#ifdef DEBUG
void
DumpAnimationProperties(nsTArray<AnimationProperty>& aAnimationProperties)
@@ -847,25 +852,26 @@ void
KeyframeEffectReadOnly::GetTarget(
Nullable<OwningElementOrCSSPseudoElement>& aRv) const
{
if (!mTarget) {
aRv.SetNull();
return;
}
- switch (mPseudoType) {
+ switch (mTarget->mPseudoType) {
case CSSPseudoElementType::before:
case CSSPseudoElementType::after:
aRv.SetValue().SetAsCSSPseudoElement() =
- CSSPseudoElement::GetCSSPseudoElement(mTarget, mPseudoType);
+ CSSPseudoElement::GetCSSPseudoElement(mTarget->mElement,
+ mTarget->mPseudoType);
break;
case CSSPseudoElementType::NotPseudo:
- aRv.SetValue().SetAsElement() = mTarget;
+ aRv.SetValue().SetAsElement() = mTarget->mElement;
break;
default:
NS_NOTREACHED("Animation of unsupported pseudo-type");
aRv.SetNull();
}
}
@@ -1061,17 +1067,18 @@ KeyframeEffectReadOnly::CanThrottle() co
LayerAnimationInfo::sRecords) {
// Skip properties that are overridden in the cascade.
// (GetAnimationOfProperty, as called by HasAnimationOfProperty,
// only returns an animation if it currently wins in the cascade.)
if (!HasAnimationOfProperty(record.mProperty)) {
continue;
}
- EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
MOZ_ASSERT(effectSet, "CanThrottle should be called on an effect "
"associated with a target element");
layers::Layer* layer =
FrameLayerBuilder::GetDedicatedLayer(frame, record.mLayerType);
// Unthrottle if the layer needs to be brought up to date
if (!layer ||
effectSet->GetAnimationGeneration() !=
layer->GetAnimationGeneration()) {
@@ -1109,17 +1116,18 @@ KeyframeEffectReadOnly::CanThrottleTrans
nsPresContext* presContext = GetPresContext();
// CanThrottleTransformChanges is only called as part of a refresh driver tick
// in which case we expect to has a pres context.
MOZ_ASSERT(presContext);
TimeStamp now =
presContext->RefreshDriver()->MostRecentRefresh();
- EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
+ EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
+ mTarget->mPseudoType);
MOZ_ASSERT(effectSet, "CanThrottleTransformChanges is expected to be called"
" on an effect in an effect set");
MOZ_ASSERT(mAnimation, "CanThrottleTransformChanges is expected to be called"
" on an effect with a parent animation");
TimeStamp animationRuleRefreshTime =
effectSet->AnimationRuleRefreshTime(mAnimation->CascadeLevel());
// If this animation can cause overflow, we can throttle some of the ticks.
if (!animationRuleRefreshTime.IsNull() &&
@@ -1147,43 +1155,43 @@ KeyframeEffectReadOnly::CanThrottleTrans
nsIFrame*
KeyframeEffectReadOnly::GetAnimationFrame() const
{
if (!mTarget) {
return nullptr;
}
- nsIFrame* frame = mTarget->GetPrimaryFrame();
+ nsIFrame* frame = mTarget->mElement->GetPrimaryFrame();
if (!frame) {
return nullptr;
}
- if (mPseudoType == CSSPseudoElementType::before) {
+ if (mTarget->mPseudoType == CSSPseudoElementType::before) {
frame = nsLayoutUtils::GetBeforeFrame(frame);
- } else if (mPseudoType == CSSPseudoElementType::after) {
+ } else if (mTarget->mPseudoType == CSSPseudoElementType::after) {
frame = nsLayoutUtils::GetAfterFrame(frame);
} else {
- MOZ_ASSERT(mPseudoType == CSSPseudoElementType::NotPseudo,
- "unknown mPseudoType");
+ MOZ_ASSERT(mTarget->mPseudoType == CSSPseudoElementType::NotPseudo,
+ "unknown mTarget->mPseudoType");
}
if (!frame) {
return nullptr;
}
return nsLayoutUtils::GetStyleFrame(frame);
}
nsIDocument*
KeyframeEffectReadOnly::GetRenderedDocument() const
{
if (!mTarget) {
return nullptr;
}
- return mTarget->GetComposedDoc();
+ return mTarget->mElement->GetComposedDoc();
}
nsPresContext*
KeyframeEffectReadOnly::GetPresContext() const
{
nsIDocument* doc = GetRenderedDocument();
if (!doc) {
return nullptr;
@@ -1291,17 +1299,17 @@ KeyframeEffectReadOnly::SetPerformanceWa
(!property.mPerformanceWarning ||
*property.mPerformanceWarning != aWarning)) {
property.mPerformanceWarning = Some(aWarning);
nsXPIDLString localizedString;
if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
property.mPerformanceWarning->ToLocalizedString(localizedString)) {
nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
- AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget);
+ AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget->mElement);
}
return;
}
}
}
//---------------------------------------------------------------------
//
@@ -1348,29 +1356,30 @@ KeyframeEffect::Constructor(
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
aOptions, aRv);
}
void KeyframeEffect::NotifySpecifiedTimingUpdated()
{
// Use the same document for a pseudo element and its parent element.
// Use nullptr if we don't have mTarget, so disable the mutation batch.
- nsAutoAnimationMutationBatch mb(mTarget ? mTarget->OwnerDoc() : nullptr);
+ nsAutoAnimationMutationBatch mb(mTarget ? mTarget->mElement->OwnerDoc()
+ : nullptr);
if (mAnimation) {
mAnimation->NotifyEffectTimingUpdated();
if (mAnimation->IsRelevant()) {
nsNodeUtils::AnimationChanged(mAnimation);
}
nsPresContext* presContext = GetPresContext();
if (presContext && mTarget) {
presContext->EffectCompositor()->
- RequestRestyle(mTarget, mPseudoType,
+ RequestRestyle(mTarget->mElement, mTarget->mPseudoType,
EffectCompositor::RestyleType::Layer,
mAnimation->CascadeLevel());
}
}
}
void
KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)