Implement step-start, step-end, and steps() timing functions. (Bug 435442, patch 0) r=bzbarsky

This commit is contained in:
L. David Baron 2011-04-11 23:18:42 -07:00
parent 3730a531ff
commit 9f841c864a
16 changed files with 369 additions and 49 deletions

View File

@ -647,6 +647,8 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN 2
#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT 3
#define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT 4
#define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START 5
#define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END 6
// See nsStyleText
// Note: these values pickup after the text-align values because there

View File

@ -437,6 +437,8 @@ CSS_KEY(square, square)
CSS_KEY(start, start)
CSS_KEY(static, static)
CSS_KEY(status-bar, status_bar)
CSS_KEY(step-end, step_end)
CSS_KEY(step-start, step_start)
CSS_KEY(stretch, stretch)
CSS_KEY(stretch-to-fit, stretch_to_fit)
CSS_KEY(stroke, stroke)

View File

@ -113,7 +113,7 @@ namespace css = mozilla::css;
#define VARIANT_NORMAL 0x080000 // M
#define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
#define VARIANT_GRADIENT 0x200000 // eCSSUnit_Gradient
#define VARIANT_CUBIC_BEZIER 0x400000 // CSS transition timing function
#define VARIANT_TIMING_FUNCTION 0x400000 // cubic-bezier() and steps()
#define VARIANT_ALL 0x800000 //
#define VARIANT_IMAGE_RECT 0x01000000 // eCSSUnit_Function
// This is an extra bit that says that a VARIANT_ANGLE allows unitless zero:
@ -154,7 +154,6 @@ namespace css = mozilla::css;
#define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
#define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
#define VARIANT_LPN (VARIANT_LP | VARIANT_NUMBER)
#define VARIANT_TIMING_FUNCTION (VARIANT_KEYWORD | VARIANT_CUBIC_BEZIER)
#define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD)
#define VARIANT_UO (VARIANT_URL | VARIANT_NONE)
#define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
@ -517,6 +516,7 @@ protected:
PRBool ParseTransitionTimingFunctionValueComponent(float& aComponent,
char aStop,
PRBool aCheckRange);
PRBool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
#ifdef MOZ_SVG
PRBool ParsePaint(nsCSSProperty aPropID);
@ -4215,7 +4215,7 @@ CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
VARIANT_NORMAL | \
VARIANT_SYSFONT | \
VARIANT_GRADIENT | \
VARIANT_CUBIC_BEZIER | \
VARIANT_TIMING_FUNCTION | \
VARIANT_ALL | \
VARIANT_CALC
@ -4487,15 +4487,22 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
}
return PR_TRUE;
}
if (((aVariantMask & VARIANT_CUBIC_BEZIER) != 0) &&
if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
(eCSSToken_Function == tk->mType)) {
if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
if (!ParseTransitionTimingFunctionValues(aValue)) {
SkipUntil(')');
return PR_FALSE;
}
return PR_TRUE;
}
if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
if (!ParseTransitionStepTimingFunctionValues(aValue)) {
SkipUntil(')');
return PR_FALSE;
}
return PR_TRUE;
}
}
if ((aVariantMask & VARIANT_CALC) &&
(eCSSToken_Function == tk->mType) &&
@ -7956,6 +7963,48 @@ CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
return PR_FALSE;
}
PRBool
CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
{
NS_ASSERTION(!mHavePushBack &&
mToken.mType == eCSSToken_Function &&
mToken.mIdent.LowerCaseEqualsLiteral("steps"),
"unexpected initial state");
nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
if (!ParsePositiveNonZeroVariant(val->Item(0), VARIANT_INTEGER, nsnull)) {
return PR_FALSE;
}
PRInt32 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
if (ExpectSymbol(',', PR_TRUE)) {
if (!GetToken(PR_TRUE)) {
return PR_FALSE;
}
type = -1;
if (mToken.mType == eCSSToken_Ident) {
if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
} else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
}
}
if (type == -1) {
UngetToken();
return PR_FALSE;
}
}
val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
if (!ExpectSymbol(')', PR_TRUE)) {
return PR_FALSE;
}
aValue.SetArrayValue(val, eCSSUnit_Steps);
return PR_TRUE;
}
static nsCSSValueList*
AppendValueToList(nsCSSValue& aContainer,
nsCSSValueList* aTail,

View File

@ -2288,7 +2288,7 @@ CSS_PROP_DISPLAY(
CSS_PROP_DOMPROP_PREFIXED(TransitionTimingFunction),
CSS_PROPERTY_PARSE_VALUE_LIST |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
VARIANT_TIMING_FUNCTION, // used by list parsing
VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing
kTransitionTimingFunctionKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)

View File

@ -1262,6 +1262,8 @@ const PRInt32 nsCSSProps::kTransitionTimingFunctionKTable[] = {
eCSSKeyword_ease_in, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN,
eCSSKeyword_ease_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT,
eCSSKeyword_ease_in_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT,
eCSSKeyword_step_start, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START,
eCSSKeyword_step_end, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END,
eCSSKeyword_UNKNOWN,-1
};

View File

@ -691,11 +691,12 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
nsStyleUtil::AppendEscapedCSSIdent(buffer, aResult);
}
}
else if (eCSSUnit_Array <= unit && unit <= eCSSUnit_Cubic_Bezier) {
else if (eCSSUnit_Array <= unit && unit <= eCSSUnit_Steps) {
switch (unit) {
case eCSSUnit_Counter: aResult.AppendLiteral("counter("); break;
case eCSSUnit_Counters: aResult.AppendLiteral("counters("); break;
case eCSSUnit_Cubic_Bezier: aResult.AppendLiteral("cubic-bezier("); break;
case eCSSUnit_Steps: aResult.AppendLiteral("steps("); break;
default: break;
}
@ -717,6 +718,21 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
else
aResult.AppendLiteral(", ");
}
if (unit == eCSSUnit_Steps && i == 1) {
NS_ABORT_IF_FALSE(array->Item(i).GetUnit() == eCSSUnit_Enumerated &&
(array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
"unexpected value");
if (array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) {
aResult.AppendLiteral("start");
} else {
aResult.AppendLiteral("end");
}
continue;
}
nsCSSProperty prop =
((eCSSUnit_Counter <= unit && unit <= eCSSUnit_Counters) &&
i == array->Count() - 1)
@ -981,6 +997,7 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
case eCSSUnit_Array: break;
case eCSSUnit_Attr:
case eCSSUnit_Cubic_Bezier:
case eCSSUnit_Steps:
case eCSSUnit_Counter:
case eCSSUnit_Counters: aResult.Append(PRUnichar(')')); break;
case eCSSUnit_Local_Font: break;

View File

@ -115,7 +115,8 @@ enum nsCSSUnit {
eCSSUnit_Counter = 21, // (nsCSSValue::Array*) a counter(string,[string]) value
eCSSUnit_Counters = 22, // (nsCSSValue::Array*) a counters(string,string[,string]) value
eCSSUnit_Cubic_Bezier = 23, // (nsCSSValue::Array*) a list of float values
eCSSUnit_Function = 24, // (nsCSSValue::Array*) a function with
eCSSUnit_Steps = 24, // (nsCSSValue::Array*) a list of (integer, enumerated)
eCSSUnit_Function = 25, // (nsCSSValue::Array*) a function with
// parameters. First elem of array is name,
// the rest of the values are arguments.
@ -127,14 +128,14 @@ enum nsCSSUnit {
// exists so we can distinguish calc(2em) from 2em as specified values
// (but we drop this distinction for nsStyleCoord when we store
// computed values).
eCSSUnit_Calc = 25, // (nsCSSValue::Array*) calc() value
eCSSUnit_Calc = 30, // (nsCSSValue::Array*) calc() value
// Plus, Minus, Times_* and Divided have arrays with exactly 2
// elements. a + b + c + d is grouped as ((a + b) + c) + d
eCSSUnit_Calc_Plus = 26, // (nsCSSValue::Array*) + node within calc()
eCSSUnit_Calc_Minus = 27, // (nsCSSValue::Array*) - within calc
eCSSUnit_Calc_Times_L = 28, // (nsCSSValue::Array*) num * val within calc
eCSSUnit_Calc_Times_R = 29, // (nsCSSValue::Array*) val * num within calc
eCSSUnit_Calc_Divided = 30, // (nsCSSValue::Array*) / within calc
eCSSUnit_Calc_Plus = 31, // (nsCSSValue::Array*) + node within calc()
eCSSUnit_Calc_Minus = 32, // (nsCSSValue::Array*) - within calc
eCSSUnit_Calc_Times_L = 33, // (nsCSSValue::Array*) num * val within calc
eCSSUnit_Calc_Times_R = 34, // (nsCSSValue::Array*) val * num within calc
eCSSUnit_Calc_Divided = 35, // (nsCSSValue::Array*) / within calc
eCSSUnit_URL = 40, // (nsCSSValue::URL*) value
eCSSUnit_Image = 41, // (nsCSSValue::Image*) value

View File

@ -3882,6 +3882,35 @@ nsComputedDOMStyle::DoGetTransitionProperty()
return valueList;
}
void
nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
const nsTimingFunction& aTimingFunction)
{
nsROCSSPrimitiveValue* timingFunction = GetROCSSPrimitiveValue();
aValueList->AppendCSSValue(timingFunction);
if (aTimingFunction.mType == nsTimingFunction::Function) {
// set the value from the cubic-bezier control points
// (We could try to regenerate the keywords if we want.)
timingFunction->SetString(
nsPrintfCString(64, "cubic-bezier(%f, %f, %f, %f)",
aTimingFunction.mFunc.mX1,
aTimingFunction.mFunc.mY1,
aTimingFunction.mFunc.mX2,
aTimingFunction.mFunc.mY2));
} else {
nsString tmp;
tmp.AppendLiteral("steps(");
tmp.AppendInt(aTimingFunction.mSteps);
if (aTimingFunction.mType == nsTimingFunction::StepStart) {
tmp.AppendLiteral(", start)");
} else {
tmp.AppendLiteral(", end)");
}
timingFunction->SetString(tmp);
}
}
nsIDOMCSSValue*
nsComputedDOMStyle::DoGetTransitionTimingFunction()
{
@ -3893,16 +3922,8 @@ nsComputedDOMStyle::DoGetTransitionTimingFunction()
"first item must be explicit");
PRUint32 i = 0;
do {
const nsTransition *transition = &display->mTransitions[i];
nsROCSSPrimitiveValue* timingFunction = GetROCSSPrimitiveValue();
valueList->AppendCSSValue(timingFunction);
// set the value from the cubic-bezier control points
// (We could try to regenerate the keywords if we want.)
const nsTimingFunction& tf = transition->GetTimingFunction();
timingFunction->SetString(
nsPrintfCString(64, "cubic-bezier(%f, %f, %f, %f)",
tf.mX1, tf.mY1, tf.mX2, tf.mY2));
AppendTimingFunction(valueList,
display->mTransitions[i].GetTimingFunction());
} while (++i < display->mTransitionTimingFunctionCount);
return valueList;

View File

@ -168,6 +168,8 @@ private:
void GetImageRectString(nsIURI* aURI,
const nsStyleSides& aCropRect,
nsString& aString);
void AppendTimingFunction(nsDOMCSSValueList *aValueList,
const nsTimingFunction& aTimingFunction);
/* Properties queryable as CSSValues.
* To avoid a name conflict with nsIDOM*CSS2Properties, these are all

View File

@ -3838,6 +3838,28 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
array->Item(3).GetFloatValue()));
}
break;
case eCSSUnit_Steps:
{
nsCSSValue::Array* array =
timingFunction.list->mValue.GetArrayValue();
NS_ASSERTION(array && array->Count() == 2,
"Need 2 items");
NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
"unexpected first value");
NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Enumerated &&
(array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
"unexpected second value");
transition->SetTimingFunction(
nsTimingFunction((
array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END)
? nsTimingFunction::StepEnd : nsTimingFunction::StepStart,
array->Item(0).GetIntValue()));
}
break;
default:
NS_NOTREACHED("Invalid transition property unit");
}

View File

@ -1921,6 +1921,20 @@ nsStyleBackground::Layer::operator==(const Layer& aOther) const
//
void nsTimingFunction::AssignFromKeyword(PRInt32 aTimingFunctionType)
{
switch (aTimingFunctionType) {
case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
mType = StepStart;
mSteps = 1;
return;
case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
mType = StepEnd;
mSteps = 1;
return;
default:
mType = Function;
break;
}
PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE == 0);
PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR == 1);
PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN == 2);
@ -1937,10 +1951,10 @@ void nsTimingFunction::AssignFromKeyword(PRInt32 aTimingFunctionType)
NS_ABORT_IF_FALSE(0 <= aTimingFunctionType && aTimingFunctionType < 5,
"keyword out of range");
mX1 = timingFunctionValues[aTimingFunctionType][0];
mY1 = timingFunctionValues[aTimingFunctionType][1];
mX2 = timingFunctionValues[aTimingFunctionType][2];
mY2 = timingFunctionValues[aTimingFunctionType][3];
mFunc.mX1 = timingFunctionValues[aTimingFunctionType][0];
mFunc.mY1 = timingFunctionValues[aTimingFunctionType][1];
mFunc.mX2 = timingFunctionValues[aTimingFunctionType][2];
mFunc.mY2 = timingFunctionValues[aTimingFunctionType][3];
}
nsTransition::nsTransition(const nsTransition& aCopy)

View File

@ -1309,6 +1309,8 @@ struct nsStyleVisibility {
};
struct nsTimingFunction {
enum Type { Function, StepStart, StepEnd };
explicit nsTimingFunction(PRInt32 aTimingFunctionType
= NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)
{
@ -1316,26 +1318,60 @@ struct nsTimingFunction {
}
nsTimingFunction(float x1, float y1, float x2, float y2)
: mX1(x1)
, mY1(y1)
, mX2(x2)
, mY2(y2)
{}
float mX1;
float mY1;
float mX2;
float mY2;
PRBool operator==(const nsTimingFunction& aOther) const
: mType(Function)
{
return !(*this != aOther);
mFunc.mX1 = x1;
mFunc.mY1 = y1;
mFunc.mX2 = x2;
mFunc.mY2 = y2;
}
PRBool operator!=(const nsTimingFunction& aOther) const
nsTimingFunction(Type aType, PRUint32 aSteps)
: mType(aType)
{
return mX1 != aOther.mX1 || mY1 != aOther.mY1 ||
mX2 != aOther.mX2 || mY2 != aOther.mY2;
NS_ABORT_IF_FALSE(mType == StepStart || mType == StepEnd, "wrong type");
mSteps = aSteps;
}
nsTimingFunction(const nsTimingFunction& aOther)
: mType(aOther.mType)
{
if (mType == Function) {
mFunc.mX1 = aOther.mFunc.mX1;
mFunc.mY1 = aOther.mFunc.mY1;
mFunc.mX2 = aOther.mFunc.mX2;
mFunc.mY2 = aOther.mFunc.mY2;
} else {
mSteps = aOther.mSteps;
}
}
Type mType;
union {
struct {
float mX1;
float mY1;
float mX2;
float mY2;
} mFunc;
PRUint32 mSteps;
};
bool operator==(const nsTimingFunction& aOther) const
{
if (mType != aOther.mType) {
return false;
}
if (mType == Function) {
return mFunc.mX1 == aOther.mFunc.mX1 && mFunc.mY1 == aOther.mFunc.mY1 &&
mFunc.mX2 == aOther.mFunc.mX2 && mFunc.mY2 == aOther.mFunc.mY2;
}
return mSteps == aOther.mSteps;
}
bool operator!=(const nsTimingFunction& aOther) const
{
return !(*this == aOther);
}
private:

View File

@ -65,6 +65,59 @@ namespace dom = mozilla::dom;
* Per-Element data *
*****************************************************************************/
class ComputedTimingFunction {
public:
typedef nsTimingFunction::Type Type;
void Init(const nsTimingFunction &aFunction);
double GetValue(double aPortion) const;
private:
Type mType;
nsSMILKeySpline mTimingFunction;
PRUint32 mSteps;
};
void
ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
{
mType = aFunction.mType;
if (mType == nsTimingFunction::Function) {
mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
aFunction.mFunc.mX2, aFunction.mFunc.mY2);
} else {
mSteps = aFunction.mSteps;
}
}
static inline double
StepEnd(PRUint32 aSteps, double aPortion)
{
NS_ABORT_IF_FALSE(0.0 <= aPortion && aPortion <= 1.0, "out of range");
PRUint32 step = PRUint32(aPortion * aSteps); // floor
return double(step) / double(aSteps);
}
double
ComputedTimingFunction::GetValue(double aPortion) const
{
switch (mType) {
case nsTimingFunction::Function:
return mTimingFunction.GetSplineValue(aPortion);
case nsTimingFunction::StepStart:
// There are diagrams in the spec that seem to suggest this check
// and the bounds point should not be symmetric with StepEnd, but
// should actually step up at rather than immediately after the
// fraction points. However, we rely on rounding negative values
// up to zero, so we can't do that. And it's not clear the spec
// really meant it.
return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
default:
NS_ABORT_IF_FALSE(PR_FALSE, "bad type");
// fall through
case nsTimingFunction::StepEnd:
return StepEnd(mSteps, aPortion);
}
}
struct ElementPropertyTransition
{
nsCSSProperty mProperty;
@ -73,7 +126,7 @@ struct ElementPropertyTransition
// data from the relevant nsTransition
TimeDuration mDuration;
nsSMILKeySpline mTimingFunction;
ComputedTimingFunction mTimingFunction;
// This is the start value to be used for a check for whether a
// transition is being reversed. Normally the same as mStartValue,
@ -134,7 +187,7 @@ ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const
timePortion = 1.0; // we might be behind on flushing
}
return mTimingFunction.GetSplineValue(timePortion);
return mTimingFunction.GetValue(timePortion);
}
/**
@ -704,7 +757,7 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
pt.mProperty = aProperty;
pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay);
pt.mDuration = TimeDuration::FromMilliseconds(duration);
pt.mTimingFunction.Init(tf.mX1, tf.mY1, tf.mX2, tf.mY2);
pt.mTimingFunction.Init(tf);
if (!aElementTransitions) {
aElementTransitions =

View File

@ -179,6 +179,7 @@ _TEST_FILES = test_acid3_test46.html \
test_transitions_events.html \
test_transitions.html \
test_transitions_per_property.html \
test_transitions_step_functions.html \
test_transitions_dynamic_changes.html \
test_transitions_bug537151.html \
test_unclosed_parentheses.html \

View File

@ -2641,8 +2641,8 @@ var gCSSProperties = {
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
other_values: [ "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)" ],
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)" ]
other_values: [ "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)" ]
},
"unicode-bidi": {
domProp: "unicodeBidi",

View File

@ -0,0 +1,98 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=435441
-->
<head>
<title>Test for Bug 435441</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
p.transition {
-moz-transition: margin-top 100s linear;
}
</style>
</head>
<body>
<div id="display">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for transition step functions **/
function px_to_num(str)
{
return Number(String(str).match(/^([\d.]+)px$/)[1]);
}
var display = document.getElementById("display");
function run_test(tf, percent, value)
{
var p = document.createElement("p");
p.className = "transition";
p.style.marginTop = "0px";
// be this percent of the way through a 100s transition
p.style.MozTransitionDelay = (percent * -100) + "s";
p.style.MozTransitionTimingFunction = tf;
display.appendChild(p);
var cs = getComputedStyle(p, "");
var flush1 = cs.marginTop;
p.style.marginTop = "1000px";
var result = px_to_num(cs.marginTop) / 1000
is(result, value, 100 * percent + "% of the way through " + tf);
display.removeChild(p);
}
run_test("step-start", 0, 0);
run_test("step-start", 0.001, 1);
run_test("step-start", 0.999, 1);
run_test("step-start", 1, 1);
run_test("step-end", 0, 0);
run_test("step-end", 0.001, 0);
run_test("step-end", 0.999, 0);
run_test("step-end", 1, 1);
run_test("steps(2)", 0.00, 0.0);
run_test("steps(2)", 0.49, 0.0);
run_test("steps(2)", 0.50, 0.5);
run_test("steps(2)", 0.99, 0.5);
run_test("steps(2)", 1.00, 1.0);
run_test("steps(2, end)", 0.00, 0.0);
run_test("steps(2, end)", 0.49, 0.0);
run_test("steps(2, end)", 0.50, 0.5);
run_test("steps(2, end)", 0.99, 0.5);
run_test("steps(2, end)", 1.00, 1.0);
run_test("steps(2, start)", 0.00, 0.0);
run_test("steps(2, start)", 0.01, 0.5);
run_test("steps(2, start)", 0.50, 0.5);
run_test("steps(2, start)", 0.51, 1.0);
run_test("steps(2, start)", 1.00, 1.0);
run_test("steps(8,end)", 0.00, 0.0);
run_test("steps(8,end)", 0.10, 0.0);
run_test("steps(8,end)", 0.20, 0.125);
run_test("steps(8,end)", 0.30, 0.25);
run_test("steps(8,end)", 0.40, 0.375);
run_test("steps(8,end)", 0.49, 0.375);
run_test("steps(8,end)", 0.50, 0.5);
run_test("steps(8,end)", 0.60, 0.5);
run_test("steps(8,end)", 0.70, 0.625);
run_test("steps(8,end)", 0.80, 0.75);
run_test("steps(8,end)", 0.90, 0.875);
run_test("steps(8,end)", 1.00, 1.0);
</script>
</pre>
</body>
</html>