Bug 895182 - [CSS Filters] Implement parsing for blur, brightness, contrast, grayscale, invert, opacity, saturate, sepia. Co-authored with Dirk Schulze (krit). r=heycam

This commit is contained in:
Max Vujovic 2013-07-23 10:47:16 -04:00
parent 700cdb0c5b
commit cac859d2f2
18 changed files with 632 additions and 31 deletions

View File

@ -137,3 +137,8 @@ PESupportsConditionExpectedCloseParen=Expected ')' while parsing supports condit
PESupportsConditionExpectedStart2=Expected 'not', '(', or function while parsing supports condition but found '%1$S'.
PESupportsConditionExpectedNot=Expected 'not' while parsing supports condition but found '%1$S'.
PESupportsGroupRuleStart=Expected '{' to begin @supports rule but found '%1$S'.
PEFilterEOF=filter
PEExpectedNoneOrURL=Expected 'none' or URL but found '%1$S'.
PEExpectedNoneOrURLOrFilterFunction=Expected 'none', URL, or filter function but found '%1$S'.
PEExpectedNonnegativeNP=Expected non-negative number or percentage.
PEFilterFunctionArgumentsParsingError=Error in parsing arguments for filter function.

View File

@ -384,6 +384,22 @@ nsLayoutUtils::AnimatedImageLayersEnabled()
return sAnimatedImageLayersEnabled;
}
bool
nsLayoutUtils::CSSFiltersEnabled()
{
static bool sCSSFiltersEnabled;
static bool sCSSFiltersPrefCached = false;
if (!sCSSFiltersPrefCached) {
sCSSFiltersPrefCached = true;
Preferences::AddBoolVarCache(&sCSSFiltersEnabled,
"layout.css.filters.enabled",
false);
}
return sCSSFiltersEnabled;
}
void
nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
nsOverflowAreas& aOverflowAreas)

View File

