Bug 1067701 - Implement Animation.target; r=birtles, r=bz
authorDavid Zbarsky <dzbarsky@gmail.com>
Thu, 02 Oct 2014 15:14:15 +0900
changeset 208353 28519d825a239e028fd12c8c59a8e53ba3f24ffc
parent 208352 35e518128ca302874769f17abcee7f754dae9d30
child 208354 c3fc41feeb22acb7f36c38ff8985c6767dfcaa83
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbirtles, bz
bugs1067701
milestone35.0a1
Bug 1067701 - Implement Animation.target; r=birtles, r=bz
dom/animation/Animation.cpp
dom/animation/Animation.h
dom/animation/test/css-integration/test_animation-target.html
dom/animation/test/mochitest.ini
dom/webidl/Animation.webidl
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -53,17 +53,17 @@ ComputedTimingFunction::GetValue(double 
 }
 
 // In the Web Animations model, the time fraction can be outside the range
 // [0.0, 1.0] but it shouldn't be Infinity.
 const double ComputedTiming::kNullTimeFraction = PositiveInfinity<double>();
 
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Animation, mDocument)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Animation, mDocument, mTarget)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Animation, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Animation, Release)
 
 JSObject*
 Animation::WrapObject(JSContext* aCx)
 {
   return AnimationBinding::Wrap(aCx, this);
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -3,22 +3,24 @@
  * 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_dom_Animation_h
 #define mozilla_dom_Animation_h
 
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsCSSPseudoElements.h"
 #include "nsIDocument.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
 
 struct JSContext;
 
 namespace mozilla {
 
@@ -123,24 +125,29 @@ struct ElementPropertyTransition;
 namespace dom {
 
 class AnimationEffect;
 
 class Animation : public nsWrapperCache
 {
 public:
   Animation(nsIDocument* aDocument,
+            Element* aTarget,
+            nsCSSPseudoElements::Type aPseudoType,
             const AnimationTiming &aTiming,
             const nsSubstring& aName)
     : mDocument(aDocument)
+    , mTarget(aTarget)
     , mTiming(aTiming)
     , mName(aName)
     , mIsFinishedTransition(false)
     , mLastNotification(LAST_NOTIFICATION_NONE)
+    , mPseudoType(aPseudoType)
   {
+    MOZ_ASSERT(aTarget, "null animation target is not yet supported");
     SetIsDOMBinding();
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Animation)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Animation)
 
   nsIDocument* GetParentObject() const { return mDocument; }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
@@ -152,16 +159,25 @@ public:
   virtual const ElementPropertyTransition* AsTransition() const {
     return nullptr;
   }
 
   // Animation interface
   // This currently returns a new object each time when used from C++ but is
   // cached when used from JS.
   already_AddRefed<AnimationEffect> GetEffect();
+  Element* GetTarget() const {
+    // Currently we only implement Element.getAnimationPlayers() which only
+    // returns animations targetting Elements so we should this should never
+    // be called for an animation that targets a pseudo-element.
+    MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
+               "Requesting the target of an Animation that targets a"
+               " pseudo-element is not yet supported.");
+    return mTarget;
+  }
 
   void SetParentTime(Nullable<TimeDuration> aParentTime);
 
   const AnimationTiming& Timing() const {
     return mTiming;
   }
   AnimationTiming& Timing() {
     return mTiming;
@@ -244,27 +260,29 @@ public:
     return mProperties;
   }
 
 protected:
   virtual ~Animation() { }
 
   // We use a document for a parent object since the other likely candidate,
   // the target element, can be empty.
-  nsRefPtr<nsIDocument> mDocument;
+  nsCOMPtr<nsIDocument> mDocument;
+  nsCOMPtr<Element> mTarget;
   Nullable<TimeDuration> mParentTime;
 
   AnimationTiming mTiming;
   nsString mName;
   // A flag to mark transitions that have finished and are due to
   // be removed on the next throttle-able cycle.
   bool mIsFinishedTransition;
   // One of the LAST_NOTIFICATION_* constants, or an integer for the iteration
   // whose start we last notified on.
   uint64_t mLastNotification;
+  nsCSSPseudoElements::Type mPseudoType;
 
   InfallibleTArray<AnimationProperty> mProperties;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Animation_h
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-integration/test_animation-target.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<style>
+@keyframes anim { }
+</style>
+<script>
+'use strict';
+
+function addDiv() {
+  var div = document.createElement('div');
+  document.body.appendChild(div);
+  return div;
+}
+
+test(function() {
+  var div = addDiv();
+  div.style.animation = 'anim 100s';
+  var players = div.getAnimationPlayers();
+  assert_equals(players[0].source.target, div,
+    'Animation.target is the animatable div');
+  div.remove();
+}, 'Returned CSS animations have the correct Animation.target');
+
+test(function() {
+  var div = addDiv();
+
+  div.style.left = '0px';
+  window.getComputedStyle(div).transitionProperty;
+  div.style.transition = 'left 100s';
+  div.style.left = '100px';
+
+  var players = div.getAnimationPlayers();
+  assert_equals(players[0].source.target, div,
+    'Animation.target is the animatable div');
+  div.remove();
+}, 'Returned CSS transitions have the correct Animation.target');
+
+</script>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,6 +1,7 @@
 [animation-timeline/test_animation-timeline.html]
 skip-if = buildapp == 'mulet'
 [css-integration/test_element-get-animation-players.html]
 skip-if = buildapp == 'mulet'
 [css-integration/test_animations-dynamic-changes.html]
 [css-integration/test_animation-effect-name.html]
+[css-integration/test_animation-target.html]
--- a/dom/webidl/Animation.webidl
+++ b/dom/webidl/Animation.webidl
@@ -9,10 +9,13 @@
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Pref="dom.animations-api.core.enabled"]
 interface Animation {
   // FIXME: |effect| should have type (AnimationEffect or EffectCallback)?
   // but we haven't implemented EffectCallback yet.
-  [Cached,Pure] readonly attribute AnimationEffect? effect;
+  [Cached,Pure]
+  readonly attribute AnimationEffect? effect;
+  // FIXME: This should be writeable (bug 1067769)
+  readonly attribute Element?         target;
 };
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -244,17 +244,17 @@ nsAnimationManager::CheckAnimationRule(n
         disp->mAnimationNameCount == 1 &&
         disp->mAnimations[0].GetName().IsEmpty()) {
       return nullptr;
     }
 
     // build the animations list
     dom::AnimationTimeline* timeline = aElement->OwnerDoc()->Timeline();
     AnimationPlayerPtrArray newPlayers;
-    BuildAnimations(aStyleContext, timeline, newPlayers);
+    BuildAnimations(aStyleContext, aElement, timeline, newPlayers);
 
     if (newPlayers.IsEmpty()) {
       if (collection) {
         collection->Destroy();
       }
       return nullptr;
     }
 
@@ -408,16 +408,17 @@ ResolvedStyleCache::Get(nsPresContext *a
     mCache.Put(aKeyframe, resultStrong);
     result = resultStrong;
   }
   return result;
 }
 
 void
 nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
