mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 896050 - Implement animation of CSS filter property. r=dholbert
This commit is contained in:
parent
ee356b49c2
commit
bc78994cda
@ -3374,7 +3374,7 @@ CSS_PROP_SVGRESET(
|
||||
0,
|
||||
nullptr,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_None)
|
||||
eStyleAnimType_Custom)
|
||||
CSS_PROP_SVGRESET(
|
||||
flood-color,
|
||||
flood_color,
|
||||
|
@ -248,6 +248,34 @@ nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
|
||||
eCSSUnit_Pixel);
|
||||
}
|
||||
|
||||
static void
|
||||
AppendCSSShadowValue(const nsCSSShadowItem *aShadow,
|
||||
nsCSSValueList **&aResultTail)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aShadow, "shadow expected");
|
||||
|
||||
// X, Y, Radius, Spread, Color, Inset
|
||||
nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
|
||||
nscoordToCSSValue(aShadow->mXOffset, arr->Item(0));
|
||||
nscoordToCSSValue(aShadow->mYOffset, arr->Item(1));
|
||||
nscoordToCSSValue(aShadow->mRadius, arr->Item(2));
|
||||
// NOTE: This code sometimes stores mSpread: 0 even when
|
||||
// the parser would be required to leave it null.
|
||||
nscoordToCSSValue(aShadow->mSpread, arr->Item(3));
|
||||
if (aShadow->mHasColor) {
|
||||
arr->Item(4).SetColorValue(aShadow->mColor);
|
||||
}
|
||||
if (aShadow->mInset) {
|
||||
arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
|
||||
eCSSUnit_Enumerated);
|
||||
}
|
||||
|
||||
nsCSSValueList *resultItem = new nsCSSValueList;
|
||||
resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
|
||||
*aResultTail = resultItem;
|
||||
aResultTail = &resultItem->mNext;
|
||||
}
|
||||
|
||||
// Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
|
||||
struct CalcValue {
|
||||
float mLength, mPercent;
|
||||
@ -753,6 +781,8 @@ nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty,
|
||||
aDistance = sqrt(squareDistance);
|
||||
return true;
|
||||
}
|
||||
case eUnit_Filter:
|
||||
// FIXME: Support paced animations for filter function interpolation.
|
||||
case eUnit_Transform: {
|
||||
return false;
|
||||
}
|
||||
@ -1032,6 +1062,38 @@ AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline float
|
||||
GetNumberOrPercent(const nsCSSValue &aValue)
|
||||
{
|
||||
nsCSSUnit unit = aValue.GetUnit();
|
||||
NS_ABORT_IF_FALSE(unit == eCSSUnit_Number || unit == eCSSUnit_Percent,
|
||||
"unexpected unit");
|
||||
return (unit == eCSSUnit_Number) ?
|
||||
aValue.GetFloatValue() : aValue.GetPercentValue();
|
||||
}
|
||||
|
||||
static inline void
|
||||
AddCSSValuePercentNumber(const uint32_t aValueRestrictions,
|
||||
double aCoeff1, const nsCSSValue &aValue1,
|
||||
double aCoeff2, const nsCSSValue &aValue2,
|
||||
nsCSSValue &aResult, float aInitialVal)
|
||||
{
|
||||
float n1 = GetNumberOrPercent(aValue1);
|
||||
float n2 = GetNumberOrPercent(aValue2);
|
||||
|
||||
// Rather than interpolating aValue1 and aValue2 directly, we
|
||||
// interpolate their *distances from aInitialVal* (the initial value,
|
||||
// which is either 1 or 0 for "filter" functions). This matters in
|
||||
// cases where aInitialVal is nonzero and the coefficients don't add
|
||||
// up to 1. For example, if initialVal is 1, aCoeff1 is 0.5, and
|
||||
// aCoeff2 is 0, then we'll return the value halfway between 1 and
|
||||
// aValue1, rather than the value halfway between 0 and aValue1.
|
||||
// Note that we do something similar in AddTransformScale().
|
||||
float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
|
||||
aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
|
||||
eCSSUnit_Number);
|
||||
}
|
||||
|
||||
static bool
|
||||
AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
|
||||
double aCoeff2, const nsCSSValue &aValue2,
|
||||
@ -1547,6 +1609,121 @@ TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
|
||||
return ToPrimitive(func1) == ToPrimitive(func2);
|
||||
}
|
||||
|
||||
static bool
|
||||
AddFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
|
||||
double aCoeff2, const nsCSSValueList* aList2,
|
||||
nsCSSValueList**& aResultTail)
|
||||
{
|
||||
// AddFilterFunction should be our only caller, and it should ensure that both
|
||||
// args are non-null.
|
||||
NS_ABORT_IF_FALSE(aList1, "expected filter list");
|
||||
NS_ABORT_IF_FALSE(aList2, "expected filter list");
|
||||
NS_ABORT_IF_FALSE(aList1->mValue.GetUnit() == eCSSUnit_Function,
|
||||
"expected function");
|
||||
NS_ABORT_IF_FALSE(aList2->mValue.GetUnit() == eCSSUnit_Function,
|
||||
"expected function");
|
||||
nsRefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
|
||||
a2 = aList2->mValue.GetArrayValue();
|
||||
nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
|
||||
if (filterFunction != a2->Item(0).GetKeywordValue())
|
||||
return false; // Can't add two filters of different types.
|
||||
|
||||
nsAutoPtr<nsCSSValueList> resultListEntry(new nsCSSValueList);
|
||||
nsCSSValue::Array* result =
|
||||
resultListEntry->mValue.InitFunction(filterFunction, 1);
|
||||
|
||||
// "hue-rotate" is the only filter-function that accepts negative values, and
|
||||
// we don't use this "restrictions" variable in its clause below.
|
||||
const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
|
||||
const nsCSSValue& funcArg1 = a1->Item(1);
|
||||
const nsCSSValue& funcArg2 = a2->Item(1);
|
||||
nsCSSValue& resultArg = result->Item(1);
|
||||
float initialVal = 1.0f;
|
||||
switch (filterFunction) {
|
||||
case eCSSKeyword_blur: {
|
||||
nsCSSUnit unit;
|
||||
if (funcArg1.GetUnit() == funcArg2.GetUnit()) {
|
||||
unit = funcArg1.GetUnit();
|
||||
} else {
|
||||
// If units differ, we'll just combine them with calc().
|
||||
unit = eCSSUnit_Calc;
|
||||
}
|
||||
if (!AddCSSValuePixelPercentCalc(restrictions,
|
||||
unit,
|
||||
aCoeff1, funcArg1,
|
||||
aCoeff2, funcArg2,
|
||||
resultArg)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eCSSKeyword_grayscale:
|
||||
case eCSSKeyword_invert:
|
||||
case eCSSKeyword_sepia:
|
||||
initialVal = 0.0f;
|
||||
case eCSSKeyword_brightness:
|
||||
case eCSSKeyword_contrast:
|
||||
case eCSSKeyword_opacity:
|
||||
case eCSSKeyword_saturate:
|
||||
AddCSSValuePercentNumber(restrictions,
|
||||
aCoeff1, funcArg1,
|
||||
aCoeff2, funcArg2,
|
||||
resultArg,
|
||||
initialVal);
|
||||
break;
|
||||
case eCSSKeyword_hue_rotate:
|
||||
AddCSSValueAngle(aCoeff1, funcArg1,
|
||||
aCoeff2, funcArg2,
|
||||
resultArg);
|
||||
break;
|
||||
case eCSSKeyword_drop_shadow: {
|
||||
nsCSSValueList* resultShadow = resultArg.SetListValue();
|
||||
nsAutoPtr<nsCSSValueList> shadowValue;
|
||||
nsCSSValueList **shadowTail = getter_Transfers(shadowValue);
|
||||
NS_ABORT_IF_FALSE(!funcArg1.GetListValue()->mNext &&
|
||||
!funcArg2.GetListValue()->mNext,
|
||||
"drop-shadow filter func doesn't support lists");
|
||||
if (!AddShadowItems(aCoeff1, funcArg1.GetListValue()->mValue,
|
||||
aCoeff2, funcArg2.GetListValue()->mValue,
|
||||
shadowTail)) {
|
||||
return false;
|
||||
}
|
||||
*resultShadow = *shadowValue;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(false, "unknown filter function");
|
||||
return false;
|
||||
}
|
||||
|
||||
*aResultTail = resultListEntry.forget();
|
||||
aResultTail = &(*aResultTail)->mNext;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AddFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
|
||||
double aCoeff2, const nsCSSValueList* aList2,
|
||||
nsCSSValueList**& aResultTail)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aList1 || aList2,
|
||||
"one function list item must not be null");
|
||||
// Note that one of our arguments could be null, indicating that
|
||||
// it's the initial value. Rather than adding special null-handling
|
||||
// logic, we just check for null values and replace them with
|
||||
// 0 * the other value. That way, AddFilterFunctionImpl can assume
|
||||
// its args are non-null.
|
||||
if (!aList1) {
|
||||
return AddFilterFunctionImpl(aCoeff2, aList2, 0, aList2, aResultTail);
|
||||
}
|
||||
if (!aList2) {
|
||||
return AddFilterFunctionImpl(aCoeff1, aList1, 0, aList1, aResultTail);
|
||||
}
|
||||
|
||||
return AddFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2, aResultTail);
|
||||
}
|
||||
|
||||
static nsCSSValueList*
|
||||
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
|
||||
double aCoeff2, const nsCSSValueList* aList2)
|
||||
@ -2060,6 +2237,44 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
|
||||
aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eUnit_Filter: {
|
||||
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
|
||||
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
|
||||
|
||||
nsAutoPtr<nsCSSValueList> result;
|
||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||
while (list1 || list2) {
|
||||
NS_ABORT_IF_FALSE(!*resultTail,
|
||||
"resultTail isn't pointing to the tail (may leak)");
|
||||
if ((list1 && list1->mValue.GetUnit() != eCSSUnit_Function) ||
|
||||
(list2 && list2->mValue.GetUnit() != eCSSUnit_Function)) {
|
||||
// If we don't have filter-functions, we must have filter-URLs, which
|
||||
// we can't add or interpolate.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddFilterFunction(aCoeff1, list1, aCoeff2, list2, resultTail)) {
|
||||
// filter function mismatch
|
||||
return false;
|
||||
}
|
||||
|
||||
// move to next list items
|
||||
if (list1) {
|
||||
list1 = list1->mNext;
|
||||
}
|
||||
if (list2) {
|
||||
list2 = list2->mNext;
|
||||
}
|
||||
};
|
||||
NS_ABORT_IF_FALSE(!*resultTail,
|
||||
"resultTail isn't pointing to the tail (may leak)");
|
||||
|
||||
aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
|
||||
eUnit_Filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eUnit_Transform: {
|
||||
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
|
||||
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
|
||||
@ -2432,6 +2647,7 @@ nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
|
||||
} break;
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Shadow:
|
||||
case eUnit_Filter:
|
||||
case eUnit_Transform:
|
||||
case eUnit_BackgroundPosition:
|
||||
aSpecifiedValue.
|
||||
@ -2544,12 +2760,27 @@ StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
|
||||
case eStyleUnit_Coord:
|
||||
nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
|
||||
break;
|
||||
case eStyleUnit_Factor:
|
||||
aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
|
||||
break;
|
||||
case eStyleUnit_Percent:
|
||||
aCSSValue.SetPercentValue(aCoord.GetPercentValue());
|
||||
break;
|
||||
case eStyleUnit_Calc:
|
||||
SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
|
||||
break;
|
||||
case eStyleUnit_Degree:
|
||||
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree);
|
||||
break;
|
||||
case eStyleUnit_Grad:
|
||||
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad);
|
||||
break;
|
||||
case eStyleUnit_Radian:
|
||||
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian);
|
||||
break;
|
||||
case eStyleUnit_Turn:
|
||||
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn);
|
||||
break;
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(false, "unexpected unit");
|
||||
return false;
|
||||
@ -3012,6 +3243,63 @@ nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
|
||||
break;
|
||||
}
|
||||
|
||||
case eCSSProperty_filter: {
|
||||
const nsStyleSVGReset *svgReset =
|
||||
static_cast<const nsStyleSVGReset*>(styleStruct);
|
||||
const nsTArray<nsStyleFilter>& filters = svgReset->mFilters;
|
||||
nsAutoPtr<nsCSSValueList> result;
|
||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||
for (uint32_t i = 0; i < filters.Length(); ++i) {
|
||||
nsCSSValueList *item = new nsCSSValueList;
|
||||
*resultTail = item;
|
||||
resultTail = &item->mNext;
|
||||
const nsStyleFilter& filter = filters[i];
|
||||
int32_t type = filter.GetType();
|
||||
if (type == NS_STYLE_FILTER_URL) {
|
||||
nsIDocument* doc = aStyleContext->PresContext()->Document();
|
||||
nsRefPtr<nsStringBuffer> uriAsStringBuffer =
|
||||
GetURIAsUtf16StringBuffer(filter.GetURL());
|
||||
nsRefPtr<mozilla::css::URLValue> url =
|
||||
new mozilla::css::URLValue(filter.GetURL(),
|
||||
uriAsStringBuffer,
|
||||
doc->GetDocumentURI(),
|
||||
doc->NodePrincipal());
|
||||
item->mValue.SetURLValue(url);
|
||||
} else {
|
||||
nsCSSKeyword functionName =
|
||||
nsCSSProps::ValueToKeywordEnum(type,
|
||||
nsCSSProps::kFilterFunctionKTable);
|
||||
nsCSSValue::Array* filterArray =
|
||||
item->mValue.InitFunction(functionName, 1);
|
||||
if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) {
|
||||
if (!StyleCoordToCSSValue(
|
||||
filter.GetFilterParameter(),
|
||||
filterArray->Item(1))) {
|
||||
return false;
|
||||
}
|
||||
} else if (type == NS_STYLE_FILTER_DROP_SHADOW) {
|
||||
nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue();
|
||||
nsAutoPtr<nsCSSValueList> tmpShadowValue;
|
||||
nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue);
|
||||
nsCSSShadowArray* shadowArray = filter.GetDropShadow();
|
||||
NS_ABORT_IF_FALSE(shadowArray->Length() == 1,
|
||||
"expected exactly one shadow");
|
||||
AppendCSSShadowValue(shadowArray->ShadowAt(0), tmpShadowResultTail);
|
||||
*shadowResult = *tmpShadowValue;
|
||||
} else {
|
||||
// We checked all possible nsStyleFilter types but
|
||||
// NS_STYLE_FILTER_NULL before. We should never enter this path.
|
||||
NS_NOTREACHED("no other filter functions defined");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
|
||||
eUnit_Filter);
|
||||
break;
|
||||
}
|
||||
|
||||
case eCSSProperty_transform: {
|
||||
const nsStyleDisplay *display =
|
||||
static_cast<const nsStyleDisplay*>(styleStruct);
|
||||
@ -3172,30 +3460,7 @@ nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
|
||||
nsAutoPtr<nsCSSValueList> result;
|
||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||
for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
|
||||
const nsCSSShadowItem *shadow = shadowArray->ShadowAt(i);
|
||||
// X, Y, Radius, Spread, Color, Inset
|
||||
nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
|
||||
nscoordToCSSValue(shadow->mXOffset, arr->Item(0));
|
||||
nscoordToCSSValue(shadow->mYOffset, arr->Item(1));
|
||||
nscoordToCSSValue(shadow->mRadius, arr->Item(2));
|
||||
// NOTE: This code sometimes stores mSpread: 0 even when
|
||||
// the parser would be required to leave it null.
|
||||
nscoordToCSSValue(shadow->mSpread, arr->Item(3));
|
||||
if (shadow->mHasColor) {
|
||||
arr->Item(4).SetColorValue(shadow->mColor);
|
||||
}
|
||||
if (shadow->mInset) {
|
||||
arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
|
||||
eCSSUnit_Enumerated);
|
||||
}
|
||||
|
||||
nsCSSValueList *resultItem = new nsCSSValueList;
|
||||
if (!resultItem) {
|
||||
return false;
|
||||
}
|
||||
resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
|
||||
*resultTail = resultItem;
|
||||
resultTail = &resultItem->mNext;
|
||||
AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail);
|
||||
}
|
||||
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
|
||||
eUnit_Shadow);
|
||||
@ -3299,12 +3564,14 @@ nsStyleAnimation::Value::operator=(const Value& aOther)
|
||||
mUnit = eUnit_Null;
|
||||
}
|
||||
break;
|
||||
case eUnit_Filter:
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Shadow:
|
||||
case eUnit_Transform:
|
||||
case eUnit_BackgroundPosition:
|
||||
NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || aOther.mValue.mCSSValueList,
|
||||
"value lists other than shadows may not be null");
|
||||
NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
|
||||
aOther.mValue.mCSSValueList,
|
||||
"value lists other than shadows and filters may not be null");
|
||||
if (aOther.mValue.mCSSValueList) {
|
||||
mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
|
||||
if (!mValue.mCSSValueList) {
|
||||
@ -3453,8 +3720,9 @@ nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
|
||||
{
|
||||
FreeValue();
|
||||
NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
|
||||
NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aValueList != nullptr,
|
||||
"dasharrays may not be null");
|
||||
NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aUnit != eUnit_Filter ||
|
||||
aValueList != nullptr,
|
||||
"dasharrays and filters may not be null");
|
||||
mUnit = aUnit;
|
||||
mValue.mCSSValueList = aValueList; // take ownership
|
||||
}
|
||||
@ -3523,6 +3791,7 @@ nsStyleAnimation::Value::operator==(const Value& aOther) const
|
||||
case eUnit_CSSRect:
|
||||
return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Filter:
|
||||
case eUnit_Shadow:
|
||||
case eUnit_Transform:
|
||||
case eUnit_BackgroundPosition:
|
||||
|
@ -221,6 +221,7 @@ public:
|
||||
eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
|
||||
eUnit_CSSRect, // nsCSSRect* (never null)
|
||||
eUnit_Dasharray, // nsCSSValueList* (never null)
|
||||
eUnit_Filter, // nsCSSValueList* (may be null)
|
||||
eUnit_Shadow, // nsCSSValueList* (may be null)
|
||||
eUnit_Transform, // nsCSSValueList* (never null)
|
||||
eUnit_BackgroundPosition, // nsCSSValueList* (never null)
|
||||
@ -380,8 +381,9 @@ public:
|
||||
return aUnit == eUnit_CSSRect;
|
||||
}
|
||||
static bool IsCSSValueListUnit(Unit aUnit) {
|
||||
return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow ||
|
||||
aUnit == eUnit_Transform || aUnit == eUnit_BackgroundPosition;
|
||||
return aUnit == eUnit_Dasharray || aUnit == eUnit_Filter ||
|
||||
aUnit == eUnit_Shadow || aUnit == eUnit_Transform ||
|
||||
aUnit == eUnit_BackgroundPosition;
|
||||
}
|
||||
static bool IsCSSValuePairListUnit(Unit aUnit) {
|
||||
return aUnit == eUnit_CSSValuePairList;
|
||||
|
@ -122,6 +122,7 @@ var supported_properties = {
|
||||
// opacity is clamped in computed style
|
||||
// (not parsing/interpolation)
|
||||
test_float_zeroToOne_clamped ],
|
||||
"filter" : [ test_filter_transition ],
|
||||
"flood-color": [ test_color_transition ],
|
||||
"flood-opacity" : [ test_float_zeroToOne_transition,
|
||||
// opacity is clamped in computed style
|
||||
@ -576,6 +577,156 @@ var transformTests = [
|
||||
round_error_ok: true },
|
||||
];
|
||||
|
||||
var filterTests = [
|
||||
{ start: "none", end: "none",
|
||||
expected: ["none"] },
|
||||
// function from none (number/length)
|
||||
{ start: "none", end: "brightness(0.5)",
|
||||
expected: ["brightness", 0.875] },
|
||||
{ start: "none", end: "contrast(0.5)",
|
||||
expected: ["contrast", 0.875] },
|
||||
{ start: "none", end: "grayscale(0.5)",
|
||||
expected: ["grayscale", 0.125] },
|
||||
{ start: "none", end: "invert(0.5)",
|
||||
expected: ["invert", 0.125] },
|
||||
{ start: "none", end: "opacity(0.5)",
|
||||
expected: ["opacity", 0.875] },
|
||||
{ start: "none", end: "saturate(0.5)",
|
||||
expected: ["saturate", 0.875] },
|
||||
{ start: "none", end: "sepia(0.5)",
|
||||
expected: ["sepia", 0.125] },
|
||||
{ start: "none", end: "blur(50px)",
|
||||
expected: ["blur", 12.5] },
|
||||
// function to none (number/length)
|
||||
{ start: "brightness(0.5)", end: "none",
|
||||
expected: ["brightness", 0.625] },
|
||||
{ start: "contrast(0.5)", end: "none",
|
||||
expected: ["contrast", 0.625] },
|
||||
{ start: "grayscale(0.5)", end: "none",
|
||||
expected: ["grayscale", 0.375] },
|
||||
{ start: "invert(0.5)", end: "none",
|
||||
expected: ["invert", 0.375] },
|
||||
{ start: "opacity(0.5)", end: "none",
|
||||
expected: ["opacity", 0.625] },
|
||||
{ start: "saturate(0.5)", end: "none",
|
||||
expected: ["saturate", 0.625] },
|
||||
{ start: "sepia(0.5)", end: "none",
|
||||
expected: ["sepia", 0.375] },
|
||||
{ start: "blur(50px)", end: "none",
|
||||
expected: ["blur", 37.5] },
|
||||
// function to same function (number/length)
|
||||
{ start: "brightness(0.25)", end: "brightness(0.75)",
|
||||
expected: ["brightness", 0.375] },
|
||||
{ start: "contrast(0.25)", end: "contrast(0.75)",
|
||||
expected: ["contrast", 0.375] },
|
||||
{ start: "grayscale(0.25)", end: "grayscale(0.75)",
|
||||
expected: ["grayscale", 0.375] },
|
||||
{ start: "invert(0.25)", end: "invert(0.75)",
|
||||
expected: ["invert", 0.375] },
|
||||
{ start: "opacity(0.25)", end: "opacity(0.75)",
|
||||
expected: ["opacity", 0.375] },
|
||||
{ start: "saturate(0.25)", end: "saturate(0.75)",
|
||||
expected: ["saturate", 0.375] },
|
||||
{ start: "sepia(0.25)", end: "sepia(0.75)",
|
||||
expected: ["sepia", 0.375] },
|
||||
{ start: "blur(25px)", end: "blur(75px)",
|
||||
expected: ["blur", 37.5] },
|
||||
// function to same function (percent)
|
||||
{ start: "brightness(25%)", end: "brightness(75%)",
|
||||
expected: ["brightness", 0.375] },
|
||||
{ start: "contrast(25%)", end: "contrast(75%)",
|
||||
expected: ["contrast", 0.375] },
|
||||
{ start: "grayscale(25%)", end: "grayscale(75%)",
|
||||
expected: ["grayscale", 0.375] },
|
||||
{ start: "invert(25%)", end: "invert(75%)",
|
||||
expected: ["invert", 0.375] },
|
||||
{ start: "opacity(25%)", end: "opacity(75%)",
|
||||
expected: ["opacity", 0.375] },
|
||||
{ start: "saturate(25%)", end: "saturate(75%)",
|
||||
expected: ["saturate", 0.375] },
|
||||
{ start: "sepia(25%)", end: "sepia(75%)",
|
||||
expected: ["sepia", 0.375] },
|
||||
// function to same function (percent, number/length)
|
||||
{ start: "brightness(0.25)", end: "brightness(75%)",
|
||||
expected: ["brightness", 0.375] },
|
||||
{ start: "contrast(25%)", end: "contrast(0.75)",
|
||||
expected: ["contrast", 0.375] },
|
||||
// hue-rotate with different angle values
|
||||
{ start: "hue-rotate(0deg)", end: "hue-rotate(720deg)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0rad)", end: "hue-rotate("+4*Math.PI+"rad)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0grad)", end: "hue-rotate(800grad)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0turn)", end: "hue-rotate(2turn)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0deg)", end: "hue-rotate("+4*Math.PI+"rad)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0turn)", end: "hue-rotate(800grad)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0grad)", end: "hue-rotate("+4*Math.PI+"rad)",
|
||||
expected: ["hue-rotate", Math.PI.toFixed(5)] },
|
||||
{ start: "hue-rotate(0grad)", end: "hue-rotate(0turn)",
|
||||
expected: ["hue-rotate", 0] },
|
||||
// multiple matching functions, same length
|
||||
{ start: "contrast(25%) brightness(0.25) blur(25px) sepia(75%)",
|
||||
end: "contrast(75%) brightness(0.75) blur(75px) sepia(25%)",
|
||||
expected: ["contrast", 0.375, "brightness", 0.375, "blur", 37.5, "sepia", 0.625] },
|
||||
{ start: "invert(25%) brightness(0.25) blur(25px) invert(50%) brightness(0.5) blur(50px)",
|
||||
end: "invert(75%) brightness(0.75) blur(75px)",
|
||||
expected: ["invert", 0.375, "brightness", 0.375, "blur", 37.5, "invert", 0.375, "brightness", 0.625, "blur", 37.5] },
|
||||
// multiple matching functions, different length
|
||||
{ start: "contrast(25%) brightness(0.5) blur(50px)",
|
||||
end: "contrast(75%)",
|
||||
expected: ["contrast", 0.375, "brightness", 0.625, "blur", 37.5] },
|
||||
// mismatching filter functions
|
||||
{ start: "contrast(0%)", end: "blur(10px)",
|
||||
expected: ["blur", 10] },
|
||||
// not supported interpolations
|
||||
{ start: "none", end: "url('#b')",
|
||||
expected: ["url", "\""+document.URL+"#b\""] },
|
||||
{ start: "url('#a')", end: "none",
|
||||
expected: ["none"] },
|
||||
{ start: "url('#a')", end: "url('#b')",
|
||||
expected: ["url", "\""+document.URL+"#b\""] },
|
||||
{ start: "url('#a')", end: "blur(10px)",
|
||||
expected: ["blur", 10] },
|
||||
{ start: "blur(10px)", end: "url('#a')",
|
||||
expected: ["url", "\""+document.URL+"#a\""] },
|
||||
{ start: "blur(0px) url('#a')", end: "blur(20px)",
|
||||
expected: ["blur", 20] },
|
||||
{ start: "blur(0px)", end: "blur(20px) url('#a')",
|
||||
expected: ["blur", 20, "url", "\""+document.URL+"#a\""] },
|
||||
{ start: "contrast(0.25) brightness(0.25) blur(25px)",
|
||||
end: "contrast(0.75) url('#a')",
|
||||
expected: ["contrast", 0.75, "url", "\""+document.URL+"#a\""] },
|
||||
{ start: "contrast(0.25) brightness(0.25) blur(75px)",
|
||||
end: "brightness(0.75) contrast(0.75) blur(25px)",
|
||||
expected: ["brightness", 0.75, "contrast", 0.75, "blur", 25] },
|
||||
{ start: "contrast(0.25) brightness(0.25) blur(25px)",
|
||||
end: "contrast(0.75) brightness(0.75) contrast(0.75)",
|
||||
expected: ["contrast", 0.75, "brightness", 0.75, "contrast", 0.75] },
|
||||
// drop-shadow animation
|
||||
{ start: "none",
|
||||
end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
|
||||
expected: ["drop-shadow", "rgba(0, 0, 0, 0.25) 1px 1px 0px"] },
|
||||
{ start: "drop-shadow(rgb(0, 0, 0) 0px 0px 0px)",
|
||||
end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
|
||||
expected: ["drop-shadow", "rgb(0, 0, 0) 1px 1px 0px"] },
|
||||
{ start: "drop-shadow(#038000 4px 4px)",
|
||||
end: "drop-shadow(8px 8px 8px red)",
|
||||
expected: ["drop-shadow", "rgb(66, 96, 0) 5px 5px 2px"] },
|
||||
{ start: "blur(25px) drop-shadow(8px 8px)",
|
||||
end: "blur(75px)",
|
||||
expected: ["blur", 37.5, "drop-shadow", "rgb(0, 0, 0) 6px 6px 0px"] },
|
||||
{ start: "blur(75px)",
|
||||
end: "blur(25px) drop-shadow(8px 8px)",
|
||||
expected: ["blur", 62.5, "drop-shadow", "rgb(0, 0, 0) 2px 2px 0px"] },
|
||||
{ start: "drop-shadow(2px 2px blue)",
|
||||
end: "none",
|
||||
expected: ["drop-shadow", "rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px"] },
|
||||
];
|
||||
|
||||
var prop;
|
||||
for (prop in supported_properties) {
|
||||
// Test that prop is in the property database.
|
||||
@ -995,6 +1146,85 @@ function test_border_color_transition(prop) {
|
||||
div.style.removeProperty("color");
|
||||
}
|
||||
|
||||
function filter_function_list_equals(string, expectedList)
|
||||
{
|
||||
// Check simple case "none"
|
||||
if (string == "none" && string == expectedList[0]) {
|
||||
return true;
|
||||
}
|
||||
// The regular expression does not filter out the last parenthesis.
|
||||
// Remove last character for now.
|
||||
is(string.substring(string.length - 1, string.length), ')', "Check if last character is close-paren.")
|
||||
string = string.substring(0, string.length - 1);
|
||||
var reg = /\)*\s*(blur|brightness|contrast|grayscale|hue\-rotate|invert|opacity|saturate|sepia|drop\-shadow|url)\(/
|
||||
var matches = string.split(reg);
|
||||
// First item must be empty. All other items are of functionName, functionValue.
|
||||
if (!matches || matches.shift() != "") {
|
||||
ok(false, "computed style of 'filter' isn't in the format we expect");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Odd items are the function name, even items the function value.
|
||||
if (!matches.length || matches.length % 2 ||
|
||||
expectedList.length != matches.length) {
|
||||
ok(false, "computed style of 'filter' isn't in the format we expect");
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < matches.length; i += 2) {
|
||||
var functionName = matches[i];
|
||||
var functionValue = matches[i+1];
|
||||
var expected = expectedList[i+1]
|
||||
var tolerance = 0;
|
||||
// Check if we have the expected function.
|
||||
if (functionName != expectedList[i]) {
|
||||
return false;
|
||||
}
|
||||
if (functionName == "blur") {
|
||||
// Last two characters must be "px".
|
||||
if (functionValue.search("px") != functionValue.length - 2) {
|
||||
return false;
|
||||
}
|
||||
functionValue = functionValue.substring(0, functionValue.length - 2);
|
||||
} else if (functionName == "hue-rotate") {
|
||||
// Last two characters must be "rad".
|
||||
if (functionValue.search("rad") != functionValue.length - 3) {
|
||||
return false;
|
||||
}
|
||||
tolerance = 0.001;
|
||||
functionValue = functionValue.substring(0, functionValue.length - 3);
|
||||
} else if (functionName == "drop-shadow" || functionName == "url") {
|
||||
if (functionValue != expected) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Check if string is not a number or difference is not in tolerance level.
|
||||
if (isNaN(functionValue) ||
|
||||
Math.abs(parseFloat(functionValue) - expected) > tolerance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function test_filter_transition(prop) {
|
||||
if (!SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
|
||||
return;
|
||||
}
|
||||
for (var i in filterTests) {
|
||||
var test = filterTests[i];
|
||||
div.style.setProperty("transition-property", "none", "");
|
||||
div.style.setProperty(prop, test.start, "");
|
||||
cs.getPropertyValue(prop);
|
||||
div.style.setProperty("transition-property", prop, "");
|
||||
div.style.setProperty(prop, test.end, "");
|
||||
var actual = cs.getPropertyValue(prop);
|
||||
ok(filter_function_list_equals(actual, test.expected),
|
||||
"Filter property is " + actual + " expected values of " +
|
||||
test.expected);
|
||||
}
|
||||
}
|
||||
|
||||
function test_shadow_transition(prop) {
|
||||
var spreadStr = (prop == "box-shadow") ? " 0px" : "";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user