@ -1622,6 +1622,11 @@ public:
*/
static bool AnimatedImageLayersEnabled();
/**
* Checks if we should enable parsing for CSS Filters.
*/
static bool CSSFiltersEnabled();
/**
* Unions the overflow areas of all non-popup children of aFrame with
* aOverflowAreas.

View File

@ -4961,7 +4961,7 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame,
// For SVG frames, we only need to account for filters.
// TODO: We could also take account of clipPath and mask to reduce the
// visual overflow, but that's not essential.
if (aFrame->StyleSVGReset()->mFilter) {
if (aFrame->StyleSVGReset()->SingleFilter()) {
if (aStoreRectProperties) {
aFrame->Properties().
Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));

View File

@ -182,6 +182,7 @@ CSS_KEY(bidi-override, bidi_override)
CSS_KEY(blink, blink)
CSS_KEY(block, block)
CSS_KEY(block-axis, block_axis)
CSS_KEY(blur, blur)
CSS_KEY(bold, bold)
CSS_KEY(bolder, bolder)
CSS_KEY(border-box, border_box)
@ -191,6 +192,7 @@ CSS_KEY(bottom-outside, bottom_outside)
CSS_KEY(bounding-box, bounding_box)
CSS_KEY(break-all, break_all)
CSS_KEY(break-word, break_word)
CSS_KEY(brightness, brightness)
CSS_KEY(button, button)
CSS_KEY(buttonface, buttonface)
CSS_KEY(buttonhighlight, buttonhighlight)
@ -220,6 +222,7 @@ CSS_KEY(contain, contain)
CSS_KEY(content-box, content_box)
CSS_KEY(context-menu, context_menu)
CSS_KEY(continuous, continuous)
CSS_KEY(contrast, contrast)
CSS_KEY(copy, copy)
CSS_KEY(contextual, contextual)
CSS_KEY(cover, cover)
@ -270,6 +273,7 @@ CSS_KEY(forwards, forwards)
CSS_KEY(full-width, full_width)
CSS_KEY(georgian, georgian)
CSS_KEY(grad, grad)
CSS_KEY(grayscale, grayscale)
CSS_KEY(graytext, graytext)
CSS_KEY(groove, groove)
CSS_KEY(hebrew, hebrew)
@ -306,6 +310,7 @@ CSS_KEY(inline-table, inline_table)
CSS_KEY(inset, inset)
CSS_KEY(inside, inside)
CSS_KEY(interpolatematrix, interpolatematrix)
CSS_KEY(invert, invert)
CSS_KEY(italic, italic)
CSS_KEY(jis78, jis78)
CSS_KEY(jis83, jis83)
@ -369,6 +374,7 @@ CSS_KEY(nw-resize, nw_resize)
CSS_KEY(nwse-resize, nwse_resize)
CSS_KEY(oblique, oblique)
CSS_KEY(oldstyle-nums, oldstyle_nums)
CSS_KEY(opacity, opacity)
CSS_KEY(open-quote, open_quote)
CSS_KEY(ordinal, ordinal)
CSS_KEY(ornaments, ornaments)
@ -418,6 +424,7 @@ CSS_KEY(ruby, ruby)
CSS_KEY(running, running)
CSS_KEY(s, s)
CSS_KEY(s-resize, s_resize)
CSS_KEY(saturate, saturate)
CSS_KEY(scale, scale)
CSS_KEY(scale3d, scale3d)
CSS_KEY(scalex, scalex)
@ -435,6 +442,7 @@ CSS_KEY(select-same, select_same)
CSS_KEY(semi-condensed, semi_condensed)
CSS_KEY(semi-expanded, semi_expanded)
CSS_KEY(separate, separate)
CSS_KEY(sepia, sepia)
CSS_KEY(show, show)
CSS_KEY(simplified, simplified)
CSS_KEY(skew, skew)

View File

@ -604,6 +604,10 @@ protected:
/* Functions for transform-origin/perspective-origin Parsing */
bool ParseTransformOrigin(bool aPerspective);
/* Functions for filter parsing */
bool ParseFilter();
bool ParseSingleFilter(nsCSSValue* aValue);
/* Find and return the namespace ID associated with aPrefix.
If aPrefix has not been declared in an @namespace rule, returns
kNameSpaceID_Unknown. */
@ -6470,6 +6474,8 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
return ParseCounterData(aPropID);
case eCSSProperty_cursor:
return ParseCursor();
case eCSSProperty_filter:
return ParseFilter();
case eCSSProperty_flex:
return ParseFlex();
case eCSSProperty_font:
@ -10032,6 +10038,144 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
return true;
}
/**
* Reads a single url or filter function from the tokenizer stream, reporting an
* error if something goes wrong.
*/
bool
CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
{
if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
return true;
}
if (!nsLayoutUtils::CSSFiltersEnabled()) {
// With CSS Filters disabled, we should only accept an SVG reference filter.
REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
return false;
}
if (!GetToken(true)) {
REPORT_UNEXPECTED_EOF(PEFilterEOF);
return false;
}
if (mToken.mType != eCSSToken_Function) {
REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
return false;
}
// Set up the parsing rules based on the filter function.
int32_t variantMask = VARIANT_PN;
bool rejectNegativeArgument = true;
bool clampArgumentToOne = false;
nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
switch (functionName) {
case eCSSKeyword_blur:
variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
// VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
rejectNegativeArgument = false;
break;
case eCSSKeyword_grayscale:
case eCSSKeyword_invert:
case eCSSKeyword_sepia:
case eCSSKeyword_opacity:
clampArgumentToOne = true;
break;
case eCSSKeyword_brightness:
case eCSSKeyword_contrast:
case eCSSKeyword_saturate:
break;
default:
// Unrecognized filter function.
REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
SkipUntil(')');
return false;
}
// Parse the function.
uint16_t minElems = 1U;
uint16_t maxElems = 1U;
uint32_t allVariants = 0;
if (!ParseFunction(functionName, &variantMask, allVariants,
minElems, maxElems, *aValue)) {
REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
return false;
}
// Get the first and only argument to the filter function.
NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
"expected a filter function");
NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
"filter function should be an array");
NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
"filter function should have exactly one argument");
nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
if (rejectNegativeArgument &&
((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
(arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
return false;
}
if (clampArgumentToOne) {
if (arg.GetUnit() == eCSSUnit_Number &&
arg.GetFloatValue() > 1.0f) {
arg.SetFloatValue(1.0f, arg.GetUnit());
} else if (arg.GetUnit() == eCSSUnit_Percent &&
arg.GetPercentValue() > 1.0f) {
arg.SetPercentValue(1.0f);
}
}
return true;
}
/**
* Parses a filter property value by continuously reading in urls and/or filter
* functions and constructing a list.
*
* When CSS Filters are enabled, the filter property accepts one or more SVG
* reference filters and/or CSS filter functions.
* e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
*
* When CSS Filters are disabled, the filter property only accepts one SVG
* reference filter.
* e.g. filter: url(#my-filter);
*/
bool
CSSParserImpl::ParseFilter()
{
nsCSSValue value;
if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
// 'inherit', 'initial', and 'none' must be alone
if (!ExpectEndProperty()) {
return false;
}
} else {
nsCSSValueList* cur = value.SetListValue();
while (cur) {
if (!ParseSingleFilter(&cur->mValue)) {
return false;
}
if (CheckEndProperty()) {
break;
}
if (!nsLayoutUtils::CSSFiltersEnabled()) {
// With CSS Filters disabled, we should only accept one SVG reference
// filter.
REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
return false;
}
cur->mNext = new nsCSSValueList;
cur = cur->mNext;
}
}
AppendValue(eCSSProperty_filter, value);
return true;
}
bool
CSSParserImpl::ParseTransitionProperty()
{

View File

@ -3327,9 +3327,9 @@ CSS_PROP_SVGRESET(
filter,
filter,
Filter,
CSS_PROPERTY_PARSE_VALUE,
CSS_PROPERTY_PARSE_FUNCTION,
"",
VARIANT_HUO,
0,
nullptr,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)

View File

@ -89,8 +89,9 @@
#define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD)
#define VARIANT_UO (VARIANT_URL | VARIANT_NONE)
#define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
#define VARIANT_LPCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_PERCENT)
#define VARIANT_LNCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_NUMBER)
#define VARIANT_LCALC (VARIANT_LENGTH | VARIANT_CALC)
#define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT)
#define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER)
#define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT)
#define VARIANT_IMAGE (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \
VARIANT_IMAGE_RECT | VARIANT_ELEMENT)

View File

@ -4468,19 +4468,96 @@ nsComputedDOMStyle::DoGetClipPath()
return val;
}
void
nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
const nsStyleCoord& aCoord)
{
nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
bool clampNegativeCalc = true;
SetValueToCoord(value, aCoord, clampNegativeCalc);
value->GetCssText(aCssText);
delete value;
}
static void
GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType)
{
switch (mType) {
case nsStyleFilter::Type::eBlur:
aString.AssignLiteral("blur(");
break;
case nsStyleFilter::Type::eBrightness:
aString.AssignLiteral("brightness(");
break;
case nsStyleFilter::Type::eContrast:
aString.AssignLiteral("contrast(");
break;
case nsStyleFilter::Type::eGrayscale:
aString.AssignLiteral("grayscale(");
break;
case nsStyleFilter::Type::eInvert:
aString.AssignLiteral("invert(");
break;
case nsStyleFilter::Type::eOpacity:
aString.AssignLiteral("opacity(");
break;
case nsStyleFilter::Type::eSaturate:
aString.AssignLiteral("saturate(");
break;
case nsStyleFilter::Type::eSepia:
aString.AssignLiteral("sepia(");
break;
default:
NS_NOTREACHED("unrecognized filter type");
}
}
nsROCSSPrimitiveValue*
nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter(
const nsStyleFilter& aStyleFilter)
{
nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
// Handle url().
if (nsStyleFilter::Type::eURL == aStyleFilter.mType) {
value->SetURI(aStyleFilter.mURL);
return value;
}
// Filter function name and opening parenthesis.
nsAutoString filterFunctionString;
GetFilterFunctionName(filterFunctionString, aStyleFilter.mType);
// Filter function argument.
nsAutoString argumentString;
SetCssTextToCoord(argumentString, aStyleFilter.mCoord);
filterFunctionString.Append(argumentString);
// Filter function closing parenthesis.
filterFunctionString.AppendLiteral(")");
value->SetString(filterFunctionString);
return value;
}
CSSValue*
nsComputedDOMStyle::DoGetFilter()
{
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
const nsTArray<nsStyleFilter>& filters = StyleSVGReset()->mFilters;
const nsStyleSVGReset* svg = StyleSVGReset();
if (filters.IsEmpty()) {
nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue;
value->SetIdent(eCSSKeyword_none);
return value;
}
if (svg->mFilter)
val->SetURI(svg->mFilter);
else
val->SetIdent(eCSSKeyword_none);
return val;
nsDOMCSSValueList* valueList = GetROCSSValueList(false);
for(uint32_t i = 0; i < filters.Length(); i++) {
nsROCSSPrimitiveValue* value =
CreatePrimitiveValueForStyleFilter(filters[i]);
valueList->AppendCSSValue(value);
}
return valueList;
}
CSSValue*

