Bug 1245748 - Remove no-longer-needed code for directly setting up properties in KeyframeEffect(ReadOnly) constructor; r=heycam

MozReview-Commit-ID: 4V3LMByas9Q

--HG--
extra : rebase_source : c2c687cd39b8287e7e9cb2a4edd12c86a056a0de
This commit is contained in:
Brian Birtles 2016-03-22 16:36:45 +09:00
Родитель 81c73a9b4b
Коммит 119a18fee3
2 изменённых файлов: 5 добавлений и 598 удалений

Просмотреть файл

@ -21,11 +21,6 @@
#include "nsTArray.h"
#include <algorithm> // For std::stable_sort
// TODO: Remove once we drop LookupStyleContext
#include "nsComputedDOMStyle.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
namespace mozilla {
// ------------------------------------------------------------------
@ -231,17 +226,6 @@ struct PropertyValuesPair
typedef TPropertyPriorityComparator<PropertyValuesPair> Comparator;
};
/**
* The result of parsing a JS object as a BaseKeyframe dictionary
* and getting its property-value pairs from its open-ended
* properties.
*/
struct OffsetIndexedKeyframe
{
dom::binding_detail::FastBaseKeyframe mKeyframeDict;
nsTArray<PropertyValuesPair> mPropertyValuePairs;
};
/**
* An additional property (for a property-values pair) found on a
* BaseKeyframe or BasePropertyIndexedKeyframe object.
@ -267,25 +251,17 @@ struct AdditionalProperty
};
};
/**
* A property and StyleAnimationValue pair.
*/
struct KeyframeValue
{
nsCSSProperty mProperty;
StyleAnimationValue mValue;
};
/**
* Data for a segment in a keyframe animation of a given property
* whose value is a StyleAnimationValue.
*
* KeyframeValueEntry is used in BuildAnimationPropertyListFromKeyframeSequence
* to gather data for each individual segment described by an author-supplied
* an IDL sequence<Keyframe> value so that they can be parsed into mProperties.
* KeyframeValueEntry is used in GetAnimationPropertiesFromKeyframes
* to gather data for each individual segment.
*/
struct KeyframeValueEntry : KeyframeValue
struct KeyframeValueEntry
{
nsCSSProperty mProperty;
StyleAnimationValue mValue;
float mOffset;
Maybe<ComputedTimingFunction> mTimingFunction;
@ -334,26 +310,12 @@ public:
//
// ------------------------------------------------------------------
static void
BuildAnimationPropertyListFromKeyframeSequence(
JSContext* aCx,
Element* aTarget,
CSSPseudoElementType aPseudoType,
JS::ForOfIterator& aIterator,
nsTArray<AnimationProperty>& aResult,
ErrorResult& aRv);
static void
GetKeyframeListFromKeyframeSequence(JSContext* aCx,
JS::ForOfIterator& aIterator,
nsTArray<Keyframe>& aResult,
ErrorResult& aRv);
static bool
ConvertKeyframeSequence(JSContext* aCx,
JS::ForOfIterator& aIterator,
nsTArray<OffsetIndexedKeyframe>& aResult);
static bool
ConvertKeyframeSequence(JSContext* aCx,
JS::ForOfIterator& aIterator,
@ -380,35 +342,13 @@ static PropertyValuePair
MakePropertyValuePair(nsCSSProperty aProperty, const nsAString& aStringValue,
nsCSSParser& aParser, nsIDocument* aDocument);
static bool
HasValidOffsets(const nsTArray<OffsetIndexedKeyframe>& aKeyframes);
static bool
HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
static void
ApplyDistributeSpacing(nsTArray<OffsetIndexedKeyframe>& aKeyframes);
static void
GenerateValueEntries(Element* aTarget,
CSSPseudoElementType aPseudoType,
nsTArray<OffsetIndexedKeyframe>& aKeyframes,
nsTArray<KeyframeValueEntry>& aResult,
ErrorResult& aRv);
static void
BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
nsTArray<AnimationProperty>& aResult);
static void
BuildAnimationPropertyListFromPropertyIndexedKeyframes(
JSContext* aCx,
Element* aTarget,
CSSPseudoElementType aPseudoType,
JS::Handle<JS::Value> aValue,
InfallibleTArray<AnimationProperty>& aResult,
ErrorResult& aRv);
static void
GetKeyframeListFromPropertyIndexedKeyframe(JSContext* aCx,
JS::Handle<JS::Value> aValue,
@ -420,64 +360,12 @@ RequiresAdditiveAnimation(const nsTArray<Keyframe>& aKeyframes,
nsIDocument* aDocument);
// TODO: This is only temporary until we remove the call sites for this.
already_AddRefed<nsStyleContext>
LookupStyleContext(dom::Element* aElement, CSSPseudoElementType aPseudoType);
// ------------------------------------------------------------------
//
// Public API
//
// ------------------------------------------------------------------
/* static */ void
KeyframeUtils::BuildAnimationPropertyList(
JSContext* aCx,
Element* aTarget,
CSSPseudoElementType aPseudoType,
JS::Handle<JSObject*> aFrames,
InfallibleTArray<AnimationProperty>& aResult,
ErrorResult& aRv)
{
MOZ_ASSERT(aResult.IsEmpty());
// See the description of frame lists in the spec:
//
// https://w3c.github.io/web-animations/#processing-a-frames-argument
//
// We don't support SharedKeyframeList yet, but we do support the other
// types of arguments. We manually implement the parts of JS-to-IDL union
// conversion algorithm from the Web IDL spec, since we have to represent
// this as an object? so we can look at the open-ended set of properties
// on these objects.
if (!aFrames) {
// The argument was explicitly null. In this case, the default dictionary
// value for PropertyIndexedKeyframe would result in no keyframes.
return;
}
// At this point we know we have an object. We try to convert it to a
// sequence<Keyframe> first, and if that fails due to not being iterable,
// we try to convert it to PropertyIndexedKeyframe.
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, aPseudoType,
iter, aResult, aRv);
} else {
BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget,
aPseudoType,
objectValue, aResult,
aRv);
}
}
/* static */ nsTArray<Keyframe>
KeyframeUtils::GetKeyframesFromObject(JSContext* aCx,
JS::Handle<JSObject*> aFrames,
@ -660,65 +548,6 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes(
//
// ------------------------------------------------------------------
/**
* Converts a JS object to an IDL sequence<Keyframe> and builds an
* array of AnimationProperty objects for the keyframe animation
* that it specifies.
*
* @param aTarget The target of the animation.
* @param aIterator An already-initialized ForOfIterator for the JS
* 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,
CSSPseudoElementType 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)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// If the sequence<> had zero elements, we won't generate any
// keyframes.
if (keyframes.IsEmpty()) {
return;
}
// Check that the keyframes are loosely sorted and with values all
// between 0% and 100%.
if (!HasValidOffsets(keyframes)) {
aRv.ThrowTypeError<dom::MSG_INVALID_KEYFRAME_OFFSETS>();
return;
}
// 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, aPseudoType, keyframes, entries, aRv);
if (aRv.Failed()) {
return;
}
// Finally, build an array of AnimationProperty objects in aResult
// corresponding to the entries.
BuildSegmentsFromValueEntries(entries, aResult);
}
/**
* Converts a JS object to an IDL sequence<Keyframe>.
*
@ -761,52 +590,6 @@ GetKeyframeListFromKeyframeSequence(JSContext* aCx,
}
}
/**
* Converts a JS object wrapped by the given JS::ForIfIterator to an
* IDL sequence<Keyframe> and stores the resulting OffsetIndexedKeyframe
* objects in aResult.
*/
static bool
ConvertKeyframeSequence(JSContext* aCx,
JS::ForOfIterator& aIterator,
nsTArray<OffsetIndexedKeyframe>& aResult)
{
JS::Rooted<JS::Value> value(aCx);
for (;;) {
bool done;
if (!aIterator.next(&value, &done)) {
return false;
}
if (done) {
break;
}
// Each value found when iterating the object must be an object
// or null/undefined (which gets treated as a default {} dictionary
// value).
if (!value.isObject() && !value.isNullOrUndefined()) {
dom::ThrowErrorMessage(aCx, dom::MSG_NOT_OBJECT,
"Element of sequence<Keyframes> argument");
return false;
}
// Convert the JS value into a BaseKeyframe dictionary value.
OffsetIndexedKeyframe* keyframe = aResult.AppendElement();
if (!keyframe->mKeyframeDict.Init(
aCx, value, "Element of sequence<Keyframes> argument")) {
return false;
}
// Look for additional property-values pairs on the object.
if (value.isObject()) {
JS::Rooted<JSObject*> object(aCx, &value.toObject());
if (!GetPropertyValuesPairs(aCx, object,
ListAllowance::eDisallow,
keyframe->mPropertyValuePairs)) {
return false;
}
}
}
return true;
}
/**
* Converts a JS object wrapped by the given JS::ForIfIterator to an
* IDL sequence<Keyframe> and stores the resulting Keyframe objects in
@ -1058,30 +841,6 @@ MakePropertyValuePair(nsCSSProperty aProperty, const nsAString& aStringValue,
return { aProperty, value };
}
/**
* Checks that the given keyframes are loosely ordered (each keyframe's
* offset that is not null is greater than or equal to the previous
* non-null offset) and that all values are within the range [0.0, 1.0].
*
* @return true if the keyframes' offsets are correctly ordered and
* within range; false otherwise.
*/
static bool
HasValidOffsets(const nsTArray<OffsetIndexedKeyframe>& aKeyframes)
{
double offset = 0.0;
for (const OffsetIndexedKeyframe& keyframe : aKeyframes) {
if (!keyframe.mKeyframeDict.mOffset.IsNull()) {
double thisOffset = keyframe.mKeyframeDict.mOffset.Value();
if (thisOffset < offset || thisOffset > 1.0f) {
return false;
}
offset = thisOffset;
}
}
return true;
}
/**
* Checks that the given keyframes are loosely ordered (each keyframe's
* offset that is not null is greater than or equal to the previous
@ -1106,147 +865,6 @@ HasValidOffsets(const nsTArray<Keyframe>& aKeyframes)
return true;
}
/**
* Fills in any null offsets for the given keyframes by applying the
* "distribute" spacing algorithm.
*
* http://w3c.github.io/web-animations/#distribute-keyframe-spacing-mode
*/
static void
ApplyDistributeSpacing(nsTArray<OffsetIndexedKeyframe>& aKeyframes)
{
// If the first or last keyframes have an unspecified offset,
// fill them in with 0% and 100%. If there is only a single keyframe,
// then it gets 100%.
if (aKeyframes.LastElement().mKeyframeDict.mOffset.IsNull()) {
aKeyframes.LastElement().mKeyframeDict.mOffset.SetValue(1.0);
}
if (aKeyframes[0].mKeyframeDict.mOffset.IsNull()) {
aKeyframes[0].mKeyframeDict.mOffset.SetValue(0.0);
}
// Fill in remaining missing offsets.
size_t i = 0;
while (i < aKeyframes.Length() - 1) {
MOZ_ASSERT(!aKeyframes[i].mKeyframeDict.mOffset.IsNull());
double start = aKeyframes[i].mKeyframeDict.mOffset.Value();
size_t j = i + 1;
while (aKeyframes[j].mKeyframeDict.mOffset.IsNull()) {
++j;
}
double end = aKeyframes[j].mKeyframeDict.mOffset.Value();
size_t n = j - i;
for (size_t k = 1; k < n; ++k) {
double offset = start + double(k) / n * (end - start);
aKeyframes[i + k].mKeyframeDict.mOffset.SetValue(offset);
}
i = j;
}
}
/**
* 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,
CSSPseudoElementType aPseudoType,
nsTArray<OffsetIndexedKeyframe>& aKeyframes,
nsTArray<KeyframeValueEntry>& aResult,
ErrorResult& aRv)
{
RefPtr<nsStyleContext> styleContext =
LookupStyleContext(aTarget, aPseudoType);
if (!styleContext) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
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) {
Maybe<ComputedTimingFunction> easing =
TimingParams::ParseEasing(keyframe.mKeyframeDict.mEasing,
aTarget->OwnerDoc(), aRv);
if (aRv.Failed()) {
return;
}
float offset = float(keyframe.mKeyframeDict.mOffset.Value());
// 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
// shorthands, and with shorthands all appearing in increasing order of
// number of components. For two longhand properties, or two shorthands
// with the same number of components, sort by IDL name.
//
// @see PropertyPriorityComparator.
keyframe.mPropertyValuePairs.Sort(PropertyValuesPair::Comparator());
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,
styleContext,
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();
entry->mOffset = offset;
entry->mProperty = value.mProperty;
entry->mValue = value.mValue;
entry->mTimingFunction = easing;
if (offset == 0.0) {
propertiesWithFromValue.AddProperty(value.mProperty);
} else if (offset == 1.0) {
propertiesWithToValue.AddProperty(value.mProperty);
}
propertiesOnThisKeyframe.AddProperty(value.mProperty);
properties.AddProperty(value.mProperty);
}
}
}
}
// We don't support additive segments and so can't support missing properties
// using their underlying value in 0% and 100% keyframes. Throw an exception
// until we do support this.
if (!propertiesWithFromValue.Equals(properties) ||
!propertiesWithToValue.Equals(properties)) {
aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
return;
}
}
/**
* Builds an array of AnimationProperty objects to represent the keyframe
* animation segments in aEntries.
@ -1347,183 +965,6 @@ BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
}
}
/**
* Converts a JS object to an IDL PropertyIndexedKeyframe and builds an
* array of AnimationProperty objects for the keyframe animation
* that it specifies.
*
* @param aTarget The target of the animation.
* @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,
CSSPseudoElementType aPseudoType,
JS::Handle<JS::Value> aValue,
InfallibleTArray<AnimationProperty>& aResult,
ErrorResult& aRv)
{
MOZ_ASSERT(aValue.isObject());
RefPtr<nsStyleContext> styleContext =
LookupStyleContext(aTarget, aPseudoType);
if (!styleContext) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// Convert the object to a PropertyIndexedKeyframe dictionary to
// get its explicit dictionary members.
dom::binding_detail::FastBasePropertyIndexedKeyframe keyframes;
if (!keyframes.Init(aCx, aValue, "BasePropertyIndexedKeyframe argument",
false)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
Maybe<ComputedTimingFunction> easing =
TimingParams::ParseEasing(keyframes.mEasing, aTarget->OwnerDoc(), aRv);
// 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());
nsTArray<PropertyValuesPair> propertyValuesPairs;
if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow,
propertyValuesPairs)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
// We must keep track of which properties we've already generated
// an AnimationProperty since the author could have specified both a
// shorthand and one of its component longhands on the
// PropertyIndexedKeyframe.
nsCSSPropertySet properties;
// Create AnimationProperty objects for each PropertyValuesPair, applying
// the "distribute" spacing algorithm to the segments.
for (const PropertyValuesPair& pair : propertyValuesPairs) {
size_t count = pair.mValues.Length();
if (count == 0) {
// No animation values for this property.
continue;
}
if (count == 1) {
// We don't support additive segments and so can't support an
// animation that goes from the underlying value to this
// specified value. Throw an exception until we do support this.
aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR);
return;
}
// If we find an invalid value, we don't create a segment for it, but
// we adjust the surrounding segments so that the timing of the segments
// is the same as if we did support it. For example, animating with
// values ["red", "green", "yellow", "invalid", "blue"] will generate
// segments with this timing:
//
// 0.00 -> 0.25 : red -> green
// 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,
styleContext,
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.
continue;
}
// Create AnimationProperty objects for each property that had a
// value computed. When pair.mProperty is a longhand, it is just
// that property. When pair.mProperty is a shorthand, we'll have
// one property per longhand component.
nsTArray<size_t> animationPropertyIndexes;
animationPropertyIndexes.SetLength(fromValues.Length());
for (size_t i = 0, n = fromValues.Length(); i < n; ++i) {
nsCSSProperty p = fromValues[i].mProperty;
bool found = false;
if (properties.HasProperty(p)) {
// We have already dealt with this property. Look up and
// overwrite the old AnimationProperty object.
for (size_t j = 0, m = aResult.Length(); j < m; ++j) {
if (aResult[j].mProperty == p) {
aResult[j].mSegments.Clear();
animationPropertyIndexes[i] = j;
found = true;
break;
}
}
MOZ_ASSERT(found, "properties is inconsistent with aResult");
}
if (!found) {
// This is the first time we've encountered this property.
animationPropertyIndexes[i] = aResult.Length();
AnimationProperty* animationProperty = aResult.AppendElement();
animationProperty->mProperty = p;
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,
styleContext,
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;
}
MOZ_ASSERT(toValues.Length() == fromValues.Length(),
"should get the same number of properties as the last time "
"we called ComputeValues for pair.mProperty");
for (size_t j = 0, n = toValues.Length(); j < n; ++j) {
size_t index = animationPropertyIndexes[j];
AnimationPropertySegment* segment =
aResult[index].mSegments.AppendElement();
segment->mFromKey = fromKey;
segment->mFromValue = fromValues[j].mValue;
segment->mToKey = toKey;
segment->mToValue = toValues[j].mValue;
segment->mTimingFunction = easing;
}
fromValues = Move(toValues);
fromKey = toKey;
}
}
}
/**
* Converts a JS object representing a property-indexed keyframe into
* an array of Keyframe objects.
@ -1695,19 +1136,4 @@ RequiresAdditiveAnimation(const nsTArray<Keyframe>& aKeyframes,
!propertiesWithToValue.Equals(properties);
}
already_AddRefed<nsStyleContext>
LookupStyleContext(dom::Element* aElement, CSSPseudoElementType aPseudoType)
{
nsIDocument* doc = aElement->GetCurrentDoc();
nsIPresShell* shell = doc->GetShell();
if (!shell) {
return nullptr;
}
nsIAtom* pseudo =
aPseudoType < CSSPseudoElementType::Count ?
nsCSSPseudoElements::GetPseudoAtom(aPseudoType) : nullptr;
return nsComputedDOMStyle::GetStyleContextForElement(aElement, pseudo, shell);
}
} // namespace mozilla

Просмотреть файл

@ -33,25 +33,6 @@ namespace mozilla {
class KeyframeUtils
{
public:
/**
* Converts a JS value to a property-indexed keyframe or a sequence of
* regular keyframes and builds an array of AnimationProperty objects for the
* keyframe animation that it specifies.
*
* @param aTarget The target of the animation, used to resolve style
* for a property's underlying value if needed.
* @param aFrames The JS value, provided as an optional IDL |object?| value,
* that is the keyframe list specification.
* @param aResult The array into which the resulting AnimationProperty
* objects will be appended.
*/
static void
BuildAnimationPropertyList(JSContext* aCx, Element* aTarget,
CSSPseudoElementType aPseudoType,
JS::Handle<JSObject*> aFrames,
InfallibleTArray<AnimationProperty>& aResult,
ErrorResult& aRv);
/**
* Converts a JS value representing a property-indexed keyframe or a sequence
* of keyframes to an array of Keyframe objects.