+                                    dom::Element* aTarget,
                                     dom::AnimationTimeline* aTimeline,
                                     AnimationPlayerPtrArray& aPlayers)
 {
   NS_ABORT_IF_FALSE(aPlayers.IsEmpty(), "expect empty array");
 
   ResolvedStyleCache resolvedStyles;
 
   const nsStyleDisplay *disp = aStyleContext->StyleDisplay();
@@ -448,17 +449,18 @@ nsAnimationManager::BuildAnimations(nsSt
     timing.mIterationDuration =
       TimeDuration::FromMilliseconds(src.GetDuration());
     timing.mDelay = TimeDuration::FromMilliseconds(src.GetDelay());
     timing.mIterationCount = src.GetIterationCount();
     timing.mDirection = src.GetDirection();
     timing.mFillMode = src.GetFillMode();
 
     nsRefPtr<Animation> destAnim =
-      new Animation(mPresContext->Document(), timing, src.GetName());
+      new Animation(mPresContext->Document(), aTarget,
+                    aStyleContext->GetPseudoType(), timing, src.GetName());
     dest->SetSource(destAnim);
 
     dest->mStartTime = now;
     dest->mPlayState = src.GetPlayState();
     if (dest->IsPaused()) {
       dest->mHoldTime.SetValue(TimeDuration(0));
     }
 
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -143,16 +143,17 @@ protected:
 
   /**
    * Check to see if we should stop or start observing the refresh driver
    */
   void CheckNeedsRefresh();
 
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
+                       mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPlayerPtrArray& aAnimations);
   bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
                       aSegments,
                     nsCSSProperty aProperty,
                     const mozilla::StyleAnimation& aAnimation,
                     float aFromKey, nsStyleContext* aFromContext,
                     mozilla::css::Declaration* aFromDeclaration,
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -510,17 +510,18 @@ nsTransitionManager::ConsiderStartingTra
   AnimationTiming timing;
   timing.mIterationDuration = TimeDuration::FromMilliseconds(duration);
   timing.mDelay = TimeDuration::FromMilliseconds(delay);
   timing.mIterationCount = 1;
   timing.mDirection = NS_STYLE_ANIMATION_DIRECTION_NORMAL;
   timing.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS;
 
   nsRefPtr<ElementPropertyTransition> pt =
-    new ElementPropertyTransition(aElement->OwnerDoc(), timing);
+    new ElementPropertyTransition(aElement->OwnerDoc(), aElement,
+                                  aNewStyleContext->GetPseudoType(), timing);
   pt->mStartForReversingTest = startForReversingTest;
   pt->mReversePortion = reversePortion;
 
   AnimationProperty& prop = *pt->Properties().AppendElement();
   prop.mProperty = aProperty;
 
   AnimationPropertySegment& segment = *prop.mSegments.AppendElement();
   segment.mFromValue = startValue;
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -27,18 +27,21 @@ struct StyleTransition;
  * Per-Element data                                                          *
  *****************************************************************************/
 
 namespace mozilla {
 
 struct ElementPropertyTransition : public dom::Animation
 {
   ElementPropertyTransition(nsIDocument* aDocument,
+                            dom::Element* aTarget,
+                            nsCSSPseudoElements::Type aPseudoType,
                             const AnimationTiming &aTiming)
-    : dom::Animation(aDocument, aTiming, EmptyString()) { }
+    : dom::Animation(aDocument, aTarget, aPseudoType, aTiming, EmptyString())
+  { }
 
   virtual ElementPropertyTransition* AsTransition() { return this; }
   virtual const ElementPropertyTransition* AsTransition() const { return this; }
 
   // This is the start value to be used for a check for whether a
   // transition is being reversed.  Normally the same as
   // mProperties[0].mSegments[0].mFromValue, except when this transition
   // started as the reversal of another in-progress transition.