View File

@ -491,6 +491,11 @@ private:
bool GetFrameBorderRectWidth(nscoord& aWidth);
bool GetFrameBorderRectHeight(nscoord& aHeight);
/* Helper functions for computing the filter property style. */
void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord);
nsROCSSPrimitiveValue* CreatePrimitiveValueForStyleFilter(
const nsStyleFilter& aStyleFilter);
struct ComputedStyleMapEntry
{
// Create a pointer-to-member-function type.

View File

@ -7707,6 +7707,68 @@ nsRuleNode::ComputeSVGData(void* aStartStruct,
COMPUTE_END_INHERITED(SVG, svg)
}
static nsStyleFilter::Type
StyleFilterTypeForFunctionName(nsCSSKeyword functionName)
{
switch (functionName) {
case eCSSKeyword_blur:
return nsStyleFilter::Type::eBlur;
case eCSSKeyword_brightness:
return nsStyleFilter::Type::eBrightness;
case eCSSKeyword_contrast:
return nsStyleFilter::Type::eContrast;
case eCSSKeyword_grayscale:
return nsStyleFilter::Type::eGrayscale;
case eCSSKeyword_invert:
return nsStyleFilter::Type::eInvert;
case eCSSKeyword_opacity:
return nsStyleFilter::Type::eOpacity;
case eCSSKeyword_saturate:
return nsStyleFilter::Type::eSaturate;
case eCSSKeyword_sepia:
return nsStyleFilter::Type::eSepia;
default:
NS_NOTREACHED("Unknown filter type.");
return nsStyleFilter::Type::eNull;
}
}
static void
SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
const nsCSSValue& aValue,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
bool& aCanStoreInRuleTree)
{
nsCSSUnit unit = aValue.GetUnit();
if (unit == eCSSUnit_URL) {
aStyleFilter->mType = nsStyleFilter::Type::eURL;
aStyleFilter->mURL = aValue.GetURLValue();
return;
}
NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function");
nsCSSValue::Array* filterFunction = aValue.GetArrayValue();
nsCSSKeyword functionName =
(nsCSSKeyword)filterFunction->Item(0).GetIntValue();
aStyleFilter->mType = StyleFilterTypeForFunctionName(functionName);
int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR;
if (aStyleFilter->mType == nsStyleFilter::Type::eBlur)
mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC;
NS_ABORT_IF_FALSE(filterFunction->Count() == 2,
"all filter functions except drop-shadow should have "
"exactly one argument");
nsCSSValue& arg = filterFunction->Item(1);
DebugOnly<bool> success = SetCoord(arg, aStyleFilter->mCoord, nsStyleCoord(),
mask, aStyleContext, aPresContext,
aCanStoreInRuleTree);
NS_ABORT_IF_FALSE(success, "unexpected unit");
}
const void*
nsRuleNode::ComputeSVGResetData(void* aStartStruct,
const nsRuleData* aRuleData,
@ -7783,14 +7845,34 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct,
// filter: url, none, inherit
const nsCSSValue* filterValue = aRuleData->ValueForFilter();
if (eCSSUnit_URL == filterValue->GetUnit()) {
svgReset->mFilter = filterValue->GetURLValue();
} else if (eCSSUnit_None == filterValue->GetUnit() ||
eCSSUnit_Initial == filterValue->GetUnit()) {
svgReset->mFilter = nullptr;
} else if (eCSSUnit_Inherit == filterValue->GetUnit()) {
canStoreInRuleTree = false;
svgReset->mFilter = parentSVGReset->mFilter;
switch (filterValue->GetUnit()) {
case eCSSUnit_Null:
break;
case eCSSUnit_None:
case eCSSUnit_Initial:
svgReset->mFilters.Clear();
break;
case eCSSUnit_Inherit:
canStoreInRuleTree = false;
svgReset->mFilters = parentSVGReset->mFilters;
break;
case eCSSUnit_List:
case eCSSUnit_ListDep: {
svgReset->mFilters.Clear();
const nsCSSValueList* cur = filterValue->GetListValue();
while(cur) {
nsStyleFilter styleFilter;
SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext,
mPresContext, canStoreInRuleTree);
NS_ABORT_IF_FALSE(styleFilter.mType != nsStyleFilter::Type::eNull,
"filter should be set");
svgReset->mFilters.AppendElement(styleFilter);
cur = cur->mNext;
}
break;
}
default:
NS_NOTREACHED("unexpected unit");
}
// mask: url, none, inherit

View File

@ -1000,6 +1000,48 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const
return hint;
}
// --------------------
// nsStyleFilter
//
nsStyleFilter::nsStyleFilter()
: mType(eNull)
{
MOZ_COUNT_CTOR(nsStyleFilter);
}
nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
: mType(aSource.mType)
{
MOZ_COUNT_CTOR(nsStyleFilter);
if (mType == eURL) {
mURL = aSource.mURL;
} else if (mType != eNull) {
mCoord = aSource.mCoord;
}
}
nsStyleFilter::~nsStyleFilter()
{
MOZ_COUNT_DTOR(nsStyleFilter);
}
bool
nsStyleFilter::operator==(const nsStyleFilter& aOther) const
{
if (mType != aOther.mType) {
return false;
}
if (mType == eURL) {
return EqualURIs(mURL, aOther.mURL);
} else if (mType != eNull) {
return mCoord == aOther.mCoord;
}
return true;
}
// --------------------
// nsStyleSVGReset
//
@ -1010,7 +1052,6 @@ nsStyleSVGReset::nsStyleSVGReset()
mFloodColor = NS_RGB(0,0,0);
mLightingColor = NS_RGB(255,255,255);
mClipPath = nullptr;
mFilter = nullptr;
mMask = nullptr;
mStopOpacity = 1.0f;
mFloodOpacity = 1.0f;
@ -1031,7 +1072,7 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
mFloodColor = aSource.mFloodColor;
mLightingColor = aSource.mLightingColor;
mClipPath = aSource.mClipPath;
mFilter = aSource.mFilter;
mFilters = aSource.mFilters;
mMask = aSource.mMask;
mStopOpacity = aSource.mStopOpacity;
mFloodOpacity = aSource.mFloodOpacity;
@ -1045,8 +1086,8 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons
nsChangeHint hint = nsChangeHint(0);
if (!EqualURIs(mClipPath, aOther.mClipPath) ||
!EqualURIs(mFilter, aOther.mFilter) ||
!EqualURIs(mMask, aOther.mMask)) {
!EqualURIs(mMask, aOther.mMask) ||
mFilters != aOther.mFilters) {
NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
}

