Bug 1067769 - Part 10: Implement SetTarget(). r=birtles
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 28 Apr 2016 23:22:43 +0800
changeset 295322 f70f145ab9b88f657d7f18cb6acc5f2cee851a22
parent 295321 7e126b275f8a374117d6d827a5ba8b76b72d0920
child 295323 6383fb78ede95870419853b35eb85c048bf73ae0
push id30223
push userkwierso@gmail.com
push dateFri, 29 Apr 2016 21:57:49 +0000
treeherdermozilla-central@2b7c421063ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1067769
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1067769 - Part 10: Implement SetTarget(). r=birtles MozReview-Commit-ID: GW3Ujn5cbY3
dom/animation/AnimationTarget.h
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
--- a/dom/animation/AnimationTarget.h
+++ b/dom/animation/AnimationTarget.h
@@ -21,16 +21,22 @@ class Element;
 struct OwningAnimationTarget
 {
   OwningAnimationTarget(dom::Element* aElement, CSSPseudoElementType aType)
     : mElement(aElement), mPseudoType(aType) { }
 
   explicit OwningAnimationTarget(dom::Element* aElement)
     : mElement(aElement) { }
 
+  bool operator==(const OwningAnimationTarget& aOther) const
+  {
+     return mElement == aOther.mElement &&
+            mPseudoType == aOther.mPseudoType;
+  }
+
   // mElement represents the parent element of a pseudo-element, not the
   // generated content element.
   RefPtr<dom::Element> mElement;
   CSSPseudoElementType mPseudoType = CSSPseudoElementType::NotPseudo;
 };
 
 struct NonOwningAnimationTarget
 {
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -769,16 +769,24 @@ void
 KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
 {
   for (AnimationProperty& property : mProperties) {
     property.mIsRunningOnCompositor = false;
   }
 }
 
 void
+KeyframeEffectReadOnly::ResetWinsInCascade()
+{
+  for (AnimationProperty& property : mProperties) {
+    property.mWinsInCascade = false;
+  }
+}
+
+void
 KeyframeEffectReadOnly::UpdateTargetRegistration()
 {
   if (!mTarget) {
     return;
   }
 
   bool isRelevant = mAnimation && mAnimation->IsRelevant();
 
@@ -1393,22 +1401,68 @@ KeyframeEffect::NotifySpecifiedTimingUpd
 
     RequestRestyle(EffectCompositor::RestyleType::Layer);
   }
 }
 
 void
 KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
 {
-  // TODO: fix in next patch
+  Maybe<OwningAnimationTarget> newTarget = ConvertTarget(aTarget);
+  if (mTarget == newTarget) {
+    // Assign the same target, skip it.
+    return;
+  }
+
+  if (mTarget) {
+    UnregisterTarget();
+    ResetIsRunningOnCompositor();
+    ResetWinsInCascade();
+
+    RequestRestyle(EffectCompositor::RestyleType::Layer);
+  }
+
+  mTarget = newTarget;
+
+  if (mTarget) {
+    UpdateTargetRegistration();
+    MaybeUpdateProperties();
+
+    RequestRestyle(EffectCompositor::RestyleType::Layer);
+  }
 }
 
 KeyframeEffect::~KeyframeEffect()
 {
   // mTiming is cycle collected, so we have to do null check first even though
   // mTiming shouldn't be null during the lifetime of KeyframeEffect.
   if (mTiming) {
     mTiming->Unlink();
   }
 }
 
+void
+KeyframeEffect::MaybeUpdateProperties()
+{
+  if (!mTarget) {
+    return;
+  }
+
+  nsIDocument* doc = mTarget->mElement->OwnerDoc();
+  if (!doc) {
+    return;
+  }
+
+  nsIAtom* pseudo = mTarget->mPseudoType < CSSPseudoElementType::Count ?
+                    nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType) :
+                    nullptr;
+  RefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElement(mTarget->mElement, pseudo,
+                                                  doc->GetShell());
+  if (!styleContext) {
+    return;
+  }
+
+  UpdateProperties(styleContext);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -348,16 +348,17 @@ protected:
   static already_AddRefed<KeyframeEffectType>
   ConstructKeyframeEffect(const GlobalObject& aGlobal,
                           const Nullable<ElementOrCSSPseudoElement>& aTarget,
                           JS::Handle<JSObject*> aFrames,
                           const OptionsType& aOptions,
                           ErrorResult& aRv);
 
   void ResetIsRunningOnCompositor();
+  void ResetWinsInCascade();
 
   // This effect is registered with its target element so long as:
   //
   // (a) It has a target element, and
   // (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
@@ -432,18 +433,29 @@ public:
   Constructor(const GlobalObject& aGlobal,
               const Nullable<ElementOrCSSPseudoElement>& aTarget,
               JS::Handle<JSObject*> aFrames,
               const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
               ErrorResult& aRv);
 
   void NotifySpecifiedTimingUpdated();
 
+  // This method calls MaybeUpdateProperties which is not safe to use when
+  // we are in the middle of updating style. If we need to use this when
+  // updating style, we should pass the nsStyleContext into this method and use
+  // that to update the properties rather than calling
+  // GetStyleContextForElement.
   void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
 
 protected:
   ~KeyframeEffect() override;
+
+  // We need to be careful to *not* call this when we are updating the style
+  // context. That's because calling GetStyleContextForElement when we are in
+  // the process of building a style context may trigger various forms of
+  // infinite recursion.
+  void MaybeUpdateProperties();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffect_h