Bug 1067701 - Implement Animation.target; r=birtles, r=bz
authorDavid Zbarsky <dzbarsky@gmail.com>
Thu, 02 Oct 2014 15:14:15 +0900
changeset 231568 28519d825a239e028fd12c8c59a8e53ba3f24ffc
parent 231567 35e518128ca302874769f17abcee7f754dae9d30
child 231569 c3fc41feeb22acb7f36c38ff8985c6767dfcaa83
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles, bz
bugs1067701
milestone35.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 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.