View File

@ -2268,6 +2268,34 @@ struct nsStyleSVG {
}
};
struct nsStyleFilter {
nsStyleFilter();
nsStyleFilter(const nsStyleFilter& aSource);
~nsStyleFilter();
bool operator==(const nsStyleFilter& aOther) const;
enum Type {
eNull,
eURL,
eBlur,
eBrightness,
eContrast,
eInvert,
eOpacity,
eGrayscale,
eSaturate,
eSepia,
};
Type mType;
union {
nsIURI* mURL;
nsStyleCoord mCoord;
// FIXME: Add a nsCSSShadowItem when we implement drop shadow.
};
};
struct nsStyleSVGReset {
nsStyleSVGReset();
nsStyleSVGReset(const nsStyleSVGReset& aSource);
@ -2286,8 +2314,17 @@ struct nsStyleSVGReset {
return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_REFLOW);
}
// The backend only supports one SVG reference right now.
// Eventually, it will support multiple chained SVG reference filters and CSS
// filter functions.
nsIURI* SingleFilter() const {
return (mFilters.Length() == 1 &&
mFilters[0].mType == nsStyleFilter::Type::eURL) ?
mFilters[0].mURL : nullptr;
}
nsCOMPtr<nsIURI> mClipPath; // [reset]
nsCOMPtr<nsIURI> mFilter; // [reset]
nsTArray<nsStyleFilter> mFilters; // [reset]
nsCOMPtr<nsIURI> mMask; // [reset]
nscolor mStopColor; // [reset]
nscolor mFloodColor; // [reset]

