--- a/dom/animation/Keyframe.h
+++ b/dom/animation/Keyframe.h
@@ -20,29 +20,38 @@ namespace dom {
enum class CompositeOperation : uint8_t;
}
/**
* A property-value pair specified on a keyframe.
*/
struct PropertyValuePair
{
+ PropertyValuePair(nsCSSPropertyID aProperty, nsCSSValue&& aValue)
+ : mProperty(aProperty), mValue(Move(aValue)) { }
+ PropertyValuePair(nsCSSPropertyID aProperty,
+ RefPtr<RawServoDeclarationBlock>&& aValue)
+ : mProperty(aProperty), mServoDeclarationBlock(Move(aValue))
+ {
+ MOZ_ASSERT(mServoDeclarationBlock, "Should be valid property value");
+ }
+
nsCSSPropertyID mProperty;
- // The specified value for the property. For shorthand properties or invalid
- // property values, we store the specified property value as a token stream
- // (string).
+ // The specified value for the property. For shorthand property values,
+ // we store the specified property value as a token stream (string).
+ // If this is uninitialized, we use the underlying value.
nsCSSValue mValue;
// The specified value when using the Servo backend.
RefPtr<RawServoDeclarationBlock> mServoDeclarationBlock;
#ifdef DEBUG
// Flag to indicate that when we call StyleAnimationValue::ComputeValues on
// this value we should behave as if that function had failed.
- bool mSimulateComputeValuesFailure;
+ bool mSimulateComputeValuesFailure = false;
#endif
bool operator==(const PropertyValuePair&) const;
};
/**
* A single keyframe.
*
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -309,45 +309,16 @@ public:
static bool LessThan(const Keyframe& aLhs, const Keyframe& aRhs)
{
return aLhs.mComputedOffset < aRhs.mComputedOffset;
}
};
// ------------------------------------------------------------------
//
-// Inlined helper methods
-//
-// ------------------------------------------------------------------
-
-inline bool
-IsInvalidValuePair(const PropertyValuePair& aPair, StyleBackendType aBackend)
-{
- if (aBackend == StyleBackendType::Servo) {
- return !aPair.mServoDeclarationBlock;
- }
-
- // There are three types of values we store as token streams:
- //
- // * Shorthand values (where we manually extract the token stream's string
- // value) and pass that along to various parsing methods
- // * Longhand values with variable references
- // * Invalid values
- //
- // We can distinguish between the last two cases because for invalid values
- // we leave the token stream's mPropertyID as eCSSProperty_UNKNOWN.
- return !nsCSSProps::IsShorthand(aPair.mProperty) &&
- aPair.mValue.GetUnit() == eCSSUnit_TokenStream &&
- aPair.mValue.GetTokenStreamValue()->mPropertyID
- == eCSSProperty_UNKNOWN;
-}
-
-
-// ------------------------------------------------------------------
-//
// Internal helper method declarations
//
// ------------------------------------------------------------------
static void
GetKeyframeListFromKeyframeSequence(JSContext* aCx,
nsIDocument* aDocument,
JS::ForOfIterator& aIterator,
@@ -373,17 +344,17 @@ AppendStringOrStringSequenceToArray(JSCo
ListAllowance aAllowLists,
nsTArray<nsString>& aValues);
static bool
AppendValueAsString(JSContext* aCx,
nsTArray<nsString>& aValues,
JS::Handle<JS::Value> aValue);
-static PropertyValuePair
+static Maybe<PropertyValuePair>
MakePropertyValuePair(nsCSSPropertyID aProperty, const nsAString& aStringValue,
nsCSSParser& aParser, nsIDocument* aDocument);
static bool
HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
#ifdef DEBUG
static void
@@ -714,19 +685,24 @@ ConvertKeyframeSequence(JSContext* aCx,
aDocument->GetStyleBackendType(),
propertyValuePairs)) {
return false;
}
}
for (PropertyValuesPair& pair : propertyValuePairs) {
MOZ_ASSERT(pair.mValues.Length() == 1);
- keyframe->mPropertyValues.AppendElement(
+
+ Maybe<PropertyValuePair> valuePair =
MakePropertyValuePair(pair.mProperty, pair.mValues[0], parser,
- aDocument));
+ aDocument);
+ if (!valuePair) {
+ continue;
+ }
+ keyframe->mPropertyValues.AppendElement(Move(valuePair.ref()));
#ifdef DEBUG
// When we go to convert keyframes into arrays of property values we
// call StyleAnimation::ComputeValues. This should normally return true
// but in order to test the case where it does not, BaseKeyframeDict
// includes a chrome-only member that can be set to indicate that
// ComputeValues should fail for shorthand property values on that
// keyframe.
@@ -872,76 +848,73 @@ AppendValueAsString(JSContext* aCx,
/**
* Construct a PropertyValuePair parsing the given string into a suitable
* nsCSSValue object.
*
* @param aProperty The CSS property.
* @param aStringValue The property value to parse.
* @param aParser The CSS parser object to use.
* @param aDocument The document to use when parsing.
- * @return The constructed PropertyValuePair object.
+ * @return The constructed PropertyValuePair, or Nothing() if |aStringValue| is
+ * an invalid property value.
*/
-static PropertyValuePair
+static Maybe<PropertyValuePair>
MakePropertyValuePair(nsCSSPropertyID aProperty, const nsAString& aStringValue,
nsCSSParser& aParser, nsIDocument* aDocument)
{
MOZ_ASSERT(aDocument);
- PropertyValuePair result;
-
- result.mProperty = aProperty;
-
-#ifdef DEBUG
- result.mSimulateComputeValuesFailure = false;
-#endif
+ Maybe<PropertyValuePair> result;
if (aDocument->GetStyleBackendType() == StyleBackendType::Servo) {
RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
KeyframeUtils::ParseProperty(aProperty, aStringValue, aDocument);
if (servoDeclarationBlock) {
- result.mServoDeclarationBlock = servoDeclarationBlock.forget();
+ result.emplace(aProperty, Move(servoDeclarationBlock));
}
return result;
}
nsCSSValue value;
if (!nsCSSProps::IsShorthand(aProperty)) {
aParser.ParseLonghandProperty(aProperty,
aStringValue,
aDocument->GetDocumentURI(),
aDocument->GetDocumentURI(),
aDocument->NodePrincipal(),
value);
+ if (value.GetUnit() == eCSSUnit_Null) {
+ // Invalid property value, so return Nothing.
+ return result;
+ }
}
if (value.GetUnit() == eCSSUnit_Null) {
- // Either we have a shorthand, or we failed to parse a longhand.
- // In either case, store the string value as a token stream.
+ // If we have a shorthand, store the string value as a token stream.
nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
tokenStream->mTokenStream = aStringValue;
// We are about to convert a null value to a token stream value but
// by leaving the mPropertyID as unknown, we will be able to
- // distinguish between invalid values and valid token stream values
+ // distinguish between shorthand values and valid token stream values
// (e.g. values with variable references).
MOZ_ASSERT(tokenStream->mPropertyID == eCSSProperty_UNKNOWN,
"The property of a token stream should be initialized"
" to unknown");
// By leaving mShorthandPropertyID as unknown, we ensure that when
// we call nsCSSValue::AppendToString we get back the string stored
// in mTokenStream.
MOZ_ASSERT(tokenStream->mShorthandPropertyID == eCSSProperty_UNKNOWN,
"The shorthand property of a token stream should be initialized"
" to unknown");
value.SetTokenStreamValue(tokenStream);
}
- result.mValue = value;
-
+ result.emplace(aProperty, Move(value));
return result;
}
/**
* 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].
*
@@ -1029,20 +1002,16 @@ GetComputedKeyframeValues(const nsTArray
nsCSSPropertyIDSet propertiesOnThisKeyframe;
ComputedKeyframeValues* computedValues = result.AppendElement();
for (const PropertyValuePair& pair :
PropertyPriorityIterator(frame.mPropertyValues)) {
MOZ_ASSERT(!pair.mServoDeclarationBlock,
"Animation values were parsed using Servo backend but target"
" element is not using Servo backend?");
- if (IsInvalidValuePair(pair, StyleBackendType::Gecko)) {
- continue;
- }
-
// Expand each value into the set of longhands and produce
// a KeyframeValueEntry for each value.
nsTArray<PropertyStyleAnimationValuePair> values;
// For shorthands, we store the string as a token stream so we need to
// extract that first.
if (nsCSSProps::IsShorthand(pair.mProperty)) {
nsCSSValueTokenStream* tokenStream = pair.mValue.GetTokenStreamValue();
@@ -1438,18 +1407,23 @@ GetKeyframeListFromPropertyIndexedKeyfra
// keyframe with offset 1.
double offset = n ? i++ / double(n) : 1;
Keyframe* keyframe = processedKeyframes.LookupOrAdd(offset);
if (keyframe->mPropertyValues.IsEmpty()) {
keyframe->mTimingFunction = easing;
keyframe->mComposite = composite;
keyframe->mComputedOffset = offset;
}
- keyframe->mPropertyValues.AppendElement(
- MakePropertyValuePair(pair.mProperty, stringValue, parser, aDocument));
+
+ Maybe<PropertyValuePair> valuePair =
+ MakePropertyValuePair(pair.mProperty, stringValue, parser, aDocument);
+ if (!valuePair) {
+ continue;
+ }
+ keyframe->mPropertyValues.AppendElement(Move(valuePair.ref()));
}
}
aResult.SetCapacity(processedKeyframes.Count());
for (auto iter = processedKeyframes.Iter(); !iter.Done(); iter.Next()) {
aResult.AppendElement(Move(*iter.UserData()));
}
@@ -1509,33 +1483,27 @@ RequiresAdditiveAnimation(const nsTArray
double computedOffset = i == len - 1
? 1.0
: i == 0 ? 0.0 : 0.5;
double offsetToUse = frame.mOffset
? frame.mOffset.value()
: computedOffset;
for (const PropertyValuePair& pair : frame.mPropertyValues) {
- if (IsInvalidValuePair(pair, styleBackend)) {
- continue;
- }
-
if (nsCSSProps::IsShorthand(pair.mProperty)) {
if (styleBackend == StyleBackendType::Gecko) {
nsCSSValueTokenStream* tokenStream =
pair.mValue.GetTokenStreamValue();
nsCSSParser parser(aDocument->CSSLoader());
if (!parser.IsValueValidForProperty(pair.mProperty,
tokenStream->mTokenStream)) {
continue;
}
}
- // For the Servo backend, invalid shorthand values are represented by
- // a null mServoDeclarationBlock member which we skip above in
- // IsInvalidValuePair.
+
MOZ_ASSERT(styleBackend != StyleBackendType::Servo ||
pair.mServoDeclarationBlock);
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(
prop, pair.mProperty, CSSEnabledState::eForAllContent) {
addToPropertySets(*prop, offsetToUse);
}
} else {
addToPropertySets(pair.mProperty, offsetToUse);
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -851,33 +851,32 @@ GeckoCSSAnimationBuilder::GetKeyframePro
for (nsCSSPropertyID prop = nsCSSPropertyID(0);
prop < eCSSProperty_COUNT_no_shorthands;
prop = nsCSSPropertyID(prop + 1)) {
if (nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None ||
!aKeyframeRule->Declaration()->HasNonImportantValueFor(prop)) {
continue;
}
- PropertyValuePair pair;
- pair.mProperty = prop;
-
StyleAnimationValue computedValue;
if (!StyleAnimationValue::ExtractComputedValue(prop, styleContext,
computedValue)) {
continue;
}
+
+ nsCSSValue propertyValue;
DebugOnly<bool> uncomputeResult =
StyleAnimationValue::UncomputeValue(prop, Move(computedValue),
- pair.mValue);
+ propertyValue);
MOZ_ASSERT(uncomputeResult,
"Unable to get specified value from computed value");
- MOZ_ASSERT(pair.mValue.GetUnit() != eCSSUnit_Null,
+ MOZ_ASSERT(propertyValue.GetUnit() != eCSSUnit_Null,
"Not expecting to read invalid properties");
- result.AppendElement(Move(pair));
+ result.AppendElement(Move(PropertyValuePair(prop, Move(propertyValue))));
aAnimatedProperties.AddProperty(prop);
}
return result;
}
void
GeckoCSSAnimationBuilder::FillInMissingKeyframeValues(
@@ -936,23 +935,23 @@ GeckoCSSAnimationBuilder::FillInMissingK
for (nsCSSPropertyID prop = nsCSSPropertyID(0);
prop < eCSSProperty_COUNT_no_shorthands;
prop = nsCSSPropertyID(prop + 1)) {
if (!aAnimatedProperties.HasProperty(prop)) {
continue;
}
if (startKeyframe && !aPropertiesSetAtStart.HasProperty(prop)) {
- PropertyValuePair propertyValue;
- propertyValue.mProperty = prop;
+ // An uninitialized nsCSSValue represents the underlying value.
+ PropertyValuePair propertyValue(prop, Move(nsCSSValue()));
startKeyframe->mPropertyValues.AppendElement(Move(propertyValue));
}
if (endKeyframe && !aPropertiesSetAtEnd.HasProperty(prop)) {
- PropertyValuePair propertyValue;
- propertyValue.mProperty = prop;
+ // An uninitialized nsCSSValue represents the underlying value.
+ PropertyValuePair propertyValue(prop, Move(nsCSSValue()));
endKeyframe->mPropertyValues.AppendElement(Move(propertyValue));
}
}
}
template<class BuilderType>
static nsAnimationManager::OwningCSSAnimationPtrArray
BuildAnimations(nsPresContext* aPresContext,
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -786,28 +786,31 @@ nsTransitionManager::DoUpdateTransitions
static Keyframe&
AppendKeyframe(double aOffset,
nsCSSPropertyID aProperty,
AnimationValue&& aValue,
nsTArray<Keyframe>& aKeyframes)
{
Keyframe& frame = *aKeyframes.AppendElement();
frame.mOffset.emplace(aOffset);
- PropertyValuePair& pv = *frame.mPropertyValues.AppendElement();
- pv.mProperty = aProperty;
if (aValue.mServo) {
- pv.mServoDeclarationBlock =
+ RefPtr<RawServoDeclarationBlock> decl =
Servo_AnimationValue_Uncompute(aValue.mServo).Consume();
+ frame.mPropertyValues.AppendElement(
+ Move(PropertyValuePair(aProperty, Move(decl))));
} else {
+ nsCSSValue propertyValue;
DebugOnly<bool> uncomputeResult =
StyleAnimationValue::UncomputeValue(aProperty, Move(aValue.mGecko),
- pv.mValue);
+ propertyValue);
MOZ_ASSERT(uncomputeResult,
"Unable to get specified value from computed value");
+ frame.mPropertyValues.AppendElement(
+ Move(PropertyValuePair(aProperty, Move(propertyValue))));
}
return frame;
}
static nsTArray<Keyframe>
GetTransitionKeyframes(nsCSSPropertyID aProperty,
AnimationValue&& aStartValue,
AnimationValue&& aEndValue,
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -220150,17 +220150,17 @@
"c9dcf7c17010e5495007e000b33aeb4dc89f92b7",
"testharness"
],
"web-animations/interfaces/KeyframeEffect/iterationComposite.html": [
"2ed50cdb27335345015d8b13c64ef86c67048757",
"testharness"
],
"web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument.html": [
- "6f561694e38420194fd1817ee965436348221d06",
+ "9f828bd1b3785d240e9a99df8c3826dd489a224e",
"testharness"
],
"web-animations/interfaces/KeyframeEffect/setKeyframes.html": [
"2982bb6f57bb52c6e4e0483e4e47b22868a6010d",
"testharness"
],
"web-animations/interfaces/KeyframeEffect/setTarget.html": [
"8c75e6605a134c96e261e5383414b7e15b32d121",
@@ -220170,17 +220170,17 @@
"8ef986f13e7fe7ffeb7403f647b4169ac0d6a138",
"testharness"
],
"web-animations/resources/easing-tests.js": [
"5ef1183a4d3e12ad3edfe678c9fa002e7edce888",
"support"
],
"web-animations/resources/keyframe-utils.js": [
- "ff5700466b5af6ffaad824437d6566003a22e25b",
+ "2903e56e508f7034e1918b5688c8a605db9bae51",
"support"
],
"web-animations/testcommon.js": [
"d057ad66c4561ef32f83770e4948f2019da89d48",
"support"
],
"web-animations/timing-model/animation-effects/active-time.html": [
"42eb1a23e89ae60ccd0a3664a9a583df1eb30d49",
--- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument.html
+++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument.html
@@ -253,19 +253,20 @@ test(() => {
}, 'Custom iterator with non object keyframe should throw.');
test(() => {
var effect = new KeyframeEffect(null, createIterable([
{done: false, value: {left: ['100px', '200px']}},
{done: true},
]));
assert_frame_lists_equal(effect.getKeyframes(), [
- {offset: null, computedOffset: 1, easing: 'linear', left: '100px,200px'}
+ {offset: null, computedOffset: 1, easing: 'linear'}
]);
-}, 'Custom iterator with value list in keyframe should give bizarre string representation of list.');
+}, 'Custom iterator with value list in keyframe should not contain invalid ' +
+ 'property value pair of list.');
test(function(t) {
var keyframe = {};
Object.defineProperty(keyframe, 'width', {value: '200px'});
Object.defineProperty(keyframe, 'height', {
value: '100px',
enumerable: true});
assert_equals(keyframe.width, '200px', 'width of keyframe is readable');
--- a/testing/web-platform/tests/web-animations/resources/keyframe-utils.js
+++ b/testing/web-platform/tests/web-animations/resources/keyframe-utils.js
@@ -123,17 +123,17 @@ var gPropertyIndexedKeyframesTests = [
{ desc: "a property-indexed keyframes specification with an invalid value",
input: { left: ["10px", "20px", "30px", "40px", "50px"],
top: ["15px", "25px", "invalid", "45px", "55px"] },
output: [{ offset: null, computedOffset: 0.00, easing: "linear",
left: "10px", top: "15px" },
{ offset: null, computedOffset: 0.25, easing: "linear",
left: "20px", top: "25px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
- left: "30px", top: "invalid" },
+ left: "30px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px", top: "45px" },
{ offset: null, computedOffset: 1.00, easing: "linear",
left: "50px", top: "55px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " that needs to stringify its values",
input: { opacity: [0, 1] },
output: [{ offset: null, computedOffset: 0, easing: "linear",
@@ -161,27 +161,25 @@ var gPropertyIndexedKeyframesTests = [
{ desc: "a one property one non-array value property-indexed keyframes"
+ " specification",
input: { left: "10px" },
output: [{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the first value is invalid",
input: { left: ["invalid", "10px"] },
- output: [{ offset: null, computedOffset: 0, easing: "linear",
- left: "invalid" },
+ output: [{ offset: null, computedOffset: 0, easing: "linear" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the second value is invalid",
input: { left: ["10px", "invalid"] },
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px" },
- { offset: null, computedOffset: 1, easing: "linear",
- left: "invalid" }] },
+ { offset: null, computedOffset: 1, easing: "linear" }] },
];
var gKeyframeSequenceTests = [
{ desc: "a one property one keyframe sequence",
input: [{ offset: 1, left: "10px" }],
output: [{ offset: 1, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two keyframe sequence",