Bug 1174575 - Part 6: Implement KeyframeEffectReadOnly Constructor for CSSPseudoElement. r=birtles
authorBoris Chiou <boris.chiou@gmail.com>
Tue, 09 Feb 2016 05:05:00 +0100
changeset 329812 70be723fde4a63c1aa4f12eef7cdde821c6cd446
parent 329811 989f065259c4318ab6684d876204a4db67e58a46
child 329813 b973ffb1e6421cf7a4f5c2d5a5ea1ae1a1bd6c1f
push id10611
push userdmitchell@mozilla.com
push dateTue, 09 Feb 2016 15:15:43 +0000
reviewersbirtles
bugs1174575
milestone47.0a1
Bug 1174575 - Part 6: Implement KeyframeEffectReadOnly Constructor for CSSPseudoElement. r=birtles Let KeyframeEffectReadOnly::Constructor support both Element and CSSPseudoElement as the target.
dom/animation/CSSPseudoElement.h
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
--- a/dom/animation/CSSPseudoElement.h
+++ b/dom/animation/CSSPseudoElement.h
@@ -35,16 +35,17 @@ public:
   ParentObject GetParentObject() const
   {
     return mParentElement->GetParentObject();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
+  nsCSSPseudoElements::Type GetType() const { return mPseudoType; }
   void GetType(nsString& aRetVal) const
   {
     MOZ_ASSERT(nsCSSPseudoElements::GetPseudoAtom(mPseudoType),
                "All pseudo-types allowed by this class should have a"
                " corresponding atom");
     nsCSSPseudoElements::GetPseudoAtom(mPseudoType)->ToString(aRetVal);
   }
   already_AddRefed<Element> ParentElement() const
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1142,32 +1142,36 @@ ApplyDistributeSpacing(nsTArray<OffsetIn
  * Splits out each property's keyframe animation segment information
  * from the OffsetIndexedKeyframe objects into an array of KeyframeValueEntry.
  *
  * The easing string value in OffsetIndexedKeyframe objects is parsed
  * into a ComputedTimingFunction value in the corresponding KeyframeValueEntry
  * objects.
  *
  * @param aTarget The target of the animation.
+ * @param aPseudoType The pseudo type of the target if it is a pseudo element.
  * @param aKeyframes The keyframes to read.
  * @param aResult The array to append the resulting KeyframeValueEntry
  *   objects to.
  */
 static void
 GenerateValueEntries(Element* aTarget,
+                     nsCSSPseudoElements::Type aPseudoType,
                      nsTArray<OffsetIndexedKeyframe>& aKeyframes,
                      nsTArray<KeyframeValueEntry>& aResult,
                      ErrorResult& aRv)
 {
   nsCSSPropertySet properties;              // All properties encountered.
   nsCSSPropertySet propertiesWithFromValue; // Those with a defined 0% value.
   nsCSSPropertySet propertiesWithToValue;   // Those with a defined 100% value.
 
   for (OffsetIndexedKeyframe& keyframe : aKeyframes) {
     float offset = float(keyframe.mKeyframeDict.mOffset.Value());
+    // ParseEasing uses element's owner doc, so if it is a pseudo element,
+    // we use its parent element's owner doc.
     Maybe<ComputedTimingFunction> easing =
       AnimationUtils::ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing);
     // We ignore keyframe.mKeyframeDict.mComposite since we don't support
     // composite modes on keyframes yet.
 
     // keyframe.mPropertyValuePairs is currently sorted by CSS property IDL
     // name, since that was the order we read the properties from the JS
     // object.  Re-sort the list so that longhand properties appear before
@@ -1192,24 +1196,23 @@ GenerateValueEntries(Element* aTarget,
     nsCSSPropertySet propertiesOnThisKeyframe;
     for (const PropertyValuesPair& pair : keyframe.mPropertyValuePairs) {
       MOZ_ASSERT(pair.mValues.Length() == 1,
                  "ConvertKeyframeSequence should have parsed single "
                  "DOMString values from the property-values pairs");
       // Parse the property's string value and produce a KeyframeValueEntry (or
       // more than one, for shorthands) for it.
       nsTArray<PropertyStyleAnimationValuePair> values;
-      if (StyleAnimationValue::ComputeValues(
-            pair.mProperty,
-            nsCSSProps::eEnabledForAllContent,
-            aTarget,
-            nsCSSPseudoElements::ePseudo_NotPseudoElement,
-            pair.mValues[0],
-            /* aUseSVGMode */ false,
-            values)) {
+      if (StyleAnimationValue::ComputeValues(pair.mProperty,
+                                             nsCSSProps::eEnabledForAllContent,
+                                             aTarget,
+                                             aPseudoType,
+                                             pair.mValues[0],
+                                             /* aUseSVGMode */ false,
+                                             values)) {
         for (auto& value : values) {
           // If we already got a value for this property on the keyframe,
           // skip this one.
           if (propertiesOnThisKeyframe.HasProperty(value.mProperty)) {
             continue;
           }
 
           KeyframeValueEntry* entry = aResult.AppendElement();
@@ -1350,16 +1353,17 @@ BuildSegmentsFromValueEntries(nsTArray<K
  *   object to iterate over as a sequence.
  * @param aResult The array into which the resulting AnimationProperty
  *   objects will be appended.
  */
 static void
 BuildAnimationPropertyListFromKeyframeSequence(
     JSContext* aCx,
     Element* aTarget,
+    nsCSSPseudoElements::Type aPseudoType,
     JS::ForOfIterator& aIterator,
     nsTArray<AnimationProperty>& aResult,
     ErrorResult& aRv)
 {
   // Convert the object in aIterator to sequence<Keyframe>, producing
   // an array of OffsetIndexedKeyframe objects.
   AutoTArray<OffsetIndexedKeyframe,4> keyframes;
   if (!ConvertKeyframeSequence(aCx, aIterator, keyframes)) {
@@ -1383,17 +1387,17 @@ BuildAnimationPropertyListFromKeyframeSe
   // Fill in 0%/100% values if the first/element keyframes don't have
   // a specified offset, and evenly space those that have a missing
   // offset.  (We don't support paced spacing yet.)
   ApplyDistributeSpacing(keyframes);
 
   // Convert the OffsetIndexedKeyframes into a list of KeyframeValueEntry
   // objects.
   nsTArray<KeyframeValueEntry> entries;
-  GenerateValueEntries(aTarget, keyframes, entries, aRv);
+  GenerateValueEntries(aTarget, aPseudoType, keyframes, entries, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   // Finally, build an array of AnimationProperty objects in aResult
   // corresponding to the entries.
   BuildSegmentsFromValueEntries(entries, aResult);
 }
@@ -1407,31 +1411,34 @@ BuildAnimationPropertyListFromKeyframeSe
  * @param aValue The JS object.
  * @param aResult The array into which the resulting AnimationProperty
  *   objects will be appended.
  */
 static void
 BuildAnimationPropertyListFromPropertyIndexedKeyframes(
     JSContext* aCx,
     Element* aTarget,
+    nsCSSPseudoElements::Type aPseudoType,
     JS::Handle<JS::Value> aValue,
     InfallibleTArray<AnimationProperty>& aResult,
     ErrorResult& aRv)
 {
   MOZ_ASSERT(aValue.isObject());
 
   // Convert the object to a PropertyIndexedKeyframes dictionary to
   // get its explicit dictionary members.
   binding_detail::FastPropertyIndexedKeyframes keyframes;
   if (!keyframes.Init(aCx, aValue, "PropertyIndexedKeyframes argument",
                       false)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
+  // ParseEasing uses element's owner doc, so if it is a pseudo element,
+  // we use its parent element's owner doc.
   Maybe<ComputedTimingFunction> easing =
     AnimationUtils::ParseEasing(aTarget, keyframes.mEasing);
 
   // We ignore easing.mComposite since we don't support composite modes on
   // keyframes yet.
 
   // Get all the property--value-list pairs off the object.
   JS::Rooted<JSObject*> object(aCx, &aValue.toObject());
@@ -1474,24 +1481,23 @@ BuildAnimationPropertyListFromPropertyIn
     //   0.25 -> 0.50 : green -> yellow
     //   0.50 -> 1.00 : yellow -> blue
     //
     // With future spec clarifications we might decide to preserve the invalid
     // value on the segment and make the animation code deal with the invalid
     // value instead.
     nsTArray<PropertyStyleAnimationValuePair> fromValues;
     float fromKey = 0.0f;
-    if (!StyleAnimationValue::ComputeValues(
-          pair.mProperty,
-          nsCSSProps::eEnabledForAllContent,
-          aTarget,
-          nsCSSPseudoElements::ePseudo_NotPseudoElement,
-          pair.mValues[0],
-          /* aUseSVGMode */ false,
-          fromValues)) {
+    if (!StyleAnimationValue::ComputeValues(pair.mProperty,
+                                            nsCSSProps::eEnabledForAllContent,
+                                            aTarget,
+                                            aPseudoType,
+                                            pair.mValues[0],
+                                            /* aUseSVGMode */ false,
+                                            fromValues)) {
       // We need to throw for an invalid first value, since that would imply an
       // additive animation, which we don't support yet.
       aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
       return;
     }
 
     if (fromValues.IsEmpty()) {
       // All longhand components of a shorthand pair.mProperty must be disabled.
@@ -1528,24 +1534,23 @@ BuildAnimationPropertyListFromPropertyIn
         properties.AddProperty(p);
       }
     }
 
     double portion = 1.0 / (count - 1);
     for (size_t i = 0; i < count - 1; ++i) {
       nsTArray<PropertyStyleAnimationValuePair> toValues;
       float toKey = (i + 1) * portion;
-      if (!StyleAnimationValue::ComputeValues(
-            pair.mProperty,
-            nsCSSProps::eEnabledForAllContent,
-            aTarget,
-            nsCSSPseudoElements::ePseudo_NotPseudoElement,
-            pair.mValues[i + 1],
-            /* aUseSVGMode */ false,
-            toValues)) {
+      if (!StyleAnimationValue::ComputeValues(pair.mProperty,
+                                              nsCSSProps::eEnabledForAllContent,
+                                              aTarget,
+                                              aPseudoType,
+                                              pair.mValues[i + 1],
+                                              /* aUseSVGMode */ false,
+                                              toValues)) {
         if (i + 1 == count - 1) {
           // We need to throw for an invalid last value, since that would
           // imply an additive animation, which we don't support yet.
           aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
           return;
         }
         // Otherwise, skip the segment.
         continue;
@@ -1581,16 +1586,17 @@ BuildAnimationPropertyListFromPropertyIn
  *   that is the keyframe list specification.
  * @param aResult The array into which the resulting AnimationProperty
  *   objects will be appended.
  */
 /* static */ void
 KeyframeEffectReadOnly::BuildAnimationPropertyList(
     JSContext* aCx,
     Element* aTarget,
+    nsCSSPseudoElements::Type aPseudoType,
     JS::Handle<JSObject*> aFrames,
     InfallibleTArray<AnimationProperty>& aResult,
     ErrorResult& aRv)
 {
   MOZ_ASSERT(aResult.IsEmpty());
 
   // A frame list specification in the IDL is:
   //
@@ -1614,58 +1620,71 @@ KeyframeEffectReadOnly::BuildAnimationPr
   JS::Rooted<JS::Value> objectValue(aCx, JS::ObjectValue(*aFrames));
   JS::ForOfIterator iter(aCx);
   if (!iter.init(objectValue, JS::ForOfIterator::AllowNonIterable)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (iter.valueIsIterable()) {
-    BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, iter,
-                                                   aResult, aRv);
+    BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, aPseudoType,
+                                                   iter, aResult, aRv);
   } else {
     BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget,
+                                                           aPseudoType,
                                                            objectValue, aResult,
                                                            aRv);
   }
 }
 
 /* static */ already_AddRefed<KeyframeEffectReadOnly>
 KeyframeEffectReadOnly::Constructor(
     const GlobalObject& aGlobal,
     const Nullable<ElementOrCSSPseudoElement>& aTarget,
     JS::Handle<JSObject*> aFrames,
     const TimingParams& aTiming,
     ErrorResult& aRv)
 {
-  if (aTarget.IsNull() || aTarget.Value().IsCSSPseudoElement()) {
-    // We don't support null or CSSPseudoElement targets yet.
+  if (aTarget.IsNull()) {
+    // We don't support null targets yet.
     aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
     return nullptr;
   }
 
-  Element& targetElement = aTarget.Value().GetAsElement();
-  if (!targetElement.GetCurrentDoc()) {
+  const ElementOrCSSPseudoElement& target = aTarget.Value();
+  MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
+             "Uninitialized target");
+
+  RefPtr<Element> targetElement;
+  nsCSSPseudoElements::Type pseudoType =
+    nsCSSPseudoElements::ePseudo_NotPseudoElement;
+  if (target.IsElement()) {
+    targetElement = &target.GetAsElement();
+  } else {
+    targetElement = target.GetAsCSSPseudoElement().ParentElement();
+    pseudoType = target.GetAsCSSPseudoElement().GetType();
+  }
+
+  if (!targetElement->GetCurrentDoc()) {
     // Bug 1245748: We don't support targets that are not in a document yet.
     aRv.Throw(NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR);
     return nullptr;
   }
 
   InfallibleTArray<AnimationProperty> animationProperties;
-  BuildAnimationPropertyList(aGlobal.Context(), &targetElement, aFrames,
-                             animationProperties, aRv);
+  BuildAnimationPropertyList(aGlobal.Context(), targetElement, pseudoType,
+                             aFrames, animationProperties, aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<KeyframeEffectReadOnly> effect =
-    new KeyframeEffectReadOnly(targetElement.OwnerDoc(), &targetElement,
-                               nsCSSPseudoElements::ePseudo_NotPseudoElement,
-                               aTiming);
+    new KeyframeEffectReadOnly(targetElement->OwnerDoc(), targetElement,
+                               pseudoType, aTiming);
   effect->mProperties = Move(animationProperties);
   return effect.forget();
 }
 
 void
 KeyframeEffectReadOnly::GetTarget(
     Nullable<OwningElementOrCSSPseudoElement>& aRv) const
 {
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -340,16 +340,17 @@ protected:
   // 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();
 
   static void BuildAnimationPropertyList(
     JSContext* aCx,
     Element* aTarget,
+    nsCSSPseudoElements::Type aPseudoType,
     JS::Handle<JSObject*> aFrames,
     InfallibleTArray<AnimationProperty>& aResult,
     ErrorResult& aRv);
 
   nsCOMPtr<Element> mTarget;
   RefPtr<Animation> mAnimation;
 
   OwningNonNull<AnimationEffectTimingReadOnly> mTiming;