View File

@ -4411,3 +4411,180 @@ if (SpecialPowers.getBoolPref("svg.paint-order.enabled")) {
invalid_values: [ "fill stroke markers fill", "fill normal" ]
};
}
if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
gCSSProperties["filter"] = {
domProp: "filter",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "none" ],
other_values: [
// SVG reference filters
"url(#my-filter)",
"url(#my-filter-1) url(#my-filter-2)",
// Filter functions
"opacity(50%) saturate(1.0)",
"invert(50%) sepia(0.1) brightness(90%)",
// Mixed SVG reference filters and filter functions
"grayscale(1) url(#my-filter-1)",
"url(#my-filter-1) brightness(50%) contrast(0.9)",
"blur(0)",
"blur(0px)",
"blur(0.5px)",
"blur(3px)",
"blur(100px)",
"blur(0.1em)",
"blur(calc(-1px))", // Parses and becomes blur(0px).
"blur(calc(0px))",
"blur(calc(5px))",
"blur(calc(2 * 5px))",
"brightness(0)",
"brightness(50%)",
"brightness(1)",
"brightness(1.0)",
"brightness(2)",
"brightness(350%)",
"brightness(4.567)",
"contrast(0)",
"contrast(50%)",
"contrast(1)",
"contrast(1.0)",
"contrast(2)",
"contrast(350%)",
"contrast(4.567)",
"grayscale(0)",
"grayscale(50%)",
"grayscale(1)",
"grayscale(1.0)",
"grayscale(2)",
"grayscale(350%)",
"grayscale(4.567)",
"invert(0)",
"invert(50%)",
"invert(1)",
"invert(1.0)",
"invert(2)",
"invert(350%)",
"invert(4.567)",
"opacity(0)",
"opacity(50%)",
"opacity(1)",
"opacity(1.0)",
"opacity(2)",
"opacity(350%)",
"opacity(4.567)",
"saturate(0)",
"saturate(50%)",
"saturate(1)",
"saturate(1.0)",
"saturate(2)",
"saturate(350%)",
"saturate(4.567)",
"sepia(0)",
"sepia(50%)",
"sepia(1)",
"sepia(1.0)",
"sepia(2)",
"sepia(350%)",
"sepia(4.567)",
],
invalid_values: [
// none
"none none",
"url(#my-filter) none",
"none url(#my-filter)",
"blur(2px) none url(#my-filter)",
// Nested filters
"grayscale(invert(1.0))",
// Comma delimited filters
"url(#my-filter),",
"invert(50%), url(#my-filter), brightness(90%)",
// Test the following situations for each filter function:
// - Invalid number of arguments
// - Comma delimited arguments
// - Wrong argument type
// - Argument value out of range
"blur()",
"blur(3px 5px)",
"blur(3px,)",
"blur(3px, 5px)",
"blur(#my-filter)",
"blur(0.5)",
"blur(50%)",
"blur(calc(0))", // Unitless zero in calc is not a valid length.
"blur(calc(0.1))",
"blur(calc(10%))",
"blur(calc(20px - 5%))",
"blur(-3px)",
"brightness()",
"brightness(0.5 0.5)",
"brightness(0.5,)",
"brightness(0.5, 0.5)",
"brightness(#my-filter)",
"brightness(10px)",
"brightness(-1)",
"contrast()",
"contrast(0.5 0.5)",
"contrast(0.5,)",
"contrast(0.5, 0.5)",
"contrast(#my-filter)",
"contrast(10px)",
"contrast(-1)",
"grayscale()",
"grayscale(0.5 0.5)",
"grayscale(0.5,)",
"grayscale(0.5, 0.5)",
"grayscale(#my-filter)",
"grayscale(10px)",
"grayscale(-1)",
"invert()",
"invert(0.5 0.5)",
"invert(0.5,)",
"invert(0.5, 0.5)",
"invert(#my-filter)",
"invert(10px)",
"invert(-1)",
"opacity()",
"opacity(0.5 0.5)",
"opacity(0.5,)",
"opacity(0.5, 0.5)",
"opacity(#my-filter)",
"opacity(10px)",
"opacity(-1)",
"saturate()",
"saturate(0.5 0.5)",
"saturate(0.5,)",
"saturate(0.5, 0.5)",
"saturate(#my-filter)",
"saturate(10px)",
"saturate(-1)",
"sepia()",
"sepia(0.5 0.5)",
"sepia(0.5,)",
"sepia(0.5, 0.5)",
"sepia(#my-filter)",
"sepia(10px)",
"sepia(-1)",
]
};
}

