--- a/dom/animation/ComputedTimingFunction.cpp
+++ b/dom/animation/ComputedTimingFunction.cpp
@@ -167,17 +167,17 @@ ComputedTimingFunction::AppendToString(n
mTimingFunction.Y2(),
aResult);
break;
case nsTimingFunction::Type::StepStart:
case nsTimingFunction::Type::StepEnd:
nsStyleUtil::AppendStepsTimingFunction(mType, mStepsOrFrames, aResult);
break;
case nsTimingFunction::Type::Frames:
- // TODO: Serialize Frames timing function.
+ nsStyleUtil::AppendFramesTimingFunction(mStepsOrFrames, aResult);
break;
default:
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult);
break;
}
}
/* static */ int32_t
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -133,16 +133,17 @@ TimingParams::ParseEasing(const nsAStrin
case eCSSUnit_Enumerated:
// Return Nothing() if "linear" is passed in.
if (list->mValue.GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) {
return Nothing();
}
MOZ_FALLTHROUGH;
case eCSSUnit_Cubic_Bezier:
+ case eCSSUnit_Function:
case eCSSUnit_Steps: {
nsTimingFunction timingFunction;
nsRuleNode::ComputeTimingFunction(list->mValue, timingFunction);
ComputedTimingFunction computedTimingFunction;
computedTimingFunction.Init(timingFunction);
return Some(computedTimingFunction);
}
default:
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -280,16 +280,17 @@ CSS_KEY(fixed, fixed)
CSS_KEY(flat, flat)
CSS_KEY(flex, flex)
CSS_KEY(flex-end, flex_end)
CSS_KEY(flex-start, flex_start)
CSS_KEY(flip, flip)
CSS_KEY(flow-root, flow_root)
CSS_KEY(forwards, forwards)
CSS_KEY(fraktur, fraktur)
+CSS_KEY(frames, frames)
CSS_KEY(from-image, from_image)
CSS_KEY(full-width, full_width)
CSS_KEY(fullscreen, fullscreen)
CSS_KEY(grab, grab)
CSS_KEY(grabbing, grabbing)
CSS_KEY(grad, grad)
CSS_KEY(grayscale, grayscale)
CSS_KEY(graytext, graytext)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -1064,16 +1064,17 @@ protected:
bool ParseShadowList(nsCSSPropertyID aProperty);
bool ParseShapeOutside(nsCSSValue& aValue);
bool ParseTransitionProperty();
bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
char aStop,
bool aIsXPoint);
bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
+ bool ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue);
enum ParseAnimationOrTransitionShorthandResult {
eParseAnimationOrTransitionShorthand_Values,
eParseAnimationOrTransitionShorthand_Inherit,
eParseAnimationOrTransitionShorthand_Error
};
ParseAnimationOrTransitionShorthandResult
ParseAnimationOrTransitionShorthand(const nsCSSPropertyID* aProperties,
const nsCSSValue* aInitialValues,
@@ -7938,16 +7939,23 @@ CSSParserImpl::ParseVariant(nsCSSValue&
}
if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
if (!ParseTransitionStepTimingFunctionValues(aValue)) {
SkipUntil(')');
return CSSParseResult::Error;
}
return CSSParseResult::Ok;
}
+ if (tk->mIdent.LowerCaseEqualsLiteral("frames")) {
+ if (!ParseTransitionFramesTimingFunctionValues(aValue)) {
+ SkipUntil(')');
+ return CSSParseResult::Error;
+ }
+ return CSSParseResult::Ok;
+ }
}
if ((aVariantMask & VARIANT_CALC) &&
IsCSSTokenCalcFunction(*tk)) {
// calc() currently allows only lengths, percents, numbers, and integers.
//
// Note that VARIANT_NUMBER can be mixed with VARIANT_LENGTH and
// VARIANT_PERCENTAGE in the list of allowed types (numbers can be used as
// coefficients).
@@ -16796,16 +16804,49 @@ CSSParserImpl::ParseTransitionStepTiming
if (!ExpectSymbol(')', true)) {
return false;
}
aValue.SetArrayValue(val, eCSSUnit_Steps);
return true;
}
+bool
+CSSParserImpl::ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue)
+{
+ NS_ASSERTION(!mHavePushBack &&
+ mToken.mType == eCSSToken_Function &&
+ mToken.mIdent.LowerCaseEqualsLiteral("frames"),
+ "unexpected initial state");
+
+ nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
+ MOZ_ASSERT(functionName == eCSSKeyword_frames);
+
+ nsCSSValue frameNumber;
+ if (!ParseSingleTokenOneOrLargerVariant(frameNumber, VARIANT_INTEGER,
+ nullptr)) {
+ return false;
+ }
+ MOZ_ASSERT(frameNumber.GetIntValue() >= 1,
+ "Parsing function should've enforced OneOrLarger, per its name");
+
+ // The number of frames must be a positive integer greater than one.
+ if (frameNumber.GetIntValue() == 1) {
+ return false;
+ }
+
+ if (!ExpectSymbol(')', true)) {
+ return false;
+ }
+
+ RefPtr<nsCSSValue::Array> val = aValue.InitFunction(functionName, 1);
+ val->Item(1) = frameNumber;
+ return true;
+}
+
static nsCSSValueList*
AppendValueToList(nsCSSValue& aContainer,
nsCSSValueList* aTail,
const nsCSSValue& aValue)
{
nsCSSValueList* entry;
if (aContainer.GetUnit() == eCSSUnit_Null) {
MOZ_ASSERT(!aTail, "should not have an entry");
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -6566,16 +6566,20 @@ nsComputedDOMStyle::AppendTimingFunction
tmp);
break;
case nsTimingFunction::Type::StepStart:
case nsTimingFunction::Type::StepEnd:
nsStyleUtil::AppendStepsTimingFunction(aTimingFunction.mType,
aTimingFunction.mStepsOrFrames,
tmp);
break;
+ case nsTimingFunction::Type::Frames:
+ nsStyleUtil::AppendFramesTimingFunction(aTimingFunction.mStepsOrFrames,
+ tmp);
+ break;
default:
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(aTimingFunction.mType,
tmp);
break;
}
timingFunction->SetString(tmp);
aValueList->AppendCSSValue(timingFunction.forget());
}
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -5512,16 +5512,28 @@ nsRuleNode::ComputeTimingFunction(const
nsTimingFunction::Type type =
(array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) ?
nsTimingFunction::Type::StepStart :
nsTimingFunction::Type::StepEnd;
aResult = nsTimingFunction(type, array->Item(0).GetIntValue());
}
break;
+ case eCSSUnit_Function:
+ {
+ nsCSSValue::Array* array = aValue.GetArrayValue();
+ NS_ASSERTION(array && array->Count() == 2, "Need 2 items");
+ NS_ASSERTION(array->Item(0).GetKeywordValue() == eCSSKeyword_frames,
+ "should be frames function");
+ NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer,
+ "unexpected frames function value");
+ aResult = nsTimingFunction(nsTimingFunction::Type::Frames,
+ array->Item(1).GetIntValue());
+ }
+ break;
default:
NS_NOTREACHED("Invalid transition property unit");
}
}
static uint8_t
GetWillChangeBitFieldFromPropFlags(const nsCSSPropertyID& aProp)
{
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -652,16 +652,25 @@ nsStyleUtil::AppendStepsTimingFunction(n
if (aType == nsTimingFunction::Type::StepStart) {
aResult.AppendLiteral(", start)");
} else {
aResult.AppendLiteral(")");
}
}
/* static */ void
+nsStyleUtil::AppendFramesTimingFunction(uint32_t aFrames,
+ nsAString& aResult)
+{
+ aResult.AppendLiteral("frames(");
+ aResult.AppendInt(aFrames);
+ aResult.AppendLiteral(")");
+}
+
+/* static */ void
nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
float aX2, float aY2,
nsAString& aResult)
{
// set the value from the cubic-bezier control points
// (We could try to regenerate the keywords if we want.)
aResult.AppendLiteral("cubic-bezier(");
aResult.AppendFloat(aX1);
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -82,16 +82,18 @@ public:
static void AppendCSSNumber(float aNumber, nsAString& aResult)
{
aResult.AppendFloat(aNumber);
}
static void AppendStepsTimingFunction(nsTimingFunction::Type aType,
uint32_t aSteps,
nsAString& aResult);
+ static void AppendFramesTimingFunction(uint32_t aFrames,
+ nsAString& aResult);
static void AppendCubicBezierTimingFunction(float aX1, float aY1,
float aX2, float aY2,
nsAString& aResult);
static void AppendCubicBezierKeywordTimingFunction(
nsTimingFunction::Type aType,
nsAString& aResult);
static void AppendSerializedFontSrc(const nsCSSValue& aValue,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -968,18 +968,18 @@ var gCSSProperties = {
other_values: [ "paused", "running, running", "paused, running", "paused, paused", "running, paused", "paused, running, running, running, paused, running" ],
invalid_values: [ "0" ]
},
"animation-timing-function": {
domProp: "animationTimingFunction",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "ease" ],
- other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
- invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
+ other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)", "frames(2)", "frames(1000)", "frames( 2 )" ],
+ invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)", "frames(1)", "frames(-2)", "frames", "frames()", "frames(,)", "frames(a)", "frames(2.0)", "frames(2.5)", "frames(2 3)" ]
},
"-moz-appearance": {
domProp: "MozAppearance",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "none" ],
other_values: [ "radio", "menulist" ],
invalid_values: []