mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1276688 part 2 - Add tests for entries array handling when ComputeValues fails; r=heycam, r=smaug
MozReview-Commit-ID: DIQyt7f91an
This commit is contained in:
parent
307d2b9f11
commit
7dbb4e5cf3
@ -372,6 +372,12 @@ MakePropertyValuePair(nsCSSProperty aProperty, const nsAString& aStringValue,
|
||||
static bool
|
||||
HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
|
||||
|
||||
static void
|
||||
MarkAsComputeValuesFailureKey(PropertyValuePair& aPair);
|
||||
|
||||
static bool
|
||||
IsComputeValuesFailureKey(const PropertyValuePair& aPair);
|
||||
|
||||
static void
|
||||
BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||
nsTArray<KeyframeValueEntry>& aEntries,
|
||||
@ -518,7 +524,8 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes(
|
||||
nsCSSValueTokenStream* tokenStream = pair.mValue.GetTokenStreamValue();
|
||||
if (!StyleAnimationValue::ComputeValues(pair.mProperty,
|
||||
CSSEnabledState::eForAllContent, aElement, aStyleContext,
|
||||
tokenStream->mTokenStream, /* aUseSVGMode */ false, values)) {
|
||||
tokenStream->mTokenStream, /* aUseSVGMode */ false, values) ||
|
||||
IsComputeValuesFailureKey(pair)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@ -677,6 +684,17 @@ ConvertKeyframeSequence(JSContext* aCx,
|
||||
MOZ_ASSERT(pair.mValues.Length() == 1);
|
||||
keyframe->mPropertyValues.AppendElement(
|
||||
MakePropertyValuePair(pair.mProperty, pair.mValues[0], parser, doc));
|
||||
|
||||
// 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.
|
||||
if (nsCSSProps::IsShorthand(pair.mProperty) &&
|
||||
keyframeDict.mSimulateComputeValuesFailure) {
|
||||
MarkAsComputeValuesFailureKey(keyframe->mPropertyValues.LastElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,6 +927,51 @@ HasValidOffsets(const nsTArray<Keyframe>& aKeyframes)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a property-value pair for a shorthand property and modifies the
|
||||
* value to indicate that when we call StyleAnimationValue::ComputeValues on
|
||||
* that value we should behave as if that function had failed.
|
||||
*
|
||||
* @param aPair The PropertyValuePair to modify. |aPair.mProperty| must be
|
||||
* a shorthand property.
|
||||
*/
|
||||
static void
|
||||
MarkAsComputeValuesFailureKey(PropertyValuePair& aPair)
|
||||
{
|
||||
MOZ_ASSERT(nsCSSProps::IsShorthand(aPair.mProperty),
|
||||
"Only shorthand property values can be marked as failure values");
|
||||
|
||||
// We store shorthand values as nsCSSValueTokenStream objects whose mProperty
|
||||
// and mShorthandPropertyID are eCSSProperty_UNKNOWN and whose mTokenStream
|
||||
// member contains the shorthand property's value as a string.
|
||||
//
|
||||
// We need to leave mShorthandPropertyID as eCSSProperty_UNKNOWN so that
|
||||
// nsCSSValue::AppendToString returns the mTokenStream value, but we can
|
||||
// update mPropertyID to a special value to indicate that this is
|
||||
// a special failure sentinel.
|
||||
nsCSSValueTokenStream* tokenStream = aPair.mValue.GetTokenStreamValue();
|
||||
MOZ_ASSERT(tokenStream->mPropertyID == eCSSProperty_UNKNOWN,
|
||||
"Shorthand value should initially have an unknown property ID");
|
||||
tokenStream->mPropertyID = eCSSPropertyExtra_no_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if |aPair| is a property-value pair on which we have
|
||||
* previously called MarkAsComputeValuesFailureKey (and hence we should
|
||||
* simulate failure when calling StyleAnimationValue::ComputeValues using its
|
||||
* value).
|
||||
*
|
||||
* @param aPair The property-value pair to test.
|
||||
* @return True if |aPair| represents a failure value.
|
||||
*/
|
||||
static bool
|
||||
IsComputeValuesFailureKey(const PropertyValuePair& aPair)
|
||||
{
|
||||
return nsCSSProps::IsShorthand(aPair.mProperty) &&
|
||||
aPair.mValue.GetTokenStreamValue()->mPropertyID ==
|
||||
eCSSPropertyExtra_no_properties;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsStyleContext>
|
||||
CreateStyleContextForAnimationValue(nsCSSProperty aProperty,
|
||||
StyleAnimationValue aValue,
|
||||
|
@ -505,6 +505,7 @@ var gTests = [
|
||||
// Tests for CSS variable handling conversion
|
||||
//
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
{ desc: 'CSS variables are resolved to their corresponding values',
|
||||
frames: { left: ['10px', 'var(--var-100px)'] },
|
||||
expected: [ { property: 'left',
|
||||
@ -533,6 +534,290 @@ var gTests = [
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '200px', 'replace') ] } ]
|
||||
},
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// Tests for properties that parse correctly but which we fail to
|
||||
// convert to computed values.
|
||||
//
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial keyframe',
|
||||
frames: [ { margin: '5px', simulateComputeValuesFailure: true },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial keyframe where we have enough values to create'
|
||||
+ ' a final segment',
|
||||
frames: [ { margin: '5px', simulateComputeValuesFailure: true },
|
||||
{ margin: '5px' },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial overlapping keyframes (first in series of two)',
|
||||
frames: [ { margin: '5px', offset: 0,
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', offset: 0 },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial overlapping keyframes (second in series of two)',
|
||||
frames: [ { margin: '5px', offset: 0 },
|
||||
{ margin: '5px', offset: 0,
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial overlapping keyframes (second in series of three)',
|
||||
frames: [ { margin: '5px', offset: 0 },
|
||||
{ margin: '5px', offset: 0,
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', offset: 0 },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace'),
|
||||
value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace'),
|
||||
value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace'),
|
||||
value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace'),
|
||||
value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final keyframe',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px', simulateComputeValuesFailure: true } ],
|
||||
expected: [ ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final keyframe where it forms the last segment in the series',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px',
|
||||
marginLeft: '5px',
|
||||
marginRight: '5px',
|
||||
marginBottom: '5px',
|
||||
// margin-top sorts last and only it will be missing since
|
||||
// the other longhand components are specified
|
||||
simulateComputeValuesFailure: true } ],
|
||||
expected: [ { property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final keyframe where we have enough values to create'
|
||||
+ ' an initial segment',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px' },
|
||||
{ margin: '5px', simulateComputeValuesFailure: true } ],
|
||||
expected: [ ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final overlapping keyframes (first in series of two)',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px', offset: 1,
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', offset: 1 } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final overlapping keyframes (second in series of two)',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px', offset: 1 },
|
||||
{ margin: '5px', offset: 1,
|
||||
simulateComputeValuesFailure: true } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final overlapping keyframes (second in series of three)',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px', offset: 1 },
|
||||
{ margin: '5px', offset: 1,
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', offset: 1 } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' intermediate keyframe',
|
||||
frames: [ { margin: '5px' },
|
||||
{ margin: '5px', simulateComputeValuesFailure: true },
|
||||
{ margin: '5px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial keyframe along with other values',
|
||||
// simulateComputeValuesFailure only applies to shorthands so we can set
|
||||
// it on the same keyframe and it will only apply to |margin| and not
|
||||
// |left|.
|
||||
frames: [ { margin: '77%', left: '10px',
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', left: '20px' } ],
|
||||
expected: [ { property: 'left',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '20px', 'replace') ] } ],
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' initial keyframe along with other values where those'
|
||||
+ ' values sort after the property with missing values',
|
||||
frames: [ { margin: '77%', right: '10px',
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', right: '20px' } ],
|
||||
expected: [ { property: 'right',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '20px', 'replace') ] } ],
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final keyframe along with other values',
|
||||
frames: [ { margin: '5px', left: '10px' },
|
||||
{ margin: '5px', left: '20px',
|
||||
simulateComputeValuesFailure: true } ],
|
||||
expected: [ { property: 'left',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '20px', 'replace') ] } ],
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' final keyframe along with other values where those'
|
||||
+ ' values sort after the property with missing values',
|
||||
frames: [ { margin: '5px', right: '10px' },
|
||||
{ margin: '5px', right: '20px',
|
||||
simulateComputeValuesFailure: true } ],
|
||||
expected: [ { property: 'right',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '20px', 'replace') ] } ],
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' an intermediate keyframe along with other values',
|
||||
frames: [ { margin: '5px', left: '10px' },
|
||||
{ margin: '5px', left: '20px',
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', left: '30px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'left',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(0.5, '20px', 'replace', 'linear'),
|
||||
value(1, '30px', 'replace') ] } ]
|
||||
},
|
||||
{ desc: 'a property that can\'t be resolved to computed values in'
|
||||
+ ' an intermediate keyframe by itself',
|
||||
frames: [ { margin: '5px', left: '10px' },
|
||||
{ margin: '5px',
|
||||
simulateComputeValuesFailure: true },
|
||||
{ margin: '5px', left: '30px' } ],
|
||||
expected: [ { property: 'margin-top',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-right',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-bottom',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'margin-left',
|
||||
values: [ value(0, '5px', 'replace', 'linear'),
|
||||
value(1, '5px', 'replace') ] },
|
||||
{ property: 'left',
|
||||
values: [ value(0, '10px', 'replace', 'linear'),
|
||||
value(1, '30px', 'replace') ] } ]
|
||||
},
|
||||
];
|
||||
|
||||
gTests.forEach(function(subtest) {
|
||||
|
@ -28,6 +28,19 @@ dictionary BaseKeyframe {
|
||||
double? offset = null;
|
||||
DOMString easing = "linear";
|
||||
CompositeOperation composite;
|
||||
|
||||
// Non-standard extensions
|
||||
|
||||
// Member to allow testing when StyleAnimationValue::ComputeValues fails.
|
||||
//
|
||||
// Note that we currently only apply this to shorthand properties since
|
||||
// it's easier to annotate shorthand property values and because we have
|
||||
// only ever observed ComputeValues failing on shorthand values.
|
||||
//
|
||||
// Bug 1216844 should remove this member since after that bug is fixed we will
|
||||
// have a well-defined behavior to use when animation endpoints are not
|
||||
// available.
|
||||
[ChromeOnly] boolean simulateComputeValuesFailure = false;
|
||||
};
|
||||
|
||||
dictionary BaseComputedKeyframe : BaseKeyframe {
|
||||
|
@ -1554,6 +1554,21 @@ private:
|
||||
nsCSSValueGradient& operator=(const nsCSSValueGradient& aOther) = delete;
|
||||
};
|
||||
|
||||
// A string value used primarily to represent variable references.
|
||||
//
|
||||
// Animation code, specifically the KeyframeUtils class, also uses this
|
||||
// type as a container for various string values including:
|
||||
//
|
||||
// * Shorthand property values
|
||||
// * Shorthand sentinel values used for testing failure conditions
|
||||
// * Invalid longhand property values
|
||||
//
|
||||
// For the most part, the above values are not passed to functions that
|
||||
// manipulate nsCSSValue objects in a generic fashion. Instead KeyframeUtils
|
||||
// extracts the string from the nsCSSValueTokenStream and passes that around
|
||||
// instead. The single exception is nsCSSValue::AppendToString which we use
|
||||
// to serialize the string contained in the nsCSSValueTokenStream by ensuring
|
||||
// the mShorthandPropertyID is set to eCSSProperty_UNKNOWN.
|
||||
struct nsCSSValueTokenStream final {
|
||||
nsCSSValueTokenStream();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user