Bug 1067769 - Part 7: Define OwningAnimationTarget and use it. r=birtles draft
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 28 Apr 2016 23:22:43 +0800
changeset 357393 b938dbc00c918861d4141128fad8e30f61668e40
parent 357392 79b52ece67f16bd32ac8c07d698a9aae076fe5fe
child 357394 d1a8fe2d8fe89e4ffdb79844ab5581a9af3b2ce5
push id16759
push userbchiou@mozilla.com
push dateThu, 28 Apr 2016 15:27:13 +0000
reviewersbirtles
bugs1067769
milestone49.0a1
Bug 1067769 - Part 7: Define OwningAnimationTarget and use it. r=birtles 1. Define OwningAnimationTarget. 2. Replace the KeyframeEffectReadOnly::mTarget/mPseudoType members with a Maybe<OwningAnimationTarget> mTarget member. MozReview-Commit-ID: 65qOoNyDRSy
dom/animation/AnimationTarget.h
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
--- 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)
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -215,17 +215,17 @@ public:
               const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
               ErrorResult& aRv);
 
   void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
   Maybe<NonOwningAnimationTarget> GetTarget() const
   {
     Maybe<NonOwningAnimationTarget> result;
     if (mTarget) {
-      result.emplace(mTarget, mPseudoType);
+      result.emplace(*mTarget);
     }
     return result;
   }
   void GetFrames(JSContext*& aCx,
                  nsTArray<JSObject*>& aResult,
                  ErrorResult& aRv);
   void GetProperties(nsTArray<AnimationPropertyDetails>& aProperties,
                      ErrorResult& aRv) const;
@@ -359,21 +359,20 @@ protected:
   // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but
   //     filling forwards)
   //
   // As a result, we need to make sure this gets called whenever anything
   // changes with regards to this effects's timing including changes to the
   // owning Animation's timing.
   void UpdateTargetRegistration();
 
-  nsCOMPtr<Element> mTarget;
+  Maybe<OwningAnimationTarget> mTarget;
   RefPtr<Animation> mAnimation;
 
   RefPtr<AnimationEffectTimingReadOnly> mTiming;
-  CSSPseudoElementType mPseudoType;
 
   // The specified keyframes.
   nsTArray<Keyframe>          mFrames;
 
   // A set of per-property value arrays, derived from |mFrames|.
   nsTArray<AnimationProperty> mProperties;
 
   // The computed progress last time we composed the style rule. This is