View File

@ -450,7 +450,7 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
EffectProperties result;
const nsStyleSVGReset *style = aFrame->StyleSVGReset();
result.mFilter = static_cast<nsSVGFilterProperty*>
(GetEffectProperty(style->mFilter, aFrame, FilterProperty(),
(GetEffectProperty(style->SingleFilter(), aFrame, FilterProperty(),
CreateFilterProperty));
result.mClipPath =
GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty());
@ -526,7 +526,7 @@ nsSVGEffects::UpdateEffects(nsIFrame *aFrame)
// Ensure that the filter is repainted correctly
// We can't do that in DoUpdate as the referenced frame may not be valid
GetEffectProperty(aFrame->StyleSVGReset()->mFilter,
GetEffectProperty(aFrame->StyleSVGReset()->SingleFilter(),
aFrame, FilterProperty(), CreateFilterProperty);
if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame &&
@ -547,7 +547,7 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
if (!aFrame->StyleSVGReset()->mFilter)
if (!aFrame->StyleSVGReset()->SingleFilter())
return nullptr;
return static_cast<nsSVGFilterProperty *>

View File

@ -150,7 +150,7 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
// checking the SDL prefs here, since we don't know if we're being called for
// painting or hit-testing anyway.
const nsStyleSVGReset *style = aFrame->StyleSVGReset();
return (style->mFilter || style->mClipPath || style->mMask);
return (style->SingleFilter() || style->mClipPath || style->mMask);
}
/* static */ nsPoint

View File

@ -1269,7 +1269,7 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
type != nsGkAtoms::svgPathGeometryFrame) {
return false;
}
if (aFrame->StyleSVGReset()->mFilter) {
if (aFrame->StyleSVGReset()->SingleFilter()) {
return false;
}
// XXX The SVG WG is intending to allow fill, stroke and markers on <image>

View File

@ -1774,6 +1774,9 @@ pref("layout.css.masking.enabled", true);
// Is support for the the @supports rule enabled?
pref("layout.css.supports-rule.enabled", true);
// Is support for CSS Filters enabled?
pref("layout.css.filters.enabled", false);
// Is support for CSS Flexbox enabled?
pref("layout.css.flexbox.enabled", true);