diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index ee53ad464ea9..38dd3bdc5fea 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -242,6 +242,7 @@ CSS_KEY(disc, disc) CSS_KEY(discretionary-ligatures, discretionary_ligatures) CSS_KEY(dotted, dotted) CSS_KEY(double, double) +CSS_KEY(drop-shadow, drop_shadow) CSS_KEY(e-resize, e_resize) CSS_KEY(each-box, each_box) CSS_KEY(ease, ease) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f9aaad7fd88b..e319805bd3da 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -607,6 +607,7 @@ protected: /* Functions for filter parsing */ bool ParseFilter(); bool ParseSingleFilter(nsCSSValue* aValue); + bool ParseDropShadow(nsCSSValue* aValue); /* Find and return the namespace ID associated with aPrefix. If aPrefix has not been declared in an @namespace rule, returns @@ -10051,6 +10052,32 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) return true; } +/** + * Reads a drop-shadow value. At the moment the Filter Effects specification + * just expects one shadow item. Should this ever change to a list of shadow + * items, use ParseShadowList instead. + */ +bool +CSSParserImpl::ParseDropShadow(nsCSSValue* aValue) +{ + // Use nsCSSValueList to reuse the shadow resolving code in + // nsRuleNode and nsComputedDOMStyle. + nsCSSValue shadow; + nsCSSValueList* cur = shadow.SetListValue(); + if (!ParseShadowItem(cur->mValue, false)) + return false; + + if (!ExpectSymbol(')', true)) + return false; + + nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1); + + // Copy things over. + dropShadow->Item(1) = shadow; + + return true; +} + /** * Reads a single url or filter function from the tokenizer stream, reporting an * error if something goes wrong. @@ -10078,11 +10105,24 @@ CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) return false; } + nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent); + // Parse drop-shadow independently of the other filter functions + // because of its more complex characteristics. + if (functionName == eCSSKeyword_drop_shadow) { + if (ParseDropShadow(aValue)) { + return true; + } else { + // Unrecognized filter function. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + SkipUntil(')'); + 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; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 01c7cbcebd82..05b3fa0a34b9 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -4498,9 +4498,9 @@ nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText, } static void -GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType) +GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type aType) { - switch (mType) { + switch (aType) { case nsStyleFilter::Type::eBlur: aString.AssignLiteral("blur("); break; @@ -4510,6 +4510,9 @@ GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType) case nsStyleFilter::Type::eContrast: aString.AssignLiteral("contrast("); break; + case nsStyleFilter::Type::eDropShadow: + aString.AssignLiteral("drop-shadow("); + break; case nsStyleFilter::Type::eGrayscale: aString.AssignLiteral("grayscale("); break; @@ -4533,25 +4536,34 @@ GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType) } } -nsROCSSPrimitiveValue* +CSSValue* nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter( const nsStyleFilter& aStyleFilter) { nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue; - // Handle url(). - if (nsStyleFilter::Type::eURL == aStyleFilter.mType) { - value->SetURI(aStyleFilter.mURL); + if (nsStyleFilter::Type::eURL == aStyleFilter.GetType()) { + value->SetURI(aStyleFilter.GetURL()); return value; } // Filter function name and opening parenthesis. nsAutoString filterFunctionString; - GetFilterFunctionName(filterFunctionString, aStyleFilter.mType); + GetFilterFunctionName(filterFunctionString, aStyleFilter.GetType()); - // Filter function argument. nsAutoString argumentString; - SetCssTextToCoord(argumentString, aStyleFilter.mFilterParameter); + if (nsStyleFilter::Type::eDropShadow == aStyleFilter.GetType()) { + // Handle drop-shadow() + nsRefPtr shadowValue = + GetCSSShadowArray(aStyleFilter.GetDropShadow(), + StyleColor()->mColor, + false); + ErrorResult dummy; + shadowValue->GetCssText(argumentString, dummy); + } else { + // Filter function argument. + SetCssTextToCoord(argumentString, aStyleFilter.GetFilterParameter()); + } filterFunctionString.Append(argumentString); // Filter function closing parenthesis. @@ -4574,8 +4586,7 @@ nsComputedDOMStyle::DoGetFilter() nsDOMCSSValueList* valueList = GetROCSSValueList(false); for(uint32_t i = 0; i < filters.Length(); i++) { - nsROCSSPrimitiveValue* value = - CreatePrimitiveValueForStyleFilter(filters[i]); + CSSValue* value = CreatePrimitiveValueForStyleFilter(filters[i]); valueList->AppendCSSValue(value); } return valueList; diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index c75265eda958..dc671b30a294 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -494,7 +494,7 @@ private: /* Helper functions for computing the filter property style. */ void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord); - nsROCSSPrimitiveValue* CreatePrimitiveValueForStyleFilter( + mozilla::dom::CSSValue* CreatePrimitiveValueForStyleFilter( const nsStyleFilter& aStyleFilter); struct ComputedStyleMapEntry diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 3c8bd1eda265..cab366d6809b 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -3752,7 +3752,7 @@ already_AddRefed nsRuleNode::GetShadowData(const nsCSSValueList* aList, nsStyleContext* aContext, bool aIsBoxShadow, - bool& canStoreInRuleTree) + bool& aCanStoreInRuleTree) { uint32_t arrayLength = ListLength(aList); @@ -3775,13 +3775,13 @@ nsRuleNode::GetShadowData(const nsCSSValueList* aList, // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT unitOK = SetCoord(arr->Item(0), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, - aContext, mPresContext, canStoreInRuleTree); + aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mXOffset = tempCoord.GetCoordValue(); unitOK = SetCoord(arr->Item(1), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, - aContext, mPresContext, canStoreInRuleTree); + aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mYOffset = tempCoord.GetCoordValue(); @@ -3790,7 +3790,7 @@ nsRuleNode::GetShadowData(const nsCSSValueList* aList, unitOK = SetCoord(arr->Item(2), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE, - aContext, mPresContext, canStoreInRuleTree); + aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mRadius = tempCoord.GetCoordValue(); } else { @@ -3801,7 +3801,7 @@ nsRuleNode::GetShadowData(const nsCSSValueList* aList, if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) { unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, - aContext, mPresContext, canStoreInRuleTree); + aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mSpread = tempCoord.GetCoordValue(); } else { @@ -3812,7 +3812,7 @@ nsRuleNode::GetShadowData(const nsCSSValueList* aList, item->mHasColor = true; // 2nd argument can be bogus since inherit is not a valid color unitOK = SetColor(arr->Item(4), 0, mPresContext, aContext, item->mColor, - canStoreInRuleTree); + aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); } @@ -7722,15 +7722,17 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, } static nsStyleFilter::Type -StyleFilterTypeForFunctionName(nsCSSKeyword functionName) +StyleFilterTypeForFunctionName(nsCSSKeyword aFunctionName) { - switch (functionName) { + switch (aFunctionName) { case eCSSKeyword_blur: return nsStyleFilter::Type::eBlur; case eCSSKeyword_brightness: return nsStyleFilter::Type::eBrightness; case eCSSKeyword_contrast: return nsStyleFilter::Type::eContrast; + case eCSSKeyword_drop_shadow: + return nsStyleFilter::Type::eDropShadow; case eCSSKeyword_grayscale: return nsStyleFilter::Type::eGrayscale; case eCSSKeyword_hue_rotate: @@ -7749,17 +7751,16 @@ StyleFilterTypeForFunctionName(nsCSSKeyword functionName) } } -static void -SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, - const nsCSSValue& aValue, - nsStyleContext* aStyleContext, - nsPresContext* aPresContext, - bool& aCanStoreInRuleTree) +void +nsRuleNode::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(); + aStyleFilter->SetURL(aValue.GetURLValue()); return; } @@ -7768,24 +7769,36 @@ SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, nsCSSValue::Array* filterFunction = aValue.GetArrayValue(); nsCSSKeyword functionName = (nsCSSKeyword)filterFunction->Item(0).GetIntValue(); - aStyleFilter->mType = StyleFilterTypeForFunctionName(functionName); + + nsStyleFilter::Type type = StyleFilterTypeForFunctionName(functionName); + if (type == nsStyleFilter::Type::eDropShadow) { + nsRefPtr shadowArray = GetShadowData( + filterFunction->Item(1).GetListValue(), + aStyleContext, + false, + aCanStoreInRuleTree); + aStyleFilter->SetDropShadow(shadowArray); + return; + } int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR; - if (aStyleFilter->mType == nsStyleFilter::Type::eBlur) { + if (type == nsStyleFilter::Type::eBlur) { mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC; - } else if (aStyleFilter->mType == nsStyleFilter::Type::eHueRotate) { + } else if (type == nsStyleFilter::Type::eHueRotate) { mask = SETCOORD_ANGLE; } NS_ABORT_IF_FALSE(filterFunction->Count() == 2, - "all filter functions except drop-shadow should have " + "all filter functions should have " "exactly one argument"); nsCSSValue& arg = filterFunction->Item(1); - DebugOnly success = SetCoord(arg, aStyleFilter->mFilterParameter, + nsStyleCoord filterParameter; + DebugOnly success = SetCoord(arg, filterParameter, nsStyleCoord(), mask, aStyleContext, aPresContext, aCanStoreInRuleTree); + aStyleFilter->SetFilterParameter(filterParameter, type); NS_ABORT_IF_FALSE(success, "unexpected unit"); } @@ -7884,7 +7897,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, nsStyleFilter styleFilter; SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, mPresContext, canStoreInRuleTree); - NS_ABORT_IF_FALSE(styleFilter.mType != nsStyleFilter::Type::eNull, + NS_ABORT_IF_FALSE(styleFilter.GetType() != nsStyleFilter::Type::eNull, "filter should be set"); svgReset->mFilters.AppendElement(styleFilter); cur = cur->mNext; diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 227eece07d94..7b2f9af59131 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -627,7 +627,12 @@ protected: GetShadowData(const nsCSSValueList* aList, nsStyleContext* aContext, bool aIsBoxShadow, - bool& inherited); + bool& aCanStoreInRuleTree); + void SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, + const nsCSSValue& aValue, + nsStyleContext* aStyleContext, + nsPresContext* aPresContext, + bool& aCanStoreInRuleTree); private: nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 2c7a1be52619..60aef799b5f9 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1011,19 +1011,20 @@ nsStyleFilter::nsStyleFilter() } nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource) - : mType(aSource.mType) { MOZ_COUNT_CTOR(nsStyleFilter); - - if (mType == eURL) { - mURL = aSource.mURL; - } else if (mType != eNull) { - mFilterParameter = aSource.mFilterParameter; + if (aSource.mType == eURL) { + SetURL(aSource.mURL); + } else if (aSource.mType == eDropShadow) { + SetDropShadow(aSource.mDropShadow); + } else if (aSource.mType != eNull) { + SetFilterParameter(aSource.mFilterParameter, aSource.mType); } } nsStyleFilter::~nsStyleFilter() { + ReleaseRef(); MOZ_COUNT_DTOR(nsStyleFilter); } @@ -1036,6 +1037,8 @@ nsStyleFilter::operator==(const nsStyleFilter& aOther) const if (mType == eURL) { return EqualURIs(mURL, aOther.mURL); + } else if (mType == eDropShadow) { + return *mDropShadow == *aOther.mDropShadow; } else if (mType != eNull) { return mFilterParameter == aOther.mFilterParameter; } @@ -1043,6 +1046,47 @@ nsStyleFilter::operator==(const nsStyleFilter& aOther) const return true; } +void +nsStyleFilter::ReleaseRef() +{ + if (mType == eDropShadow) { + NS_ASSERTION(mDropShadow, "expected pointer"); + mDropShadow->Release(); + } else if (mType == eURL) { + NS_ASSERTION(mURL, "expected pointer"); + mURL->Release(); + } +} + +void +nsStyleFilter::SetFilterParameter(const nsStyleCoord& aFilterParameter, + Type aType) +{ + ReleaseRef(); + mFilterParameter = aFilterParameter; + mType = aType; +} + +void +nsStyleFilter::SetURL(nsIURI* aURL) +{ + NS_ASSERTION(aURL, "expected pointer"); + ReleaseRef(); + mURL = aURL; + mURL->AddRef(); + mType = eURL; +} + +void +nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) +{ + NS_ASSERTION(aDropShadow, "expected pointer"); + ReleaseRef(); + mDropShadow = aDropShadow; + mDropShadow->AddRef(); + mType = eDropShadow; +} + // -------------------- // nsStyleSVGReset // diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 473711e09993..9675adfcae81 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -655,7 +655,7 @@ struct nsCSSShadowItem { MOZ_COUNT_DTOR(nsCSSShadowItem); } - bool operator==(const nsCSSShadowItem& aOther) { + bool operator==(const nsCSSShadowItem& aOther) const { return (mXOffset == aOther.mXOffset && mYOffset == aOther.mYOffset && mRadius == aOther.mRadius && @@ -664,7 +664,7 @@ struct nsCSSShadowItem { mInset == aOther.mInset && (!mHasColor || mColor == aOther.mColor)); } - bool operator!=(const nsCSSShadowItem& aOther) { + bool operator!=(const nsCSSShadowItem& aOther) const { return !(*this == aOther); } }; @@ -716,6 +716,18 @@ class nsCSSShadowArray { return false; } + bool operator==(const nsCSSShadowArray& aOther) const { + if (mLength != aOther.Length()) + return false; + + for (uint32_t i = 0; i < mLength; ++i) { + if (ShadowAt(i) != aOther.ShadowAt(i)) + return false; + } + + return true; + } + NS_INLINE_DECL_REFCOUNTING(nsCSSShadowArray) private: @@ -2281,18 +2293,49 @@ struct nsStyleFilter { eBlur, eBrightness, eContrast, + eDropShadow, + eGrayscale, eHueRotate, eInvert, eOpacity, - eGrayscale, eSaturate, eSepia, }; + Type GetType() const { + return mType; + } + + const nsStyleCoord& GetFilterParameter() const { + NS_ASSERTION(mType != eDropShadow && + mType != eURL && + mType != eNull, "wrong filter type"); + return mFilterParameter; + } + void SetFilterParameter(const nsStyleCoord& aFilterParameter, + Type aType); + + nsIURI* GetURL() const { + NS_ASSERTION(mType == eURL, "wrong filter type"); + return mURL; + } + void SetURL(nsIURI* aURL); + + nsCSSShadowArray* GetDropShadow() const { + NS_ASSERTION(mType == eDropShadow, "wrong filter type"); + return mDropShadow; + } + void SetDropShadow(nsCSSShadowArray* aDropShadow); + +private: + void ReleaseRef(); + Type mType; - nsIURI* mURL; nsStyleCoord mFilterParameter; // coord, percent, factor, angle - // FIXME: Add a nsCSSShadowItem when we implement drop shadow. + union { + nsIURI* mURL; + nsCSSShadowArray* mDropShadow; + }; }; struct nsStyleSVGReset { @@ -2318,8 +2361,8 @@ struct nsStyleSVGReset { // filter functions. nsIURI* SingleFilter() const { return (mFilters.Length() == 1 && - mFilters[0].mType == nsStyleFilter::Type::eURL) ? - mFilters[0].mURL : nullptr; + mFilters[0].GetType() == nsStyleFilter::Type::eURL) ? + mFilters[0].GetURL() : nullptr; } nsCOMPtr mClipPath; // [reset] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index f317ff4d3285..922f42be92bd 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -4458,6 +4458,24 @@ if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) { "contrast(350%)", "contrast(4.567)", + "drop-shadow(2px 2px)", + "drop-shadow(2px 2px 1px)", + "drop-shadow(2px 2px green))", + "drop-shadow(2px 2px 1px green)", + "drop-shadow(green 2px 2px)", + "drop-shadow(green 2px 2px 1px)", + "drop-shadow(currentColor 3px 3px)", + "drop-shadow(2px 2px calc(-5px))", /* clamped */ + "drop-shadow(calc(3em - 2px) 2px green)", + "drop-shadow(green calc(3em - 2px) 2px)", + "drop-shadow(2px calc(2px + 0.2em))", + "drop-shadow(blue 2px calc(2px + 0.2em))", + "drop-shadow(2px calc(2px + 0.2em) blue)", + "drop-shadow(calc(-2px) calc(-2px))", + "drop-shadow(-2px -2px)", + "drop-shadow(calc(2px) calc(2px))", + "drop-shadow(calc(2px) calc(2px) calc(2px))", + "grayscale(0)", "grayscale(50%)", "grayscale(1)", @@ -4556,6 +4574,22 @@ if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) { "contrast(10px)", "contrast(-1)", + "drop-shadow()", + "drop-shadow(3% 3%)", + "drop-shadow(2px 2px -5px)", + "drop-shadow(2px 2px 2px 2px)", + "drop-shadow(2px 2px, none)", + "drop-shadow(none, 2px 2px)", + "drop-shadow(inherit, 2px 2px)", + "drop-shadow(2px 2px, inherit)", + "drop-shadow(2 2px)", + "drop-shadow(2px 2)", + "drop-shadow(2px 2px 2)", + "drop-shadow(2px 2px 2px 2)", + "drop-shadow(calc(2px) calc(2px) calc(2px) calc(2px))", + "drop-shadow(green 2px 2px, blue 1px 3px 4px)", + "drop-shadow(blue 2px 2px, currentColor 1px 2px)", + "grayscale()", "grayscale(0.5 0.5)", "grayscale(0.5,)",