diff --git a/dom/locales/en-US/chrome/layout/css.properties b/dom/locales/en-US/chrome/layout/css.properties index 964c3fdb4e48..13ed5dca09bc 100644 --- a/dom/locales/en-US/chrome/layout/css.properties +++ b/dom/locales/en-US/chrome/layout/css.properties @@ -56,6 +56,7 @@ PECounterExtendsNotIdent=Expected identifier for extends system but found '%1$S' PECounterASWeight=Each weight in the additive-symbols descriptor must be smaller than the previous weight. PEClassSelEOF=class name PEClassSelNotIdent=Expected identifier for class selector but found '%1$S'. +PECoordinatePair=Expected coordinate pair but found '%1$S'. PETypeSelEOF=element type PETypeSelNotType=Expected element name or '*' but found '%1$S'. PEUnknownNamespacePrefix=Unknown namespace prefix '%1$S'. @@ -106,6 +107,7 @@ PEColorLightnessEOF=lightness PEColorOpacityEOF=opacity in color value PEExpectedNumber=Expected a number but found '%1$S'. PEExpectedCloseParen=Expected ')' but found '%1$S'. +PEClipPathEOF= or reference box PEDeclEndEOF=';' or '}' to end declaration PEParseDeclarationNoColon=Expected ':' but found '%1$S'. PEParseDeclarationDeclExpected=Expected declaration but found '%1$S'. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index af1560b7c23f..37076b0b4c2d 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -607,6 +607,22 @@ nsLayoutUtils::CSSFiltersEnabled() return sCSSFiltersEnabled; } +bool +nsLayoutUtils::CSSClipPathShapesEnabled() +{ + static bool sCSSClipPathShapesEnabled; + static bool sCSSClipPathShapesPrefCached = false; + + if (!sCSSClipPathShapesPrefCached) { + sCSSClipPathShapesPrefCached = true; + Preferences::AddBoolVarCache(&sCSSClipPathShapesEnabled, + "layout.css.clip-path-shapes.enabled", + false); + } + + return sCSSClipPathShapesEnabled; +} + bool nsLayoutUtils::UnsetValueEnabled() { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 01d87e74eb72..2dec2e6a9ef4 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1929,6 +1929,11 @@ public: */ static bool CSSFiltersEnabled(); + /** + * Checks if we should enable parsing for CSS clip-path basic shapes. + */ + static bool CSSClipPathShapesEnabled(); + /** * Checks whether support for the CSS-wide "unset" value is enabled. */ diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index e2636c6dc455..fc6cbf7d3462 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -257,6 +257,7 @@ CSS_KEY(fantasy, fantasy) CSS_KEY(farthest-side, farthest_side) CSS_KEY(farthest-corner, farthest_corner) CSS_KEY(fill, fill) +CSS_KEY(fill-box, fill_box) CSS_KEY(fixed, fixed) CSS_KEY(flat, flat) CSS_KEY(flex, flex) @@ -404,6 +405,7 @@ CSS_KEY(perspective, perspective) CSS_KEY(petite-caps, petite_caps) CSS_KEY(physical, physical) CSS_KEY(pointer, pointer) +CSS_KEY(polygon, polygon) CSS_KEY(portrait, portrait) CSS_KEY(pre, pre) CSS_KEY(pre-wrap, pre_wrap) @@ -503,6 +505,7 @@ CSS_KEY(stretch, stretch) CSS_KEY(stretch-to-fit, stretch_to_fit) CSS_KEY(stretched, stretched) CSS_KEY(stroke, stroke) +CSS_KEY(stroke-box, stroke_box) CSS_KEY(style, style) CSS_KEY(styleset, styleset) CSS_KEY(stylistic, stylistic) @@ -558,6 +561,7 @@ CSS_KEY(vertical, vertical) CSS_KEY(vertical-lr, vertical_lr) CSS_KEY(vertical-rl, vertical_rl) CSS_KEY(vertical-text, vertical_text) +CSS_KEY(view-box, view_box) CSS_KEY(visible, visible) CSS_KEY(visiblefill, visiblefill) CSS_KEY(visiblepainted, visiblepainted) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index adcd3499018c..6197dc69d9df 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -788,6 +788,7 @@ protected: bool ParseListStyleType(nsCSSValue& aValue); bool ParseMargin(); bool ParseMarks(nsCSSValue& aValue); + bool ParseClipPath(); bool ParseTransform(bool aIsPrefixed); bool ParseObjectPosition(); bool ParseOutline(); @@ -961,6 +962,10 @@ protected: return mParsingCompoundProperty; } + /* Functions for basic shapes */ + bool ParseBasicShape(nsCSSValue& aValue); + bool ParsePolygonFunction(nsCSSValue& aValue); + /* Functions for transform Parsing */ bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue); bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[], @@ -9917,6 +9922,8 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) return ParseMarker(); case eCSSProperty_paint_order: return ParsePaintOrder(); + case eCSSProperty_clip_path: + return ParseClipPath(); case eCSSProperty_all: return ParseAll(); default: @@ -13780,6 +13787,125 @@ bool CSSParserImpl::ParseTransform(bool aIsPrefixed) return true; } +/* Reads a polygon function's argument list. + */ +bool +CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue) +{ + uint16_t numArgs = 1; + + nsCSSValue fillRuleValue; + if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) { + numArgs++; + + // The fill-rule must be comma separated from the polygon points. + if (!ExpectSymbol(',', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedComma); + SkipUntil(')'); + return false; + } + } + + nsCSSValue coordinates; + nsCSSValuePairList* item = coordinates.SetPairListValue(); + for (;;) { + nsCSSValue xValue, yValue; + if (!ParseVariant(xValue, VARIANT_LPCALC, nullptr) || + !ParseVariant(yValue, VARIANT_LPCALC, nullptr)) { + REPORT_UNEXPECTED_TOKEN(PECoordinatePair); + SkipUntil(')'); + return false; + } + item->mXValue = xValue; + item->mYValue = yValue; + + // See whether to continue or whether to look for end of function. + if (!ExpectSymbol(',', true)) { + // We need to read the closing parenthesis. + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); + SkipUntil(')'); + return false; + } + break; + } + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + + nsRefPtr functionArray = + aValue.InitFunction(eCSSKeyword_polygon, numArgs); + functionArray->Item(numArgs) = coordinates; + if (numArgs > 1) { + functionArray->Item(1) = fillRuleValue; + } + + return true; +} + +bool +CSSParserImpl::ParseBasicShape(nsCSSValue& aValue) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_Function) { + UngetToken(); + return false; + } + + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + switch (keyword) { + case eCSSKeyword_polygon: + return ParsePolygonFunction(aValue); + default: + return false; + } +} + +/* Parse a clip-path url to a element or a basic shape. */ +bool CSSParserImpl::ParseClipPath() +{ + nsCSSValue value; + if (!ParseVariant(value, VARIANT_HUO, nullptr)) { + if (!nsLayoutUtils::CSSClipPathShapesEnabled()) { + // With CSS Clip Path Shapes disabled, we should only accept + // SVG clipPath reference and none. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL); + return false; + } + + bool shape = false, box = false; + nsCSSValueList* cur = value.SetListValue(); + bool eof = false; + for (int i = 0; i < 2; ++i) { + if (ParseBasicShape(cur->mValue) && !shape) { + shape = true; + } else if (ParseEnum(cur->mValue, nsCSSProps::kClipShapeSizingKTable) && + !box) { + box = true; + } else { + break; + } + if (!GetToken(true)) { + eof = true; + break; + } + UngetToken(); + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + if (!shape && !box && !eof) { + REPORT_UNEXPECTED_EOF(PEClipPathEOF); + return false; + } + } + + AppendValue(eCSSProperty_clip_path, value); + return true; +} + bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) { nsCSSValuePair position; diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index bfcded70bb47..cf57c9e65ed6 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3612,10 +3612,10 @@ CSS_PROP_SVGRESET( clip-path, clip_path, ClipPath, - CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_PARSE_FUNCTION | CSS_PROPERTY_CREATES_STACKING_CONTEXT, "", - VARIANT_HUO, + 0, nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 6fe8017e5b7d..a21332dd5815 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1880,6 +1880,17 @@ const KTableValue nsCSSProps::kFillRuleKTable[] = { eCSSKeyword_UNKNOWN, -1 }; +const KTableValue nsCSSProps::kClipShapeSizingKTable[] = { + eCSSKeyword_content_box, NS_STYLE_CLIP_SHAPE_SIZING_CONTENT, + eCSSKeyword_padding_box, NS_STYLE_CLIP_SHAPE_SIZING_PADDING, + eCSSKeyword_border_box, NS_STYLE_CLIP_SHAPE_SIZING_BORDER, + eCSSKeyword_margin_box, NS_STYLE_CLIP_SHAPE_SIZING_MARGIN, + eCSSKeyword_fill_box, NS_STYLE_CLIP_SHAPE_SIZING_FILL, + eCSSKeyword_stroke_box, NS_STYLE_CLIP_SHAPE_SIZING_STROKE, + eCSSKeyword_view_box, NS_STYLE_CLIP_SHAPE_SIZING_VIEW, + eCSSKeyword_UNKNOWN,-1 +}; + const KTableValue nsCSSProps::kFilterFunctionKTable[] = { eCSSKeyword_blur, NS_STYLE_FILTER_BLUR, eCSSKeyword_brightness, NS_STYLE_FILTER_BRIGHTNESS, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 43223a7da2ae..16cd47546907 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -535,6 +535,7 @@ public: static const KTableValue kBoxDirectionKTable[]; static const KTableValue kBoxOrientKTable[]; static const KTableValue kBoxPackKTable[]; + static const KTableValue kClipShapeSizingKTable[]; static const KTableValue kDominantBaselineKTable[]; static const KTableValue kFillRuleKTable[]; static const KTableValue kFilterFunctionKTable[]; diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index 322b0c67ceaf..b2bcf98e595c 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -826,6 +826,32 @@ private: } // anonymous namespace +void +nsCSSValue::AppendPolygonToString(nsCSSProperty aProperty, nsAString& aResult, + Serialization aSerialization) const +{ + const nsCSSValue::Array* array = GetArrayValue(); + NS_ABORT_IF_FALSE(array->Count() > 1 && array->Count() <= 3, + "Polygons must have name and at least one more value."); + // When the array has 2 elements, the item on index 1 is the coordinate + // pair list. + // When the array has 3 elements, the item on index 1 is a fill-rule + // and item on index 2 is the coordinate pair list. + size_t index = 1; + if (array->Count() == 3) { + const nsCSSValue& fillRuleValue = array->Item(index); + NS_ABORT_IF_FALSE(fillRuleValue.GetUnit() == eCSSUnit_Enumerated, + "Expected polygon fill rule."); + int32_t fillRule = fillRuleValue.GetIntValue(); + AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(fillRule, + nsCSSProps::kFillRuleKTable), + aResult); + aResult.AppendLiteral(", "); + ++index; + } + array->Item(index).AppendToString(aProperty, aResult, aSerialization); +} + void nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult, Serialization aSerialization) const @@ -922,53 +948,61 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult, NS_ABORT_IF_FALSE(array->Count() >= 1, "Functions must have at least one element for the name."); - /* Append the function name. */ const nsCSSValue& functionName = array->Item(0); - if (functionName.GetUnit() == eCSSUnit_Enumerated) { - // We assume that the first argument is always of nsCSSKeyword type. - const nsCSSKeyword functionId = functionName.GetKeywordValue(); - NS_ConvertASCIItoUTF16 ident(nsCSSKeywords::GetStringValue(functionId)); - // Bug 721136: Normalize the identifier to lowercase, except that things - // like scaleX should have the last character capitalized. This matches - // what other browsers do. - switch (functionId) { - case eCSSKeyword_rotatex: - case eCSSKeyword_scalex: - case eCSSKeyword_skewx: - case eCSSKeyword_translatex: - ident.Replace(ident.Length() - 1, 1, char16_t('X')); - break; + NS_ABORT_IF_FALSE(functionName.GetUnit() == eCSSUnit_Enumerated, + "Functions must have an enumerated name."); - case eCSSKeyword_rotatey: - case eCSSKeyword_scaley: - case eCSSKeyword_skewy: - case eCSSKeyword_translatey: - ident.Replace(ident.Length() - 1, 1, char16_t('Y')); - break; + /* Append the function name. */ + // The first argument is always of nsCSSKeyword type. + const nsCSSKeyword functionId = functionName.GetKeywordValue(); + NS_ConvertASCIItoUTF16 ident(nsCSSKeywords::GetStringValue(functionId)); + // Bug 721136: Normalize the identifier to lowercase, except that things + // like scaleX should have the last character capitalized. This matches + // what other browsers do. + switch (functionId) { + case eCSSKeyword_rotatex: + case eCSSKeyword_scalex: + case eCSSKeyword_skewx: + case eCSSKeyword_translatex: + ident.Replace(ident.Length() - 1, 1, char16_t('X')); + break; - case eCSSKeyword_rotatez: - case eCSSKeyword_scalez: - case eCSSKeyword_translatez: - ident.Replace(ident.Length() - 1, 1, char16_t('Z')); - break; + case eCSSKeyword_rotatey: + case eCSSKeyword_scaley: + case eCSSKeyword_skewy: + case eCSSKeyword_translatey: + ident.Replace(ident.Length() - 1, 1, char16_t('Y')); + break; - default: - break; - } - nsStyleUtil::AppendEscapedCSSIdent(ident, aResult); - } else { - MOZ_ASSERT(false, "should no longer have non-enumerated functions"); + case eCSSKeyword_rotatez: + case eCSSKeyword_scalez: + case eCSSKeyword_translatez: + ident.Replace(ident.Length() - 1, 1, char16_t('Z')); + break; + + default: + break; } + nsStyleUtil::AppendEscapedCSSIdent(ident, aResult); aResult.Append('('); - /* Now, step through the function contents, writing each of them as we go. */ - for (size_t index = 1; index < array->Count(); ++index) { - array->Item(index).AppendToString(aProperty, aResult, - aSerialization); + switch (functionId) { + case eCSSKeyword_polygon: + AppendPolygonToString(aProperty, aResult, aSerialization); + break; - /* If we're not at the final element, append a comma. */ - if (index + 1 != array->Count()) - aResult.AppendLiteral(", "); + default: { + // Now, step through the function contents, writing each of + // them as we go. + for (size_t index = 1; index < array->Count(); ++index) { + array->Item(index).AppendToString(aProperty, aResult, + aSerialization); + + /* If we're not at the final element, append a comma. */ + if (index + 1 != array->Count()) + aResult.AppendLiteral(", "); + } + } } /* Finally, append the closing parenthesis. */ @@ -1086,6 +1120,12 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult, aResult); break; + case eCSSProperty_clip_path: + AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue, + nsCSSProps::kClipShapeSizingKTable), + aResult); + break; + default: const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue); AppendASCIItoUTF16(name, aResult); @@ -2045,7 +2085,8 @@ nsCSSValuePairList::AppendToString(nsCSSProperty aProperty, break; if (nsCSSProps::PropHasFlags(aProperty, - CSS_PROPERTY_VALUE_LIST_USES_COMMAS)) + CSS_PROPERTY_VALUE_LIST_USES_COMMAS) || + aProperty == eCSSProperty_clip_path) aResult.Append(char16_t(',')); aResult.Append(char16_t(' ')); } diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 5b617eb481a9..27ff8844b84f 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -719,6 +719,9 @@ private: return static_cast(aBuffer->Data()); } + void AppendPolygonToString(nsCSSProperty aProperty, nsAString& aResult, + Serialization aValueSerialization) const; + protected: nsCSSUnit mUnit; union { diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 0341e7c51084..459bea7368a8 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -5176,18 +5176,84 @@ nsComputedDOMStyle::DoGetStopColor() } CSSValue* -nsComputedDOMStyle::DoGetClipPath() +nsComputedDOMStyle::CreatePrimitiveValueForClipPath( + const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox) { + nsDOMCSSValueList* valueList = GetROCSSValueList(false); + + if (aStyleBasicShape && + aStyleBasicShape->GetShapeType() == nsStyleBasicShape::Type::ePolygon) { + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + + // Shape function name and opening parenthesis. + nsAutoString shapeFunctionString; + AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(eCSSKeyword_polygon), + shapeFunctionString); + shapeFunctionString.Append('('); + uint8_t fillRule = aStyleBasicShape->GetFillRule(); + if (fillRule == NS_STYLE_FILL_RULE_EVENODD) { + shapeFunctionString.AppendLiteral("evenodd"); + } + for (size_t i = 0; i < aStyleBasicShape->Coordinates().Length(); i += 2) { + nsAutoString coordString; + if (i > 0 || fillRule) { + shapeFunctionString.AppendLiteral(", "); + } + SetCssTextToCoord(coordString, + aStyleBasicShape->Coordinates()[i]); + shapeFunctionString.Append(coordString); + shapeFunctionString.Append(' '); + SetCssTextToCoord(coordString, + aStyleBasicShape->Coordinates()[i + 1]); + shapeFunctionString.Append(coordString); + } + shapeFunctionString.Append(')'); + val->SetString(shapeFunctionString); + valueList->AppendCSSValue(val); + } + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + if (aSizingBox == NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) { + return valueList; + } + + nsAutoString boxString; + AppendASCIItoUTF16( + nsCSSProps::ValueToKeyword(aSizingBox, + nsCSSProps::kClipShapeSizingKTable), + boxString); + val->SetString(boxString); + valueList->AppendCSSValue(val); + + return valueList; +} + +CSSValue* +nsComputedDOMStyle::DoGetClipPath() +{ const nsStyleSVGReset* svg = StyleSVGReset(); - - if (svg->mClipPath) - val->SetURI(svg->mClipPath); - else - val->SetIdent(eCSSKeyword_none); - - return val; + switch (svg->mClipPath.GetType()) { + case NS_STYLE_CLIP_PATH_SHAPE: + return CreatePrimitiveValueForClipPath(svg->mClipPath.GetBasicShape(), + svg->mClipPath.GetSizingBox()); + case NS_STYLE_CLIP_PATH_BOX: + return CreatePrimitiveValueForClipPath(nullptr, + svg->mClipPath.GetSizingBox()); + case NS_STYLE_CLIP_PATH_URL: { + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + val->SetURI(svg->mClipPath.GetURL()); + return val; + } + case NS_STYLE_CLIP_PATH_NONE: { + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + val->SetIdent(eCSSKeyword_none); + return val; + } + default: + NS_NOTREACHED("unexpected type"); + } + return nullptr; } void diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 955221940b98..955c8ef09ae2 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -586,6 +586,10 @@ private: mozilla::dom::CSSValue* CreatePrimitiveValueForStyleFilter( const nsStyleFilter& aStyleFilter); + // Helper function for computing basic shape styles. + mozilla::dom::CSSValue* CreatePrimitiveValueForClipPath( + const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox); + static nsComputedStyleMap* GetComputedStyleMap(); // We don't really have a good immutable representation of "presentation". diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 448a60330ed0..675bb19b1ccb 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -8758,6 +8758,86 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, COMPUTE_END_INHERITED(SVG, svg) } +void +nsRuleNode::SetStyleClipPathToCSSValue(nsStyleClipPath* aStyleClipPath, + const nsCSSValue* aValue, + nsStyleContext* aStyleContext, + nsPresContext* aPresContext, + bool& aCanStoreInRuleTree) +{ + NS_ABORT_IF_FALSE(aValue->GetUnit() != eCSSUnit_ListDep || + aValue->GetUnit() != eCSSUnit_List, + "expected a basic shape or reference box"); + + const nsCSSValueList* cur = aValue->GetListValue(); + + uint8_t sizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX; + nsStyleBasicShape* basicShape = nullptr; + for (unsigned i = 0; i < 2; ++i) { + if (!cur) { + break; + } + if (cur->mValue.GetUnit() == eCSSUnit_Function) { + nsCSSValue::Array* shapeFunction = cur->mValue.GetArrayValue(); + nsCSSKeyword functionName = + (nsCSSKeyword)shapeFunction->Item(0).GetIntValue(); + if (functionName == eCSSKeyword_polygon) { + basicShape = new nsStyleBasicShape(nsStyleBasicShape::ePolygon); + NS_ABORT_IF_FALSE(shapeFunction->Count() > 1, + "polygon has wrong number of arguments"); + size_t j = 1; + if (shapeFunction->Item(j).GetUnit() == eCSSUnit_Enumerated) { + basicShape->SetFillRule(shapeFunction->Item(j).GetIntValue()); + ++j; + } + int32_t mask = SETCOORD_PERCENT | SETCOORD_LENGTH | + SETCOORD_STORE_CALC; + const nsCSSValuePairList* curPair = + shapeFunction->Item(j).GetPairListValue(); + nsTArray& coordinates = basicShape->Coordinates(); + while (curPair) { + nsStyleCoord xCoord, yCoord; + DebugOnly didSetCoordX = SetCoord(curPair->mXValue, xCoord, + nsStyleCoord(), mask, + aStyleContext, aPresContext, + aCanStoreInRuleTree); + coordinates.AppendElement(xCoord); + NS_ABORT_IF_FALSE(didSetCoordX, "unexpected x coordinate unit"); + DebugOnly didSetCoordY = SetCoord(curPair->mYValue, yCoord, + nsStyleCoord(), mask, + aStyleContext, aPresContext, + aCanStoreInRuleTree); + coordinates.AppendElement(yCoord); + NS_ABORT_IF_FALSE(didSetCoordY, "unexpected y coordinate unit"); + curPair = curPair->mNext; + } + } else { + // XXX Handle more basic shape functions later. + NS_NOTREACHED("unexpected basic shape function"); + return; + } + } else if (cur->mValue.GetUnit() == eCSSUnit_Enumerated) { + int32_t type = cur->mValue.GetIntValue(); + if (type > NS_STYLE_CLIP_SHAPE_SIZING_VIEW || + type < NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) { + NS_NOTREACHED("unexpected reference box"); + return; + } + sizingBox = (uint8_t)type; + } else { + NS_NOTREACHED("unexpected value"); + return; + } + cur = cur->mNext; + } + + if (basicShape) { + aStyleClipPath->SetBasicShape(basicShape, sizingBox); + } else { + aStyleClipPath->SetSizingBox(sizingBox); + } +} + // Returns true if the nsStyleFilter was successfully set using the nsCSSValue. bool nsRuleNode::SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, @@ -8862,17 +8942,37 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, canStoreInRuleTree); } - // clip-path: url, none, inherit + // clip-path: url, || , none, inherit const nsCSSValue* clipPathValue = aRuleData->ValueForClipPath(); - if (eCSSUnit_URL == clipPathValue->GetUnit()) { - svgReset->mClipPath = clipPathValue->GetURLValue(); - } else if (eCSSUnit_None == clipPathValue->GetUnit() || - eCSSUnit_Initial == clipPathValue->GetUnit() || - eCSSUnit_Unset == clipPathValue->GetUnit()) { - svgReset->mClipPath = nullptr; - } else if (eCSSUnit_Inherit == clipPathValue->GetUnit()) { - canStoreInRuleTree = false; - svgReset->mClipPath = parentSVGReset->mClipPath; + switch (clipPathValue->GetUnit()) { + case eCSSUnit_Null: + break; + case eCSSUnit_None: + case eCSSUnit_Initial: + case eCSSUnit_Unset: + svgReset->mClipPath = nsStyleClipPath(); + break; + case eCSSUnit_Inherit: + canStoreInRuleTree = false; + svgReset->mClipPath = parentSVGReset->mClipPath; + break; + case eCSSUnit_URL: { + svgReset->mClipPath = nsStyleClipPath(); + nsIURI* url = clipPathValue->GetURLValue(); + if (url) { + svgReset->mClipPath.SetURL(url); + } + break; + } + case eCSSUnit_List: + case eCSSUnit_ListDep: { + svgReset->mClipPath = nsStyleClipPath(); + SetStyleClipPathToCSSValue(&svgReset->mClipPath, clipPathValue, aContext, + mPresContext, canStoreInRuleTree); + break; + } + default: + NS_NOTREACHED("unexpected unit"); } // stop-opacity: diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 4b098ecc394d..3529e1be6ce6 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -665,6 +665,11 @@ protected: nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree); + void SetStyleClipPathToCSSValue(nsStyleClipPath* aStyleClipPath, + const nsCSSValue* aValue, + nsStyleContext* aStyleContext, + nsPresContext* aPresContext, + bool& aCanStoreInRuleTree); private: nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent, diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index 3c34550aa624..39c7e0208c43 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -65,6 +65,22 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_BOX_SIZING_PADDING 1 #define NS_STYLE_BOX_SIZING_BORDER 2 +// clip-path sizing +#define NS_STYLE_CLIP_SHAPE_SIZING_NOBOX 0 +#define NS_STYLE_CLIP_SHAPE_SIZING_CONTENT 1 +#define NS_STYLE_CLIP_SHAPE_SIZING_PADDING 2 +#define NS_STYLE_CLIP_SHAPE_SIZING_BORDER 3 +#define NS_STYLE_CLIP_SHAPE_SIZING_MARGIN 4 +#define NS_STYLE_CLIP_SHAPE_SIZING_FILL 5 +#define NS_STYLE_CLIP_SHAPE_SIZING_STROKE 6 +#define NS_STYLE_CLIP_SHAPE_SIZING_VIEW 7 + +// Basic Shapes +#define NS_STYLE_BASIC_SHAPE_POLYGON 0 +//#define NS_STYLE_BASIC_SHAPE_CIRCLE 1 +//#define NS_STYLE_BASIC_SHAPE_ELLIPSE 2 +//#define NS_STYLE_BASIC_SHAPE_INSET 3 + // box-shadow #define NS_STYLE_BOX_SHADOW_INSET 0 @@ -478,6 +494,12 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_FLOAT_LEFT 1 #define NS_STYLE_FLOAT_RIGHT 2 +// See nsStyleClipPath +#define NS_STYLE_CLIP_PATH_NONE 0 +#define NS_STYLE_CLIP_PATH_URL 1 +#define NS_STYLE_CLIP_PATH_SHAPE 2 +#define NS_STYLE_CLIP_PATH_BOX 3 + // See nsStyleFilter #define NS_STYLE_FILTER_NONE 0 #define NS_STYLE_FILTER_URL 1 diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index f803c7bcca07..f5de0aba0d65 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1030,6 +1030,119 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const return hint; } +// -------------------- +// nsStyleClipPath +// +nsStyleClipPath::nsStyleClipPath() + : mType(NS_STYLE_CLIP_PATH_NONE) + , mURL(nullptr) + , mSizingBox(NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) +{ +} + +nsStyleClipPath::nsStyleClipPath(const nsStyleClipPath& aSource) + : mType(NS_STYLE_CLIP_PATH_NONE) + , mURL(nullptr) + , mSizingBox(NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) +{ + if (aSource.mType == NS_STYLE_CLIP_PATH_URL) { + SetURL(aSource.mURL); + } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) { + SetBasicShape(aSource.mBasicShape, aSource.mSizingBox); + } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) { + SetSizingBox(aSource.mSizingBox); + } +} + +nsStyleClipPath::~nsStyleClipPath() +{ + ReleaseRef(); +} + +nsStyleClipPath& +nsStyleClipPath::operator=(const nsStyleClipPath& aOther) +{ + if (this == &aOther) { + return *this; + } + + ReleaseRef(); + + if (aOther.mType == NS_STYLE_CLIP_PATH_URL) { + SetURL(aOther.mURL); + } else if (aOther.mType == NS_STYLE_CLIP_PATH_SHAPE) { + SetBasicShape(aOther.mBasicShape, aOther.mSizingBox); + } else if (aOther.mType == NS_STYLE_CLIP_PATH_BOX) { + SetSizingBox(aOther.mSizingBox); + } else { + mSizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX; + mType = NS_STYLE_CLIP_PATH_NONE; + } + return *this; +} + + +bool +nsStyleClipPath::operator==(const nsStyleClipPath& aOther) const +{ + if (mType != aOther.mType) { + return false; + } + + if (mType == NS_STYLE_CLIP_PATH_URL) { + return EqualURIs(mURL, aOther.mURL); + } else if (mType == NS_STYLE_CLIP_PATH_SHAPE) { + return *mBasicShape == *aOther.mBasicShape && + mSizingBox == aOther.mSizingBox; + } else if (mType == NS_STYLE_CLIP_PATH_BOX) { + return mSizingBox == aOther.mSizingBox; + } + + return true; +} + +void +nsStyleClipPath::ReleaseRef() +{ + if (mType == NS_STYLE_CLIP_PATH_SHAPE) { + NS_ASSERTION(mBasicShape, "expected pointer"); + mBasicShape->Release(); + } else if (mType == NS_STYLE_CLIP_PATH_URL) { + NS_ASSERTION(mURL, "expected pointer"); + mURL->Release(); + } + mURL = nullptr; +} + +void +nsStyleClipPath::SetURL(nsIURI* aURL) +{ + NS_ASSERTION(aURL, "expected pointer"); + ReleaseRef(); + mURL = aURL; + mURL->AddRef(); + mType = NS_STYLE_CLIP_PATH_URL; +} + +void +nsStyleClipPath::SetBasicShape(nsStyleBasicShape* aBasicShape, uint8_t aSizingBox) +{ + NS_ASSERTION(aBasicShape, "expected pointer"); + ReleaseRef(); + mBasicShape = aBasicShape; + mBasicShape->AddRef(); + mSizingBox = aSizingBox; + mType = NS_STYLE_CLIP_PATH_SHAPE; +} + +void +nsStyleClipPath::SetSizingBox(uint8_t aSizingBox) +{ + ReleaseRef(); + mSizingBox = aSizingBox; + mType = NS_STYLE_CLIP_PATH_BOX; +} + // -------------------- // nsStyleFilter // @@ -1145,7 +1258,6 @@ nsStyleSVGReset::nsStyleSVGReset() mStopColor = NS_RGB(0,0,0); mFloodColor = NS_RGB(0,0,0); mLightingColor = NS_RGB(255,255,255); - mClipPath = nullptr; mMask = nullptr; mStopOpacity = 1.0f; mFloodOpacity = 1.0f; @@ -1179,7 +1291,7 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons { nsChangeHint hint = nsChangeHint(0); - if (!EqualURIs(mClipPath, aOther.mClipPath) || + if (mClipPath != aOther.mClipPath || !EqualURIs(mMask, aOther.mMask) || mFilters != aOther.mFilters) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 59db408fd961..869c9aa3527a 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2827,6 +2827,107 @@ struct nsStyleSVG { } }; +class nsStyleBasicShape MOZ_FINAL { +public: + enum Type { + // eInset, + // eCircle, + // eEllipse, + ePolygon + }; + + nsStyleBasicShape(Type type) + : mType(type) + { + } + + Type GetShapeType() const { return mType; } + + int32_t GetFillRule() const { return mFillRule; } + void SetFillRule(int32_t aFillRule) + { + NS_ASSERTION(mType == ePolygon, "expected polygon"); + mFillRule = aFillRule; + } + + nsTArray& Coordinates() + { + NS_ASSERTION(mType == ePolygon, "expected polygon"); + return mCoordinates; + } + + const nsTArray& Coordinates() const + { + NS_ASSERTION(mType == ePolygon, "expected polygon"); + return mCoordinates; + } + + bool operator==(const nsStyleBasicShape& aOther) const + { + return mType == aOther.mType && + mFillRule == aOther.mFillRule && + mCoordinates == aOther.mCoordinates; + } + bool operator!=(const nsStyleBasicShape& aOther) const { + return !(*this == aOther); + } + + NS_INLINE_DECL_REFCOUNTING(nsStyleBasicShape); + +private: + ~nsStyleBasicShape() {} + + Type mType; + int32_t mFillRule; + nsTArray mCoordinates; +}; + +struct nsStyleClipPath +{ + nsStyleClipPath(); + nsStyleClipPath(const nsStyleClipPath& aSource); + ~nsStyleClipPath(); + + nsStyleClipPath& operator=(const nsStyleClipPath& aOther); + + bool operator==(const nsStyleClipPath& aOther) const; + bool operator!=(const nsStyleClipPath& aOther) const { + return !(*this == aOther); + } + + int32_t GetType() const { + return mType; + } + + nsIURI* GetURL() const { + NS_ASSERTION(mType == NS_STYLE_CLIP_PATH_URL, "wrong clip-path type"); + return mURL; + } + void SetURL(nsIURI* aURL); + + nsStyleBasicShape* GetBasicShape() const { + NS_ASSERTION(mType == NS_STYLE_CLIP_PATH_SHAPE, "wrong clip-path type"); + return mBasicShape; + } + + void SetBasicShape(nsStyleBasicShape* mBasicShape, + uint8_t aSizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX); + + uint8_t GetSizingBox() const { return mSizingBox; } + void SetSizingBox(uint8_t aSizingBox); + +private: + void ReleaseRef(); + void* operator new(size_t) MOZ_DELETE; + + int32_t mType; // see NS_STYLE_CLIP_PATH_* constants in nsStyleConsts.h + union { + nsStyleBasicShape* mBasicShape; + nsIURI* mURL; + }; + uint8_t mSizingBox; // see NS_STYLE_CLIP_SHAPE_SIZING_* constants in nsStyleConsts.h +}; + struct nsStyleFilter { nsStyleFilter(); nsStyleFilter(const nsStyleFilter& aSource); @@ -2911,7 +3012,7 @@ struct nsStyleSVGReset { return mFilters.Length() > 0; } - nsCOMPtr mClipPath; // [reset] + nsStyleClipPath mClipPath; // [reset] nsTArray mFilters; // [reset] nsCOMPtr mMask; // [reset] nscolor mStopColor; // [reset] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index f3fe4bcc791c..2ebc61a48aff 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -4584,6 +4584,54 @@ if (SpecialPowers.getBoolPref("svg.paint-order.enabled")) { }; } +if (SpecialPowers.getBoolPref("layout.css.clip-path-shapes.enabled")) { + gCSSProperties["clip-path"] = { + domProp: "clip-path", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + // SVG reference clip-path + "url(#my-clip-path)", + + "polygon(20px 20px)", + "polygon(20px 20%)", + "polygon(20% 20%)", + "polygon(20rem 20em)", + "polygon(20cm 20mm)", + "polygon(20px 20px, 30px 30px)", + "polygon(20px 20px, 30% 30%, 30px 30px)", + "polygon(nonzero, 20px 20px, 30% 30%, 30px 30px)", + "polygon(evenodd, 20px 20px, 30% 30%, 30px 30px)", + + "content-box", + "padding-box", + "border-box", + "margin-box", + "fill-box", + "stroke-box", + "view-box", + + "polygon(0 0) conten-box", + "border-box polygon(0 0)", + "padding-box polygon( 0 20px , 30px 20% ) ", + ], + invalid_values: [ + "url(#test) url(#tes2)", + "polygon (0 0)", + "polygon(20px, 40px)", + "border-box content-box", + "polygon(0 0) polygon(0 0)", + "polygon(nonzero 0 0)", + "polygon(evenodd 20px 20px)", + "polygon(20px 20px, evenodd)", + "polygon(20px 20px, nonzero)", + "polygon(30% 30%", + ] + }; +} + + if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) { gCSSProperties["filter"] = { domProp: "filter", diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index f79203b55c1e..009860a43b5e 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -551,8 +551,12 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) EffectProperties result; const nsStyleSVGReset *style = aFrame->StyleSVGReset(); result.mFilter = GetOrCreateFilterProperty(aFrame); - result.mClipPath = - GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty()); + if (style->mClipPath.GetType() == NS_STYLE_CLIP_PATH_URL) { + result.mClipPath = + GetPaintingProperty(style->mClipPath.GetURL(), aFrame, ClipPathProperty()); + } else { + result.mClipPath = nullptr; + } result.mMask = GetPaintingProperty(style->mMask, aFrame, MaskProperty()); return result; diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 94982c972ff1..b60d7788bf82 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -152,7 +152,8 @@ 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->HasFilters() || style->mClipPath || style->mMask); + return (style->HasFilters() || + style->mClipPath.GetType() != NS_STYLE_CLIP_PATH_NONE || style->mMask); } // For non-SVG frames, this gives the offset to the frame's "user space". diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 1490e6f31188..e29b8aa53141 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2006,6 +2006,9 @@ pref("layout.css.mix-blend-mode.enabled", true); // Is support for CSS Filters enabled? pref("layout.css.filters.enabled", false); +// Is support for basic shapes in clip-path enabled? +pref("layout.css.clip-path-shapes.enabled", false); + // Is support for CSS sticky positioning enabled? pref("layout.css.sticky.enabled", true);