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
49e92d12a3
commit
fafa0b24ab
@ -3374,7 +3374,7 @@ CSS_PROP_SVGRESET(
|
|||||||
0,
|
0,
|
||||||
nullptr,
|
nullptr,
|
||||||
CSS_PROP_NO_OFFSET,
|
CSS_PROP_NO_OFFSET,
|
||||||
eStyleAnimType_None)
|
eStyleAnimType_Custom)
|
||||||
CSS_PROP_SVGRESET(
|
CSS_PROP_SVGRESET(
|
||||||
flood-color,
|
flood-color,
|
||||||
flood_color,
|
flood_color,
|
||||||
|
@ -248,6 +248,34 @@ nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
|
|||||||
eCSSUnit_Pixel);
|
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.
|
// Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
|
||||||
struct CalcValue {
|
struct CalcValue {
|
||||||
float mLength, mPercent;
|
float mLength, mPercent;
|
||||||
@ -753,6 +781,8 @@ nsStyleAnimation::ComputeDistance(nsCSSProperty aProperty,
|
|||||||
aDistance = sqrt(squareDistance);
|
aDistance = sqrt(squareDistance);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case eUnit_Filter:
|
||||||
|
// FIXME: Support paced animations for filter function interpolation.
|
||||||
case eUnit_Transform: {
|
case eUnit_Transform: {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1032,6 +1062,38 @@ AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
|
|||||||
return true;
|
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
|
static bool
|
||||||
AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
|
AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
|
||||||
double aCoeff2, const nsCSSValue &aValue2,
|
double aCoeff2, const nsCSSValue &aValue2,
|
||||||
@ -1547,6 +1609,121 @@ TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
|
|||||||
return ToPrimitive(func1) == ToPrimitive(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*
|
static nsCSSValueList*
|
||||||
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
|
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
|
||||||
double aCoeff2, const nsCSSValueList* aList2)
|
double aCoeff2, const nsCSSValueList* aList2)
|
||||||
@ -2060,6 +2237,44 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
|
|||||||
aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
|
aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
|
||||||
return true;
|
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: {
|
case eUnit_Transform: {
|
||||||
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
|
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
|
||||||
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
|
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
|
||||||
@ -2432,6 +2647,7 @@ nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
|
|||||||
} break;
|
} break;
|
||||||
case eUnit_Dasharray:
|
case eUnit_Dasharray:
|
||||||
case eUnit_Shadow:
|
case eUnit_Shadow:
|
||||||
|
case eUnit_Filter:
|
||||||
case eUnit_Transform:
|
case eUnit_Transform:
|
||||||
case eUnit_BackgroundPosition:
|
case eUnit_BackgroundPosition:
|
||||||
aSpecifiedValue.
|
aSpecifiedValue.
|
||||||
@ -2544,12 +2760,27 @@ StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
|
|||||||
case eStyleUnit_Coord:
|
case eStyleUnit_Coord:
|
||||||
nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
|
nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
|
||||||
break;
|
break;
|
||||||
|
case eStyleUnit_Factor:
|
||||||
|
aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
|
||||||
|
break;
|
||||||
case eStyleUnit_Percent:
|
case eStyleUnit_Percent:
|
||||||
aCSSValue.SetPercentValue(aCoord.GetPercentValue());
|
aCSSValue.SetPercentValue(aCoord.GetPercentValue());
|
||||||
break;
|
break;
|
||||||
case eStyleUnit_Calc:
|
case eStyleUnit_Calc:
|
||||||
SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
|
SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
|
||||||
break;
|
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:
|
default:
|
||||||
NS_ABORT_IF_FALSE(false, "unexpected unit");
|
NS_ABORT_IF_FALSE(false, "unexpected unit");
|
||||||
return false;
|
return false;
|
||||||
@ -3012,6 +3243,63 @@ nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
|
|||||||
break;
|
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: {
|
case eCSSProperty_transform: {
|
||||||
const nsStyleDisplay *display =
|
const nsStyleDisplay *display =
|
||||||
static_cast<const nsStyleDisplay*>(styleStruct);
|
static_cast<const nsStyleDisplay*>(styleStruct);
|
||||||
@ -3172,30 +3460,7 @@ nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
|
|||||||
nsAutoPtr<nsCSSValueList> result;
|
nsAutoPtr<nsCSSValueList> result;
|
||||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||||
for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
|
for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
|
||||||
const nsCSSShadowItem *shadow = shadowArray->ShadowAt(i);
|
AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
|
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
|
||||||
eUnit_Shadow);
|
eUnit_Shadow);
|
||||||
@ -3299,12 +3564,14 @@ nsStyleAnimation::Value::operator=(const Value& aOther)
|
|||||||
mUnit = eUnit_Null;
|
mUnit = eUnit_Null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case eUnit_Filter:
|
||||||
case eUnit_Dasharray:
|
case eUnit_Dasharray:
|
||||||
case eUnit_Shadow:
|
case eUnit_Shadow:
|
||||||
case eUnit_Transform:
|
case eUnit_Transform:
|
||||||
case eUnit_BackgroundPosition:
|
case eUnit_BackgroundPosition:
|
||||||
NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || aOther.mValue.mCSSValueList,
|
NS_ABORT_IF_FALSE(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
|
||||||
"value lists other than shadows may not be null");
|
aOther.mValue.mCSSValueList,
|
||||||
|
"value lists other than shadows and filters may not be null");
|
||||||
if (aOther.mValue.mCSSValueList) {
|
if (aOther.mValue.mCSSValueList) {
|
||||||
mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
|
mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
|
||||||
if (!mValue.mCSSValueList) {
|
if (!mValue.mCSSValueList) {
|
||||||
@ -3453,8 +3720,9 @@ nsStyleAnimation::Value::SetAndAdoptCSSValueListValue(
|
|||||||
{
|
{
|
||||||
FreeValue();
|
FreeValue();
|
||||||
NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
|
NS_ABORT_IF_FALSE(IsCSSValueListUnit(aUnit), "bad unit");
|
||||||
NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aValueList != nullptr,
|
NS_ABORT_IF_FALSE(aUnit != eUnit_Dasharray || aUnit != eUnit_Filter ||
|
||||||
"dasharrays may not be null");
|
aValueList != nullptr,
|
||||||
|
"dasharrays and filters may not be null");
|
||||||
mUnit = aUnit;
|
mUnit = aUnit;
|
||||||
mValue.mCSSValueList = aValueList; // take ownership
|
mValue.mCSSValueList = aValueList; // take ownership
|
||||||
}
|
}
|
||||||
@ -3523,6 +3791,7 @@ nsStyleAnimation::Value::operator==(const Value& aOther) const
|
|||||||
case eUnit_CSSRect:
|
case eUnit_CSSRect:
|
||||||
return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
|
return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
|
||||||
case eUnit_Dasharray:
|
case eUnit_Dasharray:
|
||||||
|
case eUnit_Filter:
|
||||||
case eUnit_Shadow:
|
case eUnit_Shadow:
|
||||||
case eUnit_Transform:
|
case eUnit_Transform:
|
||||||
case eUnit_BackgroundPosition:
|
case eUnit_BackgroundPosition:
|
||||||
|
@ -221,6 +221,7 @@ public:
|
|||||||
eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
|
eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
|
||||||
eUnit_CSSRect, // nsCSSRect* (never null)
|
eUnit_CSSRect, // nsCSSRect* (never null)
|
||||||
eUnit_Dasharray, // nsCSSValueList* (never null)
|
eUnit_Dasharray, // nsCSSValueList* (never null)
|
||||||
|
eUnit_Filter, // nsCSSValueList* (may be null)
|
||||||
eUnit_Shadow, // nsCSSValueList* (may be null)
|
eUnit_Shadow, // nsCSSValueList* (may be null)
|
||||||
eUnit_Transform, // nsCSSValueList* (never null)
|
eUnit_Transform, // nsCSSValueList* (never null)
|
||||||
eUnit_BackgroundPosition, // nsCSSValueList* (never null)
|
eUnit_BackgroundPosition, // nsCSSValueList* (never null)
|
||||||
@ -380,8 +381,9 @@ public:
|
|||||||
return aUnit == eUnit_CSSRect;
|
return aUnit == eUnit_CSSRect;
|
||||||
}
|
}
|
||||||
static bool IsCSSValueListUnit(Unit aUnit) {
|
static bool IsCSSValueListUnit(Unit aUnit) {
|
||||||
return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow ||
|
return aUnit == eUnit_Dasharray || aUnit == eUnit_Filter ||
|
||||||
aUnit == eUnit_Transform || aUnit == eUnit_BackgroundPosition;
|
aUnit == eUnit_Shadow || aUnit == eUnit_Transform ||
|
||||||
|
aUnit == eUnit_BackgroundPosition;
|
||||||
}
|
}
|
||||||
static bool IsCSSValuePairListUnit(Unit aUnit) {
|
static bool IsCSSValuePairListUnit(Unit aUnit) {
|
||||||
return aUnit == eUnit_CSSValuePairList;
|
return aUnit == eUnit_CSSValuePairList;
|
||||||
|
@ -122,6 +122,7 @@ var supported_properties = {
|
|||||||
// opacity is clamped in computed style
|
// opacity is clamped in computed style
|
||||||
// (not parsing/interpolation)
|
// (not parsing/interpolation)
|
||||||
test_float_zeroToOne_clamped ],
|
test_float_zeroToOne_clamped ],
|
||||||
|
"filter" : [ test_filter_transition ],
|
||||||
"flood-color": [ test_color_transition ],
|
"flood-color": [ test_color_transition ],
|
||||||
"flood-opacity" : [ test_float_zeroToOne_transition,
|
"flood-opacity" : [ test_float_zeroToOne_transition,
|
||||||
// opacity is clamped in computed style
|
// opacity is clamped in computed style
|
||||||
@ -576,6 +577,156 @@ var transformTests = [
|
|||||||
round_error_ok: true },
|
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;
|
var prop;
|
||||||
for (prop in supported_properties) {
|
for (prop in supported_properties) {
|
||||||
// Test that prop is in the property database.
|
// Test that prop is in the property database.
|
||||||
@ -995,6 +1146,85 @@ function test_border_color_transition(prop) {
|
|||||||
div.style.removeProperty("color");
|
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) {
|
function test_shadow_transition(prop) {
|
||||||
var spreadStr = (prop == "box-shadow") ? " 0px" : "";
|
var spreadStr = (prop == "box-shadow") ? " 0px" : "";
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user