зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
3feda59817
Коммит
0bd2b965c3
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -4964,7 +4964,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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2268,6 +2268,32 @@ 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;
|
||||
nsIURI* mURL;
|
||||
nsStyleCoord mCoord;
|
||||
// FIXME: Add a nsCSSShadowItem when we implement drop shadow.
|
||||
};
|
||||
|
||||
struct nsStyleSVGReset {
|
||||
nsStyleSVGReset();
|
||||
nsStyleSVGReset(const nsStyleSVGReset& aSource);
|
||||
|
@ -2286,8 +2312,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]
|
||||
|
|
|
@ -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)",
|
||||
]
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 *>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче