Bug 1072894 - Implement polygon() parsing for clip-path. r=heycam

This commit is contained in:
Dirk Schulze 2014-09-28 01:56:00 +02:00
parent b86ae28eb1
commit a028173cff
21 changed files with 741 additions and 66 deletions

View File

@ -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=<basic-shape> or reference box
PEDeclEndEOF=';' or '}' to end declaration
PEParseDeclarationNoColon=Expected ':' but found '%1$S'.
PEParseDeclarationDeclExpected=Expected declaration but found '%1$S'.

View File

@ -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()
{

View File

@ -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.
*/

View File

@ -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)

View File

@ -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<nsCSSValue::Array> 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 <clipPath> 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;

View File

@ -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)

View File

@ -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,

View File

@ -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[];

View File

@ -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(' '));
}

View File

@ -719,6 +719,9 @@ private:
return static_cast<char16_t*>(aBuffer->Data());
}
void AppendPolygonToString(nsCSSProperty aProperty, nsAString& aResult,
Serialization aValueSerialization) const;
protected:
nsCSSUnit mUnit;
union {

View File

@ -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

View File

@ -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".

View File

@ -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<nsStyleCoord>& coordinates = basicShape->Coordinates();
while (curPair) {
nsStyleCoord xCoord, yCoord;
DebugOnly<bool> didSetCoordX = SetCoord(curPair->mXValue, xCoord,
nsStyleCoord(), mask,
aStyleContext, aPresContext,
aCanStoreInRuleTree);
coordinates.AppendElement(xCoord);
NS_ABORT_IF_FALSE(didSetCoordX, "unexpected x coordinate unit");
DebugOnly<bool> 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, <basic-shape> || <geometry-box>, 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:

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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<nsStyleCoord>& Coordinates()
{
NS_ASSERTION(mType == ePolygon, "expected polygon");
return mCoordinates;
}
const nsTArray<nsStyleCoord>& 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<nsStyleCoord> 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<nsIURI> mClipPath; // [reset]
nsStyleClipPath mClipPath; // [reset]
nsTArray<nsStyleFilter> mFilters; // [reset]
nsCOMPtr<nsIURI> mMask; // [reset]
nscolor mStopColor; // [reset]

View File

@ -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",

View File

@ -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;

View File

@ -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".

View File

@ -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);