--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -196,16 +196,51 @@ Declaration::GetValue(nsCSSProperty aPro
}
void
Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
{
GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
}
+static void
+AppendSingleImageLayerPositionValue(const nsCSSValue& aPositionX,
+ const nsCSSValue& aPositionY,
+ const nsCSSProperty aTable[],
+ nsAString& aValue,
+ nsCSSValue::Serialization aSerialization)
+{
+ // We need to make sure that we don't serialize to an invalid 3-value form.
+ // The 3-value form is only valid if both edges are present.
+ const nsCSSValue &xEdge = aPositionX.GetArrayValue()->Item(0);
+ const nsCSSValue &xOffset = aPositionX.GetArrayValue()->Item(1);
+ const nsCSSValue &yEdge = aPositionY.GetArrayValue()->Item(0);
+ const nsCSSValue &yOffset = aPositionY.GetArrayValue()->Item(1);
+ bool xHasEdge = (eCSSUnit_Enumerated == xEdge.GetUnit());
+ bool xHasBoth = xHasEdge && (eCSSUnit_Null != xOffset.GetUnit());
+ bool yHasEdge = (eCSSUnit_Enumerated == yEdge.GetUnit());
+ bool yHasBoth = yHasEdge && (eCSSUnit_Null != yOffset.GetUnit());
+
+ if (yHasBoth && !xHasEdge) {
+ // Output 4-value form by adding the x edge.
+ aValue.AppendLiteral("left ");
+ }
+ aPositionX.AppendToString(aTable[nsStyleImageLayers::positionX],
+ aValue, aSerialization);
+
+ aValue.Append(char16_t(' '));
+
+ if (xHasBoth && !yHasEdge) {
+ // Output 4-value form by adding the y edge.
+ aValue.AppendLiteral("top ");
+ }
+ aPositionY.AppendToString(aTable[nsStyleImageLayers::positionY],
+ aValue, aSerialization);
+}
+
void
Declaration::GetImageLayerValue(
nsCSSCompressedDataBlock *data,
nsAString& aValue,
nsCSSValue::Serialization aSerialization,
const nsCSSProperty aTable[]) const
{
// We know from our caller that all subproperties were specified.
@@ -216,18 +251,20 @@ Declaration::GetImageLayerValue(
// background-origin that are different and not the default
// values. (We omit them if they're both default.)
// Common CSS properties for both background & mask layer.
const nsCSSValueList *image =
data->ValueFor(aTable[nsStyleImageLayers::image])->GetListValue();
const nsCSSValuePairList *repeat =
data->ValueFor(aTable[nsStyleImageLayers::repeat])->GetPairListValue();
- const nsCSSValueList *position =
- data->ValueFor(aTable[nsStyleImageLayers::position])->GetListValue();
+ const nsCSSValueList *positionX =
+ data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue();
+ const nsCSSValueList *positionY =
+ data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue();
const nsCSSValueList *clip =
data->ValueFor(aTable[nsStyleImageLayers::clip])->GetListValue();
const nsCSSValueList *origin =
data->ValueFor(aTable[nsStyleImageLayers::origin])->GetListValue();
const nsCSSValuePairList *size =
data->ValueFor(aTable[nsStyleImageLayers::size])->GetPairListValue();
// Background layer property.
@@ -269,18 +306,18 @@ Declaration::GetImageLayerValue(
if (attachment) {
aValue.Append(char16_t(' '));
attachment->mValue.AppendToString(aTable[nsStyleImageLayers::attachment],
aValue, aSerialization);
}
aValue.Append(char16_t(' '));
- position->mValue.AppendToString(aTable[nsStyleImageLayers::position],
- aValue, aSerialization);
+ AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue,
+ aTable, aValue, aSerialization);
if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
size->mYValue.GetUnit() != eCSSUnit_Auto) {
aValue.Append(char16_t(' '));
aValue.Append(char16_t('/'));
aValue.Append(char16_t(' '));
size->mXValue.AppendToString(aTable[nsStyleImageLayers::size], aValue,
aSerialization);
@@ -332,75 +369,112 @@ Declaration::GetImageLayerValue(
if (mode) {
aValue.Append(char16_t(' '));
mode->mValue.AppendToString(aTable[nsStyleImageLayers::maskMode],
aValue, aSerialization);
}
image = image->mNext;
repeat = repeat->mNext;
- position = position->mNext;
+ positionX = positionX->mNext;
+ positionY = positionY->mNext;
clip = clip->mNext;
origin = origin->mNext;
size = size->mNext;
attachment = attachment ? attachment->mNext : nullptr;
composite = composite ? composite->mNext : nullptr;
mode = mode ? mode->mNext : nullptr;
if (!image) {
// This layer is an background layer
if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
- if (repeat || position || clip || origin || size || attachment) {
+ if (repeat || positionX || positionY || clip || origin || size ||
+ attachment) {
// Uneven length lists, so can't be serialized as shorthand.
aValue.Truncate();
return;
}
// This layer is an mask layer
} else {
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
#else
MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
#endif
- if (repeat || position || clip || origin || size || composite || mode) {
+ if (repeat || positionX || positionY || clip || origin || size ||
+ composite || mode) {
// Uneven length lists, so can't be serialized as shorthand.
aValue.Truncate();
return;
}
}
break;
}
// This layer is an background layer
if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
- if (!repeat || !position || !clip || !origin || !size || !attachment) {
+ if (!repeat || !positionX || !positionY || !clip || !origin || !size ||
+ !attachment) {
// Uneven length lists, so can't be serialized as shorthand.
aValue.Truncate();
return;
}
// This layer is an mask layer
} else {
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
#else
MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
#endif
- if (!repeat || !position || !clip || !origin || !size ||
+ if (!repeat || !positionX || !positionY || !clip || !origin || !size ||
!composite || !mode) {
// Uneven length lists, so can't be serialized as shorthand.
aValue.Truncate();
return;
}
}
aValue.Append(char16_t(','));
aValue.Append(char16_t(' '));
}
}
void
+Declaration::GetImageLayerPositionValue(
+ nsCSSCompressedDataBlock *data,
+ nsAString& aValue,
+ nsCSSValue::Serialization aSerialization,
+ const nsCSSProperty aTable[]) const
+{
+ // We know from above that all subproperties were specified.
+ // However, we still can't represent that in the shorthand unless
+ // they're all lists of the same length. So if they're different
+ // lengths, we need to bail out.
+ const nsCSSValueList *positionX =
+ data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue();
+ const nsCSSValueList *positionY =
+ data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue();
+ for (;;) {
+ AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue,
+ aTable, aValue, aSerialization);
+ positionX = positionX->mNext;
+ positionY = positionY->mNext;
+
+ if (!positionX || !positionY) {
+ if (positionX || positionY) {
+ // Uneven length lists, so can't be serialized as shorthand.
+ aValue.Truncate();
+ }
+ return;
+ }
+ aValue.Append(char16_t(','));
+ aValue.Append(char16_t(' '));
+ }
+}
+
+void
Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
nsCSSValue::Serialization aSerialization) const
{
aValue.Truncate(0);
// simple properties are easy.
if (!nsCSSProps::IsShorthand(aProperty)) {
AppendValueToString(aProperty, aValue, aSerialization);
@@ -664,22 +738,32 @@ Declaration::GetValue(nsCSSProperty aPro
}
break;
}
case eCSSProperty_background: {
GetImageLayerValue(data, aValue, aSerialization,
nsStyleImageLayers::kBackgroundLayerTable);
break;
}
+ case eCSSProperty_background_position: {
+ GetImageLayerPositionValue(data, aValue, aSerialization,
+ nsStyleImageLayers::kBackgroundLayerTable);
+ break;
+ }
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
case eCSSProperty_mask: {
GetImageLayerValue(data, aValue, aSerialization,
nsStyleImageLayers::kMaskLayerTable);
break;
}
+ case eCSSProperty_mask_position: {
+ GetImageLayerPositionValue(data, aValue, aSerialization,
+ nsStyleImageLayers::kMaskLayerTable);
+ break;
+ }
#endif
case eCSSProperty_font: {
// systemFont might not be present; other values are guaranteed to be
// available based on the shorthand check at the beginning of the
// function, as long as the prop is enabled
const nsCSSValue *systemFont =
data->ValueFor(eCSSProperty__x_system_font);
const nsCSSValue *style =
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -370,16 +370,21 @@ private:
void AppendVariableAndValueToString(const nsAString& aName,
nsAString& aResult) const;
void GetImageLayerValue(nsCSSCompressedDataBlock *data,
nsAString& aValue,
nsCSSValue::Serialization aSerialization,
const nsCSSProperty aTable[]) const;
+ void GetImageLayerPositionValue(nsCSSCompressedDataBlock *data,
+ nsAString& aValue,
+ nsCSSValue::Serialization aSerialization,
+ const nsCSSProperty aTable[]) const;
+
public:
/**
* Returns the property at the given index in the ordered list of
* declarations. For custom properties, eCSSPropertyExtra_variable
* is returned.
*/
nsCSSProperty GetPropertyAt(uint32_t aIndex) const {
uint32_t value = mOrder[aIndex];
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -860,51 +860,57 @@ protected:
struct ImageLayersShorthandParseState {
nsCSSValue& mColor;
nsCSSValueList* mImage;
nsCSSValuePairList* mRepeat;
nsCSSValueList* mAttachment; // A property for background layer only
nsCSSValueList* mClip;
nsCSSValueList* mOrigin;
- nsCSSValueList* mPosition;
+ nsCSSValueList* mPositionX;
+ nsCSSValueList* mPositionY;
nsCSSValuePairList* mSize;
nsCSSValueList* mComposite; // A property for mask layer only
nsCSSValueList* mMode; // A property for mask layer only
ImageLayersShorthandParseState(
nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
nsCSSValueList* aAttachment, nsCSSValueList* aClip,
- nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
+ nsCSSValueList* aOrigin,
+ nsCSSValueList* aPositionX, nsCSSValueList* aPositionY,
nsCSSValuePairList* aSize, nsCSSValueList* aComposite,
nsCSSValueList* aMode) :
mColor(aColor), mImage(aImage), mRepeat(aRepeat),
mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
- mPosition(aPosition), mSize(aSize), mComposite(aComposite),
+ mPositionX(aPositionX), mPositionY(aPositionY),
+ mSize(aSize), mComposite(aComposite),
mMode(aMode) {};
};
bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const;
bool ParseImageLayersItem(ImageLayersShorthandParseState& aState,
const nsCSSProperty aTable[]);
bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
bool ParseImageLayerRepeat(nsCSSProperty aPropID);
bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue);
- bool ParseImageLayerPosition(nsCSSProperty aPropID);
+ bool ParseImageLayerPosition(const nsCSSProperty aTable[]);
+ bool ParseImageLayerPositionCoord(nsCSSProperty aPropID, bool aIsHorizontal);
// ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
// which is still used by some properties. See ParsePositionValue
// for the css3-background syntax.
bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
bool aAllowExplicitCenter = true); // deprecated
// ParsePositionValue parses a CSS <position> value, which is used by
// the 'background-position' property.
bool ParsePositionValue(nsCSSValue& aOut);
-
+ bool ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY);
+
+ bool ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal);
bool ParseImageLayerSize(nsCSSProperty aPropID);
bool ParseImageLayerSizeValues(nsCSSValuePair& aOut);
bool ParseBorderColor();
bool ParseBorderColors(nsCSSProperty aProperty);
void SetBorderImageInitialValues();
bool ParseBorderImageRepeat(bool aAcceptsInherit);
// If ParseBorderImageSlice returns false, aConsumedTokens indicates
// whether or not any tokens were consumed (in other words, was the property
@@ -11421,17 +11427,21 @@ bool
CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
{
switch (aPropID) { // handle shorthand or multiple properties
case eCSSProperty_background:
return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable);
case eCSSProperty_background_repeat:
return ParseImageLayerRepeat(eCSSProperty_background_repeat);
case eCSSProperty_background_position:
- return ParseImageLayerPosition(eCSSProperty_background_position);
+ return ParseImageLayerPosition(nsStyleImageLayers::kBackgroundLayerTable);
+ case eCSSProperty_background_position_x:
+ case eCSSProperty_background_position_y:
+ return ParseImageLayerPositionCoord(aPropID,
+ aPropID == eCSSProperty_background_position_x);
case eCSSProperty_background_size:
return ParseImageLayerSize(eCSSProperty_background_size);
case eCSSProperty_border:
return ParseBorderSide(kBorderTopIDs, true);
case eCSSProperty_border_color:
return ParseBorderColor();
case eCSSProperty_border_spacing:
return ParseBorderSpacing();
@@ -11603,17 +11613,21 @@ CSSParserImpl::ParsePropertyByFunction(n
case eCSSProperty_scroll_snap_type:
return ParseScrollSnapType();
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
case eCSSProperty_mask:
return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
case eCSSProperty_mask_repeat:
return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
case eCSSProperty_mask_position:
- return ParseImageLayerPosition(eCSSProperty_mask_position);
+ return ParseImageLayerPosition(nsStyleImageLayers::kMaskLayerTable);
+ case eCSSProperty_mask_position_x:
+ case eCSSProperty_mask_position_y:
+ return ParseImageLayerPositionCoord(aPropID,
+ aPropID == eCSSProperty_mask_position_x);
case eCSSProperty_mask_size:
return ParseImageLayerSize(eCSSProperty_mask_size);
#endif
case eCSSProperty__webkit_text_stroke:
return ParseWebkitTextStroke();
case eCSSProperty_all:
return ParseAll();
default:
@@ -11884,22 +11898,23 @@ CSSParserImpl::ParseImageLayers(const ns
for (const nsCSSProperty* subprops =
nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]);
*subprops != eCSSProperty_UNKNOWN; ++subprops) {
AppendValue(*subprops, color);
}
return true;
}
- nsCSSValue image, repeat, attachment, clip, origin, position, size,
+ nsCSSValue image, repeat, attachment, clip, origin, positionX, positionY, size,
composite, maskMode;
ImageLayersShorthandParseState state(color, image.SetListValue(),
repeat.SetPairListValue(),
attachment.SetListValue(), clip.SetListValue(),
- origin.SetListValue(), position.SetListValue(),
+ origin.SetListValue(),
+ positionX.SetListValue(), positionY.SetListValue(),
size.SetPairListValue(), composite.SetListValue(),
maskMode.SetListValue());
for (;;) {
if (!ParseImageLayersItem(state, aTable)) {
return false;
}
@@ -11923,17 +11938,19 @@ CSSParserImpl::ParseImageLayers(const ns
APPENDNEXT(nsStyleImageLayers::image, state.mImage,
nsCSSValueList);
APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat,
nsCSSValuePairList);
APPENDNEXT(nsStyleImageLayers::clip, state.mClip,
nsCSSValueList);
APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin,
nsCSSValueList);
- APPENDNEXT(nsStyleImageLayers::position, state.mPosition,
+ APPENDNEXT(nsStyleImageLayers::positionX, state.mPositionX,
+ nsCSSValueList);
+ APPENDNEXT(nsStyleImageLayers::positionY, state.mPositionY,
nsCSSValueList);
APPENDNEXT(nsStyleImageLayers::size, state.mSize,
nsCSSValuePairList);
APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment,
nsCSSValueList);
APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode,
nsCSSValueList);
APPENDNEXT(nsStyleImageLayers::composite, state.mComposite,
@@ -11952,17 +11969,18 @@ CSSParserImpl::ParseImageLayers(const ns
if (propID_ != eCSSProperty_UNKNOWN) { \
AppendValue(propID_, propValue_); \
}
APPENDVALUE(aTable[nsStyleImageLayers::image], image);
APPENDVALUE(aTable[nsStyleImageLayers::repeat], repeat);
APPENDVALUE(aTable[nsStyleImageLayers::clip], clip);
APPENDVALUE(aTable[nsStyleImageLayers::origin], origin);
- APPENDVALUE(aTable[nsStyleImageLayers::position], position);
+ APPENDVALUE(aTable[nsStyleImageLayers::positionX], positionX);
+ APPENDVALUE(aTable[nsStyleImageLayers::positionY], positionY);
APPENDVALUE(aTable[nsStyleImageLayers::size], size);
APPENDVALUE(aTable[nsStyleImageLayers::color], color);
APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment);
APPENDVALUE(aTable[nsStyleImageLayers::maskMode], maskMode);
APPENDVALUE(aTable[nsStyleImageLayers::composite], composite);
#undef APPENDVALUE
@@ -12011,20 +12029,23 @@ CSSParserImpl::ParseImageLayersItem(
eCSSUnit_Enumerated);
aState.mRepeat->mYValue.Reset();
aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL,
eCSSUnit_Enumerated);
aState.mClip->mValue.SetIntValue(NS_STYLE_IMAGELAYER_CLIP_BORDER,
eCSSUnit_Enumerated);
aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_PADDING,
eCSSUnit_Enumerated);
- RefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
- aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
- positionArr->Item(1).SetPercentValue(0.0f);
- positionArr->Item(3).SetPercentValue(0.0f);
+
+ RefPtr<nsCSSValue::Array> positionXArr = nsCSSValue::Array::Create(2);
+ RefPtr<nsCSSValue::Array> positionYArr = nsCSSValue::Array::Create(2);
+ aState.mPositionX->mValue.SetArrayValue(positionXArr, eCSSUnit_Array);
+ aState.mPositionY->mValue.SetArrayValue(positionYArr, eCSSUnit_Array);
+ positionXArr->Item(1).SetPercentValue(0.0f);
+ positionYArr->Item(1).SetPercentValue(0.0f);
aState.mSize->mXValue.SetAutoValue();
aState.mSize->mYValue.SetAutoValue();
aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
eCSSUnit_Enumerated);
aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
eCSSUnit_Enumerated);
bool haveColor = false,
haveImage = false,
@@ -12087,17 +12108,19 @@ CSSParserImpl::ParseImageLayersItem(
}
aState.mRepeat->mXValue = scratch.mXValue;
aState.mRepeat->mYValue = scratch.mYValue;
} else if (nsCSSProps::FindKeyword(keyword,
nsCSSProps::kImageLayerPositionKTable, dummy)) {
if (havePositionAndSize)
return false;
havePositionAndSize = true;
- if (!ParsePositionValue(aState.mPosition->mValue)) {
+
+ if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
+ aState.mPositionY->mValue)) {
return false;
}
if (ExpectSymbol('/', true)) {
nsCSSValuePair scratch;
if (!ParseImageLayerSizeValues(scratch)) {
return false;
}
aState.mSize->mXValue = scratch.mXValue;
@@ -12199,17 +12222,18 @@ CSSParserImpl::ParseImageLayersItem(
tt == eCSSToken_Number ||
tt == eCSSToken_Percentage ||
(tt == eCSSToken_Function &&
(mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
if (havePositionAndSize)
return false;
havePositionAndSize = true;
- if (!ParsePositionValue(aState.mPosition->mValue)) {
+ if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
+ aState.mPositionY->mValue)) {
return false;
}
if (ExpectSymbol('/', true)) {
nsCSSValuePair scratch;
if (!ParseImageLayerSizeValues(scratch)) {
return false;
}
aState.mSize->mXValue = scratch.mXValue;
@@ -12308,35 +12332,73 @@ CSSParserImpl::ParseImageLayerRepeatValu
yValue.Reset();
}
return true;
}
return false;
}
-// This function is very similar to ParseScrollSnapCoordinate,
-// ParseImageLayers, ParseImageLayerSize.
-bool
-CSSParserImpl::ParseImageLayerPosition(nsCSSProperty aPropID)
+bool
+CSSParserImpl::ParseImageLayerPosition(const nsCSSProperty aTable[])
+{
+ // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
+ nsCSSValue position;
+ if (ParseSingleTokenVariant(position, VARIANT_INHERIT, nullptr)) {
+ AppendValue(aTable[nsStyleImageLayers::positionX], position);
+ AppendValue(aTable[nsStyleImageLayers::positionY], position);
+ return true;
+ }
+
+ nsCSSValue itemValueX;
+ nsCSSValue itemValueY;
+ if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
+ return false;
+ }
+
+ nsCSSValue valueX;
+ nsCSSValue valueY;
+ nsCSSValueList* itemX = valueX.SetListValue();
+ nsCSSValueList* itemY = valueY.SetListValue();
+ for (;;) {
+ itemX->mValue = itemValueX;
+ itemY->mValue = itemValueY;
+ if (!ExpectSymbol(',', true)) {
+ break;
+ }
+ if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
+ return false;
+ }
+ itemX->mNext = new nsCSSValueList;
+ itemY->mNext = new nsCSSValueList;
+ itemX = itemX->mNext;
+ itemY = itemY->mNext;
+ }
+ AppendValue(aTable[nsStyleImageLayers::positionX], valueX);
+ AppendValue(aTable[nsStyleImageLayers::positionY], valueY);
+ return true;
+}
+
+bool
+CSSParserImpl::ParseImageLayerPositionCoord(nsCSSProperty aPropID, bool aIsHorizontal)
{
nsCSSValue value;
// 'initial', 'inherit' and 'unset' stand alone, no list permitted.
if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
nsCSSValue itemValue;
- if (!ParsePositionValue(itemValue)) {
+ if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
return false;
}
nsCSSValueList* item = value.SetListValue();
for (;;) {
item->mValue = itemValue;
if (!ExpectSymbol(',', true)) {
break;
}
- if (!ParsePositionValue(itemValue)) {
+ if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
return false;
}
item->mNext = new nsCSSValueList;
item = item->mNext;
}
}
AppendValue(aPropID, value);
return true;
@@ -12626,16 +12688,90 @@ CSSParserImpl::ParsePositionValue(nsCSSV
xOffset = yOffset;
yEdge = swapEdge;
yOffset = swapOffset;
}
return true;
}
+bool
+CSSParserImpl::ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY)
+{
+ nsCSSValue scratch;
+ if (!ParsePositionValue(scratch)) {
+ return false;
+ }
+
+ // Separate the four values into two pairs of two values for X and Y.
+ RefPtr<nsCSSValue::Array> valueX = nsCSSValue::Array::Create(2);
+ RefPtr<nsCSSValue::Array> valueY = nsCSSValue::Array::Create(2);
+ aOutX.SetArrayValue(valueX, eCSSUnit_Array);
+ aOutY.SetArrayValue(valueY, eCSSUnit_Array);
+
+ RefPtr<nsCSSValue::Array> value = scratch.GetArrayValue();
+ valueX->Item(0) = value->Item(0);
+ valueX->Item(1) = value->Item(1);
+ valueY->Item(0) = value->Item(2);
+ valueY->Item(1) = value->Item(3);
+ return true;
+}
+
+// Parses one item in a list of values for the 'background-position-x' or
+// 'background-position-y' property. Does not support the start/end keywords.
+// Spec reference: https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x
+bool
+CSSParserImpl::ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal)
+{
+ RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(2);
+ aOut.SetArrayValue(value, eCSSUnit_Array);
+
+ nsCSSValue &edge = value->Item(0),
+ &offset = value->Item(1);
+
+ nsCSSValue edgeOrOffset;
+ CSSParseResult result =
+ ParseVariant(edgeOrOffset, VARIANT_LPCALC | VARIANT_KEYWORD,
+ nsCSSProps::kImageLayerPositionKTable);
+ if (result != CSSParseResult::Ok) {
+ return false;
+ }
+
+ if (edgeOrOffset.GetUnit() == eCSSUnit_Enumerated) {
+ edge = edgeOrOffset;
+
+ // The edge can be followed by an optional offset.
+ result = ParseVariant(offset, VARIANT_LPCALC, nullptr);
+ if (result == CSSParseResult::Error) {
+ return false;
+ }
+ } else {
+ offset = edgeOrOffset;
+ }
+
+ // Keywords for horizontal properties cannot be vertical keywords, and
+ // keywords for vertical properties cannot be horizontal keywords.
+ // Also, if an offset is specified, the edge cannot be center.
+ int32_t edgeEnum =
+ edge.GetUnit() == eCSSUnit_Enumerated ? edge.GetIntValue() : 0;
+ int32_t allowedKeywords =
+ (aIsHorizontal ? (BG_LEFT | BG_RIGHT) : (BG_TOP | BG_BOTTOM)) |
+ (offset.GetUnit() == eCSSUnit_Null ? BG_CENTER : 0);
+ if (edgeEnum & ~allowedKeywords) {
+ return false;
+ }
+
+ NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() ||
+ eCSSUnit_Null == edge.GetUnit()) &&
+ eCSSUnit_Enumerated != offset.GetUnit(),
+ "Unexpected units");
+
+ return true;
+}
+
// This function is very similar to ParseScrollSnapCoordinate,
// ParseImageLayers, and ParseImageLayerPosition.
bool
CSSParserImpl::ParseImageLayerSize(nsCSSProperty aPropID)
{
nsCSSValue value;
// 'initial', 'inherit' and 'unset' stand alone, no list permitted.
if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
--- a/layout/style/nsCSSPropAliasList.h
+++ b/layout/style/nsCSSPropAliasList.h
@@ -378,16 +378,24 @@ CSS_PROP_ALIAS(-webkit-mask-image,
CSS_PROP_ALIAS(-webkit-mask-origin,
mask_origin,
WebkitMaskOrigin,
WEBKIT_PREFIX_PREF)
CSS_PROP_ALIAS(-webkit-mask-position,
mask_position,
WebkitMaskPosition,
WEBKIT_PREFIX_PREF)
+CSS_PROP_ALIAS(-webkit-mask-position-x,
+ mask_position_x,
+ WebkitMaskPositionX,
+ WEBKIT_PREFIX_PREF)
+CSS_PROP_ALIAS(-webkit-mask-position-y,
+ mask_position_y,
+ WebkitMaskPositionY,
+ WEBKIT_PREFIX_PREF)
CSS_PROP_ALIAS(-webkit-mask-repeat,
mask_repeat,
WebkitMaskRepeat,
WEBKIT_PREFIX_PREF)
CSS_PROP_ALIAS(-webkit-mask-size,
mask_size,
WebkitMaskSize,
WEBKIT_PREFIX_PREF)
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -562,25 +562,45 @@ CSS_PROP_BACKGROUND(
CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
"",
VARIANT_KEYWORD, // used by list parsing
kImageLayerOriginKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
-CSS_PROP_BACKGROUND(
+CSS_PROP_SHORTHAND(
background-position,
background_position,
BackgroundPosition,
CSS_PROPERTY_PARSE_FUNCTION |
+ CSS_PROPERTY_UNITLESS_LENGTH_QUIRK,
+ "")
+CSS_PROP_BACKGROUND(
+ background-position-x,
+ background_position_x,
+ BackgroundPositionX,
+ CSS_PROPERTY_PARSE_FUNCTION |
CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
- CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
+ CSS_PROPERTY_STORES_CALC,
+ "",
+ 0,
+ kImageLayerPositionKTable,
+ CSS_PROP_NO_OFFSET,
+ eStyleAnimType_Custom)
+CSS_PROP_BACKGROUND(
+ background-position-y,
+ background_position_y,
+ BackgroundPositionY,
+ CSS_PROPERTY_PARSE_FUNCTION |
+ CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
+ CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
+ CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
CSS_PROPERTY_STORES_CALC,
"",
0,
kImageLayerPositionKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_Custom)
CSS_PROP_BACKGROUND(
background-repeat,
@@ -2709,21 +2729,40 @@ CSS_PROP_SVGRESET(
MaskOrigin,
CSS_PROPERTY_PARSE_VALUE_LIST |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
"",
VARIANT_KEYWORD, // used by list parsing
kImageLayerOriginKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
-CSS_PROP_SVGRESET(
+CSS_PROP_SHORTHAND(
mask-position,
mask_position,
MaskPosition,
CSS_PROPERTY_PARSE_FUNCTION |
+ CSS_PROPERTY_UNITLESS_LENGTH_QUIRK,
+ "")
+CSS_PROP_SVGRESET(
+ mask-position-x,
+ mask_position_x,
+ MaskPositionX,
+ CSS_PROPERTY_PARSE_FUNCTION |
+ CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+ CSS_PROPERTY_STORES_CALC,
+ "",
+ 0,
+ kImageLayerPositionKTable,
+ CSS_PROP_NO_OFFSET,
+ eStyleAnimType_Custom)
+CSS_PROP_SVGRESET(
+ mask-position-y,
+ mask_position_y,
+ MaskPositionY,
+ CSS_PROPERTY_PARSE_FUNCTION |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
CSS_PROPERTY_STORES_CALC,
"",
0,
kImageLayerPositionKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_Custom)
CSS_PROP_SVGRESET(
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -2620,23 +2620,30 @@ static const nsCSSProperty gOutlineRadiu
eCSSProperty_UNKNOWN
};
static const nsCSSProperty gBackgroundSubpropTable[] = {
eCSSProperty_background_color,
eCSSProperty_background_image,
eCSSProperty_background_repeat,
eCSSProperty_background_attachment,
- eCSSProperty_background_position,
eCSSProperty_background_clip,
eCSSProperty_background_origin,
+ eCSSProperty_background_position_x,
+ eCSSProperty_background_position_y,
eCSSProperty_background_size,
eCSSProperty_UNKNOWN
};
+static const nsCSSProperty gBackgroundPositionSubpropTable[] = {
+ eCSSProperty_background_position_x,
+ eCSSProperty_background_position_y,
+ eCSSProperty_UNKNOWN
+};
+
static const nsCSSProperty gBorderSubpropTable[] = {
eCSSProperty_border_top_width,
eCSSProperty_border_right_width,
eCSSProperty_border_bottom_width,
eCSSProperty_border_left_width,
eCSSProperty_border_top_style,
eCSSProperty_border_right_style,
eCSSProperty_border_bottom_style,
@@ -2960,24 +2967,30 @@ static const nsCSSProperty gScrollSnapTy
eCSSProperty_scroll_snap_type_x,
eCSSProperty_scroll_snap_type_y,
eCSSProperty_UNKNOWN
};
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
static const nsCSSProperty gMaskSubpropTable[] = {
eCSSProperty_mask_image,
eCSSProperty_mask_repeat,
- eCSSProperty_mask_position,
+ eCSSProperty_mask_position_x,
+ eCSSProperty_mask_position_y,
eCSSProperty_mask_clip,
eCSSProperty_mask_origin,
eCSSProperty_mask_size,
eCSSProperty_mask_composite,
eCSSProperty_mask_mode,
eCSSProperty_UNKNOWN
};
+static const nsCSSProperty gMaskPositionSubpropTable[] = {
+ eCSSProperty_mask_position_x,
+ eCSSProperty_mask_position_y,
+ eCSSProperty_UNKNOWN
+};
#endif
// FIXME: mask-border tables should be added when we implement
// mask-border properties.
const nsCSSProperty *const
nsCSSProps::kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands] = {
#define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
// Need an extra level of macro nesting to force expansion of method_
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -929,17 +929,17 @@ nsCSSValue::AppendCircleOrEllipseToStrin
"unexpected value");
return;
}
if (hasRadii) {
aResult.Append(' ');
}
aResult.AppendLiteral("at ");
- array->Item(count).AppendToString(eCSSProperty_background_position,
+ array->Item(count).AppendToString(eCSSProperty_object_position,
aResult, aSerialization);
}
// Helper to append |aString| with the shorthand sides notation used in e.g.
// 'padding'. |aProperties| and |aValues| are expected to have 4 elements.
/*static*/ void
nsCSSValue::AppendSidesShorthandToString(const nsCSSProperty aProperties[],
const nsCSSValue* aValues[],
@@ -1544,22 +1544,22 @@ nsCSSValue::AppendToString(nsCSSProperty
gradient->mBgPos.mYValue.GetUnit() != eCSSUnit_None) {
MOZ_ASSERT(gradient->mAngle.GetUnit() == eCSSUnit_None);
MOZ_ASSERT(gradient->mBgPos.mXValue.GetUnit() == eCSSUnit_Enumerated &&
gradient->mBgPos.mYValue.GetUnit() == eCSSUnit_Enumerated,
"unexpected unit");
aResult.AppendLiteral("to");
if (!(gradient->mBgPos.mXValue.GetIntValue() & NS_STYLE_IMAGELAYER_POSITION_CENTER)) {
aResult.Append(' ');
- gradient->mBgPos.mXValue.AppendToString(eCSSProperty_background_position,
+ gradient->mBgPos.mXValue.AppendToString(eCSSProperty_background_position_x,
aResult, aSerialization);
}
if (!(gradient->mBgPos.mYValue.GetIntValue() & NS_STYLE_IMAGELAYER_POSITION_CENTER)) {
aResult.Append(' ');
- gradient->mBgPos.mYValue.AppendToString(eCSSProperty_background_position,
+ gradient->mBgPos.mYValue.AppendToString(eCSSProperty_background_position_y,
aResult, aSerialization);
}
needSep = true;
} else if (gradient->mAngle.GetUnit() != eCSSUnit_None) {
gradient->mAngle.AppendToString(aProperty, aResult, aSerialization);
needSep = true;
}
} else if (gradient->mBgPos.mXValue.GetUnit() != eCSSUnit_None ||
@@ -1567,22 +1567,22 @@ nsCSSValue::AppendToString(nsCSSProperty
gradient->mAngle.GetUnit() != eCSSUnit_None) {
if (needSep) {
aResult.Append(' ');
}
if (gradient->mIsRadial && !gradient->mIsLegacySyntax) {
aResult.AppendLiteral("at ");
}
if (gradient->mBgPos.mXValue.GetUnit() != eCSSUnit_None) {
- gradient->mBgPos.mXValue.AppendToString(eCSSProperty_background_position,
+ gradient->mBgPos.mXValue.AppendToString(eCSSProperty_background_position_x,
aResult, aSerialization);
aResult.Append(' ');
}
if (gradient->mBgPos.mYValue.GetUnit() != eCSSUnit_None) {
- gradient->mBgPos.mYValue.AppendToString(eCSSProperty_background_position,
+ gradient->mBgPos.mYValue.AppendToString(eCSSProperty_background_position_y,
aResult, aSerialization);
aResult.Append(' ');
}
if (gradient->mAngle.GetUnit() != eCSSUnit_None) {
MOZ_ASSERT(gradient->mIsLegacySyntax,
"angle is allowed only for legacy syntax");
gradient->mAngle.AppendToString(aProperty, aResult, aSerialization);
}
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2154,19 +2154,24 @@ nsComputedDOMStyle::DoGetImageLayerImage
}
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers)
{
+ if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
+ // No value to return. We can't express this combination of
+ // values as a shorthand.
+ return nullptr;
+ }
+
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
-
- for (uint32_t i = 0, i_end = aLayers.mPositionCount; i < i_end; ++i) {
+ for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
valueList->AppendCSSValue(itemList.forget());
}
return valueList.forget();
}
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6622,16 +6622,19 @@ ComputePositionValue(nsStyleContext* aSt
const nsCSSValue& aValue,
nsStyleImageLayers::Position& aComputedValue,
RuleNodeCacheConditions& aConditions)
{
NS_ASSERTION(aValue.GetUnit() == eCSSUnit_Array,
"unexpected unit for CSS <position> value");
RefPtr<nsCSSValue::Array> positionArray = aValue.GetArrayValue();
+ NS_ASSERTION(positionArray->Count() == 4,
+ "unexpected number of values in CSS <position> value");
+
const nsCSSValue &xEdge = positionArray->Item(0);
const nsCSSValue &xOffset = positionArray->Item(1);
const nsCSSValue &yEdge = positionArray->Item(2);
const nsCSSValue &yOffset = positionArray->Item(3);
NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
eCSSUnit_Null == xEdge.GetUnit()) &&
(eCSSUnit_Enumerated == yEdge.GetUnit() ||
@@ -6644,29 +6647,43 @@ ComputePositionValue(nsStyleContext* aSt
&aComputedValue.mXPosition,
aConditions);
ComputePositionCoord(aStyleContext, yEdge, yOffset,
&aComputedValue.mYPosition,
aConditions);
}
-template <>
-struct BackgroundItemComputer<nsCSSValueList, nsStyleImageLayers::Position>
-{
- static void ComputeValue(nsStyleContext* aStyleContext,
- const nsCSSValueList* aSpecifiedValue,
- nsStyleImageLayers::Position& aComputedValue,
- RuleNodeCacheConditions& aConditions)
- {
- ComputePositionValue(aStyleContext, aSpecifiedValue->mValue,
- aComputedValue, aConditions);
- }
-};
-
+/* Helper function to convert the -x or -y part of a CSS <position> specified
+ * value into its computed-style form. */
+static void
+ComputePositionCoordValue(nsStyleContext* aStyleContext,
+ const nsCSSValue& aValue,
+ nsStyleImageLayers::Position::PositionCoord& aComputedValue,
+ RuleNodeCacheConditions& aConditions)
+{
+ NS_ASSERTION(aValue.GetUnit() == eCSSUnit_Array,
+ "unexpected unit for position coord value");
+
+ RefPtr<nsCSSValue::Array> positionArray = aValue.GetArrayValue();
+ NS_ASSERTION(positionArray->Count() == 2,
+ "unexpected number of values, expecting one edge and one offset");
+
+ const nsCSSValue &edge = positionArray->Item(0);
+ const nsCSSValue &offset = positionArray->Item(1);
+
+ NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() ||
+ eCSSUnit_Null == edge.GetUnit()) &&
+ eCSSUnit_Enumerated != offset.GetUnit(),
+ "Invalid background position");
+
+ ComputePositionCoord(aStyleContext, edge, offset,
+ &aComputedValue,
+ aConditions);
+}
struct BackgroundSizeAxis {
nsCSSValue nsCSSValuePairList::* specified;
nsStyleImageLayers::Size::Dimension nsStyleImageLayers::Size::* result;
uint8_t nsStyleImageLayers::Size::* type;
};
static const BackgroundSizeAxis gBGSizeAxes[] = {
@@ -6822,16 +6839,86 @@ SetImageLayerList(nsStyleContext* aStyle
default:
MOZ_ASSERT(false, "unexpected unit");
}
if (aItemCount > aMaxItemCount)
aMaxItemCount = aItemCount;
}
+// The same as SetImageLayerList, but for values stored in
+// layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
+// This code is duplicated because it would be annoying to make
+// SetImageLayerList generic enough to handle both cases.
+static void
+SetImageLayerPositionCoordList(
+ nsStyleContext* aStyleContext,
+ const nsCSSValue& aValue,
+ nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
+ const nsStyleAutoArray<nsStyleImageLayers::Layer>& aParentLayers,
+ nsStyleImageLayers::Position::PositionCoord
+ nsStyleImageLayers::Position::* aResultLocation,
+ nsStyleImageLayers::Position::PositionCoord aInitialValue,
+ uint32_t aParentItemCount,
+ uint32_t& aItemCount,
+ uint32_t& aMaxItemCount,
+ bool& aRebuild,
+ RuleNodeCacheConditions& aConditions)
+{
+ switch (aValue.GetUnit()) {
+ case eCSSUnit_Null:
+ break;
+
+ case eCSSUnit_Inherit:
+ aRebuild = true;
+ aConditions.SetUncacheable();
+ aLayers.EnsureLengthAtLeast(aParentItemCount);
+ aItemCount = aParentItemCount;
+ for (uint32_t i = 0; i < aParentItemCount; ++i) {
+ aLayers[i].mPosition.*aResultLocation = aParentLayers[i].mPosition.*aResultLocation;
+ }
+ break;
+
+ case eCSSUnit_Initial:
+ case eCSSUnit_Unset:
+ aRebuild = true;
+ aItemCount = 1;
+ aLayers[0].mPosition.*aResultLocation = aInitialValue;
+ break;
+
+ case eCSSUnit_List:
+ case eCSSUnit_ListDep: {
+ aRebuild = true;
+ aItemCount = 0;
+ const nsCSSValueList* item = aValue.GetListValue();
+ do {
+ NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null &&
+ item->mValue.GetUnit() != eCSSUnit_Inherit &&
+ item->mValue.GetUnit() != eCSSUnit_Initial &&
+ item->mValue.GetUnit() != eCSSUnit_Unset,
+ "unexpected unit");
+ ++aItemCount;
+ aLayers.EnsureLengthAtLeast(aItemCount);
+
+ ComputePositionCoordValue(aStyleContext, item->mValue,
+ aLayers[aItemCount-1].mPosition.*aResultLocation,
+ aConditions);
+ item = item->mNext;
+ } while (item);
+ break;
+ }
+
+ default:
+ MOZ_ASSERT(false, "unexpected unit");
+ }
+
+ if (aItemCount > aMaxItemCount)
+ aMaxItemCount = aItemCount;
+}
+
template <class ComputedValueItem>
static void
SetImageLayerPairList(nsStyleContext* aStyleContext,
const nsCSSValue& aValue,
nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
const nsStyleAutoArray<nsStyleImageLayers::Layer>& aParentLayers,
ComputedValueItem nsStyleImageLayers::Layer::*
aResultLocation,
@@ -6906,16 +6993,34 @@ FillBackgroundList(
for (uint32_t sourceLayer = 0, destLayer = aItemCount;
destLayer < aFillCount;
++sourceLayer, ++destLayer) {
aLayers[destLayer].*aResultLocation =
aLayers[sourceLayer].*aResultLocation;
}
}
+// The same as FillBackgroundList, but for values stored in
+// layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
+static void
+FillBackgroundPositionCoordList(
+ nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
+ nsStyleImageLayers::Position::PositionCoord
+ nsStyleImageLayers::Position::* aResultLocation,
+ uint32_t aItemCount, uint32_t aFillCount)
+{
+ NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length");
+ for (uint32_t sourceLayer = 0, destLayer = aItemCount;
+ destLayer < aFillCount;
+ ++sourceLayer, ++destLayer) {
+ aLayers[destLayer].mPosition.*aResultLocation =
+ aLayers[sourceLayer].mPosition.*aResultLocation;
+ }
+}
+
const void*
nsRuleNode::ComputeBackgroundData(void* aStartStruct,
const nsRuleData* aRuleData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail,
const RuleNodeCacheConditions aConditions)
{
@@ -6990,25 +7095,37 @@ nsRuleNode::ComputeBackgroundData(void*
bg->mImage.mLayers,
parentBG->mImage.mLayers,
&nsStyleImageLayers::Layer::mOrigin,
uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
parentBG->mImage.mOriginCount,
bg->mImage.mOriginCount, maxItemCount, rebuild,
conditions);
- // background-position: enum, length, percent (flags), inherit [pair list]
- nsStyleImageLayers::Position initialPosition;
- initialPosition.SetInitialPercentValues(0.0f);
- SetImageLayerList(aContext, *aRuleData->ValueForBackgroundPosition(),
+ // background-position-x/y: enum, length, percent (flags), inherit [list]
+ nsStyleImageLayers::Position::PositionCoord initialPositionCoord;
+ initialPositionCoord.mPercent = 0.0f;
+ initialPositionCoord.mLength = 0;
+ initialPositionCoord.mHasPercent = true;
+
+ SetImageLayerPositionCoordList(
+ aContext, *aRuleData->ValueForBackgroundPositionX(),
bg->mImage.mLayers,
parentBG->mImage.mLayers,
- &nsStyleImageLayers::Layer::mPosition,
- initialPosition, parentBG->mImage.mPositionCount,
- bg->mImage.mPositionCount, maxItemCount, rebuild,
+ &nsStyleImageLayers::Position::mXPosition,
+ initialPositionCoord, parentBG->mImage.mPositionXCount,
+ bg->mImage.mPositionXCount, maxItemCount, rebuild,
+ conditions);
+ SetImageLayerPositionCoordList(
+ aContext, *aRuleData->ValueForBackgroundPositionY(),
+ bg->mImage.mLayers,
+ parentBG->mImage.mLayers,
+ &nsStyleImageLayers::Position::mYPosition,
+ initialPositionCoord, parentBG->mImage.mPositionYCount,
+ bg->mImage.mPositionYCount, maxItemCount, rebuild,
conditions);
// background-size: enum, length, auto, inherit, initial [pair list]
nsStyleImageLayers::Size initialSize;
initialSize.SetInitialValues();
SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundSize(),
bg->mImage.mLayers,
parentBG->mImage.mLayers,
@@ -7036,19 +7153,22 @@ nsRuleNode::ComputeBackgroundData(void*
&nsStyleImageLayers::Layer::mClip,
bg->mImage.mClipCount, fillCount);
FillBackgroundList(bg->mImage.mLayers,
&nsStyleImageLayers::Layer::mBlendMode,
bg->mImage.mBlendModeCount, fillCount);
FillBackgroundList(bg->mImage.mLayers,
&nsStyleImageLayers::Layer::mOrigin,
bg->mImage.mOriginCount, fillCount);
- FillBackgroundList(bg->mImage.mLayers,
- &nsStyleImageLayers::Layer::mPosition,
- bg->mImage.mPositionCount, fillCount);
+ FillBackgroundPositionCoordList(bg->mImage.mLayers,
+ &nsStyleImageLayers::Position::mXPosition,
+ bg->mImage.mPositionXCount, fillCount);
+ FillBackgroundPositionCoordList(bg->mImage.mLayers,
+ &nsStyleImageLayers::Position::mYPosition,
+ bg->mImage.mPositionYCount, fillCount);
FillBackgroundList(bg->mImage.mLayers,
&nsStyleImageLayers::Layer::mSize,
bg->mImage.mSizeCount, fillCount);
}
// Now that the dust has settled, register the images with the document
bg->mImage.TrackImages(aContext->PresContext());
@@ -9787,25 +9907,37 @@ nsRuleNode::ComputeSVGResetData(void* aS
svgReset->mMask.mLayers,
parentSVGReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mOrigin,
uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
parentSVGReset->mMask.mOriginCount,
svgReset->mMask.mOriginCount, maxItemCount, rebuild,
conditions);
- // mask-position: enum, length, percent (flags), inherit [pair list]
- nsStyleImageLayers::Position initialPosition;
- initialPosition.SetInitialPercentValues(0.0f);
- SetImageLayerList(aContext, *aRuleData->ValueForMaskPosition(),
+ // mask-position-x/y: enum, length, percent (flags), inherit [list]
+ nsStyleImageLayers::Position::PositionCoord initialPositionCoord;
+ initialPositionCoord.mPercent = 0.0f;
+ initialPositionCoord.mLength = 0;
+ initialPositionCoord.mHasPercent = true;
+
+ SetImageLayerPositionCoordList(
+ aContext, *aRuleData->ValueForMaskPositionX(),
svgReset->mMask.mLayers,
parentSVGReset->mMask.mLayers,
- &nsStyleImageLayers::Layer::mPosition,
- initialPosition, parentSVGReset->mMask.mPositionCount,
- svgReset->mMask.mPositionCount, maxItemCount, rebuild,
+ &nsStyleImageLayers::Position::mXPosition,
+ initialPositionCoord, parentSVGReset->mMask.mPositionXCount,
+ svgReset->mMask.mPositionXCount, maxItemCount, rebuild,
+ conditions);
+ SetImageLayerPositionCoordList(
+ aContext, *aRuleData->ValueForMaskPositionY(),
+ svgReset->mMask.mLayers,
+ parentSVGReset->mMask.mLayers,
+ &nsStyleImageLayers::Position::mYPosition,
+ initialPositionCoord, parentSVGReset->mMask.mPositionYCount,
+ svgReset->mMask.mPositionYCount, maxItemCount, rebuild,
conditions);
// mask-size: enum, length, auto, inherit, initial [pair list]
nsStyleImageLayers::Size initialSize;
initialSize.SetInitialValues();
SetImageLayerPairList(aContext, *aRuleData->ValueForMaskSize(),
svgReset->mMask.mLayers,
parentSVGReset->mMask.mLayers,
@@ -9849,19 +9981,22 @@ nsRuleNode::ComputeSVGResetData(void* aS
&nsStyleImageLayers::Layer::mRepeat,
svgReset->mMask.mRepeatCount, fillCount);
FillBackgroundList(svgReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mClip,
svgReset->mMask.mClipCount, fillCount);
FillBackgroundList(svgReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mOrigin,
svgReset->mMask.mOriginCount, fillCount);
- FillBackgroundList(svgReset->mMask.mLayers,
- &nsStyleImageLayers::Layer::mPosition,
- svgReset->mMask.mPositionCount, fillCount);
+ FillBackgroundPositionCoordList(svgReset->mMask.mLayers,
+ &nsStyleImageLayers::Position::mXPosition,
+ svgReset->mMask.mPositionXCount, fillCount);
+ FillBackgroundPositionCoordList(svgReset->mMask.mLayers,
+ &nsStyleImageLayers::Position::mYPosition,
+ svgReset->mMask.mPositionYCount, fillCount);
FillBackgroundList(svgReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mSize,
svgReset->mMask.mSizeCount, fillCount);
FillBackgroundList(svgReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mMaskMode,
svgReset->mMask.mMaskModeCount, fillCount);
FillBackgroundList(svgReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mComposite,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2276,80 +2276,85 @@ nsStyleImage::operator==(const nsStyleIm
// nsStyleImageLayers
//
const nsCSSProperty nsStyleImageLayers::kBackgroundLayerTable[] = {
eCSSProperty_background, // shorthand
eCSSProperty_background_color, // color
eCSSProperty_background_image, // image
eCSSProperty_background_repeat, // repeat
- eCSSProperty_background_position, // position
+ eCSSProperty_background_position_x, // positionX
+ eCSSProperty_background_position_y, // positionY
eCSSProperty_background_clip, // clip
eCSSProperty_background_origin, // origin
eCSSProperty_background_size, // size
eCSSProperty_background_attachment, // attachment
eCSSProperty_UNKNOWN, // maskMode
eCSSProperty_UNKNOWN // composite
};
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
const nsCSSProperty nsStyleImageLayers::kMaskLayerTable[] = {
eCSSProperty_mask, // shorthand
eCSSProperty_UNKNOWN, // color
eCSSProperty_mask_image, // image
eCSSProperty_mask_repeat, // repeat
- eCSSProperty_mask_position, // position
+ eCSSProperty_mask_position_x, // positionX
+ eCSSProperty_mask_position_y, // positionY
eCSSProperty_mask_clip, // clip
eCSSProperty_mask_origin, // origin
eCSSProperty_mask_size, // size
eCSSProperty_UNKNOWN, // attachment
eCSSProperty_mask_mode, // maskMode
eCSSProperty_mask_composite // composite
};
#endif
nsStyleImageLayers::nsStyleImageLayers()
: mAttachmentCount(1)
, mClipCount(1)
, mOriginCount(1)
, mRepeatCount(1)
- , mPositionCount(1)
+ , mPositionXCount(1)
+ , mPositionYCount(1)
, mImageCount(1)
, mSizeCount(1)
, mMaskModeCount(1)
, mBlendModeCount(1)
, mCompositeCount(1)
, mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT)
{
MOZ_COUNT_CTOR(nsStyleImageLayers);
}
nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource)
: mAttachmentCount(aSource.mAttachmentCount)
, mClipCount(aSource.mClipCount)
, mOriginCount(aSource.mOriginCount)
, mRepeatCount(aSource.mRepeatCount)
- , mPositionCount(aSource.mPositionCount)
+ , mPositionXCount(aSource.mPositionXCount)
+ , mPositionYCount(aSource.mPositionYCount)
, mImageCount(aSource.mImageCount)
, mSizeCount(aSource.mSizeCount)
, mMaskModeCount(aSource.mMaskModeCount)
, mBlendModeCount(aSource.mBlendModeCount)
, mCompositeCount(aSource.mCompositeCount)
, mLayers(aSource.mLayers) // deep copy
{
MOZ_COUNT_CTOR(nsStyleImageLayers);
// If the deep copy of mLayers failed, truncate the counts.
uint32_t count = mLayers.Length();
if (count != aSource.mLayers.Length()) {
NS_WARNING("truncating counts due to out-of-memory");
mAttachmentCount = std::max(mAttachmentCount, count);
mClipCount = std::max(mClipCount, count);
mOriginCount = std::max(mOriginCount, count);
mRepeatCount = std::max(mRepeatCount, count);
- mPositionCount = std::max(mPositionCount, count);
+ mPositionXCount = std::max(mPositionXCount, count);
+ mPositionYCount = std::max(mPositionYCount, count);
mImageCount = std::max(mImageCount, count);
mSizeCount = std::max(mSizeCount, count);
mMaskModeCount = std::max(mMaskModeCount, count);
mBlendModeCount = std::max(mBlendModeCount, count);
mCompositeCount = std::max(mCompositeCount, count);
}
}
@@ -2389,17 +2394,18 @@ nsStyleImageLayers::CalcDifference(const
if (mAttachmentCount != aOther.mAttachmentCount ||
mBlendModeCount != aOther.mBlendModeCount ||
mClipCount != aOther.mClipCount ||
mCompositeCount != aOther.mCompositeCount ||
mMaskModeCount != aOther.mMaskModeCount ||
mOriginCount != aOther.mOriginCount ||
mRepeatCount != aOther.mRepeatCount ||
- mPositionCount != aOther.mPositionCount ||
+ mPositionXCount != aOther.mPositionXCount ||
+ mPositionYCount != aOther.mPositionYCount ||
mSizeCount != aOther.mSizeCount) {
NS_UpdateHint(hint, nsChangeHint_NeutralChange);
}
return hint;
}
bool
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -522,17 +522,18 @@ struct nsStyleImageLayers {
}
// Indices into kBackgroundLayerTable and kMaskLayerTable
enum {
shorthand = 0,
color,
image,
repeat,
- position,
+ positionX,
+ positionY,
clip,
origin,
size,
attachment,
maskMode,
composite
};
@@ -732,17 +733,18 @@ struct nsStyleImageLayers {
};
// The (positive) number of computed values of each property, since
// the lengths of the lists are independent.
uint32_t mAttachmentCount,
mClipCount,
mOriginCount,
mRepeatCount,
- mPositionCount,
+ mPositionXCount,
+ mPositionYCount,
mImageCount,
mSizeCount,
mMaskModeCount,
mBlendModeCount,
mCompositeCount;
// Layers are stored in an array, matching the top-to-bottom order in
// which they are specified in CSS. The number of layers to be used
--- a/layout/style/test/test_shorthand_property_getters.html
+++ b/layout/style/test/test_shorthand_property_getters.html
@@ -153,16 +153,53 @@ e.setAttribute("style", "background-clip
is(e.style.background, "", "should not have background shorthand (background-attachment too short)");
e.setAttribute("style", "background-clip: border-box, padding-box, border-box; background-origin: border-box, padding-box, padding-box; background-size: cover, auto, contain; background-color: blue; background-image: url(404.png), none, url(404-2.png); background-attachment: fixed, scroll, scroll; background-position: top left, center, 30px 50px, bottom; background-repeat: repeat-x, repeat, no-repeat");
is(e.style.background, "", "should not have background shorthand (background-position too long)");
e.setAttribute("style", "background-clip: border-box, padding-box, border-box; background-origin: border-box, padding-box, padding-box; background-size: cover, auto, contain; background-color: blue; background-image: url(404.png), none, url(404-2.png); background-attachment: fixed, scroll, scroll; background-position: top left, center, 30px 50px; background-repeat: repeat-x, repeat");
is(e.style.background, "", "should not have background shorthand (background-repeat too short)");
e.setAttribute("style", "background-clip: border-box, padding-box, border-box; background-origin: border-box, padding-box, padding-box; background-size: cover, auto, contain, contain; background-color: blue; background-image: url(404.png), none, url(404-2.png); background-attachment: fixed, scroll, scroll; background-position: top left, center, 30px 50px; background-repeat: repeat-x, repeat, no-repeat");
is(e.style.background, "", "should not have background shorthand (background-size too long)");
+// Check that we only serialize background-position when the lists (of layers) for
+// the -x/-y subproperties are the same length.
+e.setAttribute("style", "background-position-x: 10%, left 2em, right; background-position-y: top 2em, bottom, 10%");
+is(e.style.backgroundPosition, "left 10% top 2em, left 2em bottom, right 10%", "should have background-position shorthand (both lists length 3)");
+e.setAttribute("style", "background-position-x: 10%, left 2em; background-position-y: top 2em, bottom, 10%");
+is(e.style.backgroundPosition, "", "should not have background-position shorthand (background-position-x too short)");
+e.setAttribute("style", "background-position-x: 10%, left 2em, right; background-position-y: top 2em");
+is(e.style.backgroundPosition, "", "should not have background-position shorthand (background-position-y too short)");
+
+// Check that background-position serialization doesn't produce invalid values.
+e.setAttribute("style", "background-position: 0px");
+is(e.style.backgroundPosition, "0px center", "1-value form should be accepted, with implied center value for background-position-y");
+e.setAttribute("style", "background-position: 0px center");
+is(e.style.backgroundPosition, "0px center", "2-value form 'x-offset' 'y-edge' should be accepted, and serialize to 2-value form");
+e.setAttribute("style", "background-position: left 0px center");
+is(e.style.backgroundPosition, "left 0px center", "3-value form 'x-edge' 'x-offset' 'y-edge' should be accepted and serialize to 3-value form");
+e.setAttribute("style", "background-position: left top 0px");
+is(e.style.backgroundPosition, "left top 0px", "3-value form 'x-edge' 'y-edge' 'y-offset' should be accepted and serialize to 3-value form");
+e.setAttribute("style", "background-position: left 0px top 0px");
+is(e.style.backgroundPosition, "left 0px top 0px", "4-value form should be accepted and serialize to 4-value form");
+e.setAttribute("style", "background-position-x: 0px; background-position-y: center");
+is(e.style.backgroundPosition, "0px center", "should always serialize to 2-value form if setting -x and -y with the 1-value form");
+e.setAttribute("style", "background-position-x: 0px; background-position-y: 0px");
+is(e.style.backgroundPosition, "0px 0px", "should always serialize to 2-value form if setting -x and -y with the 1-value form");
+e.setAttribute("style", "background-position-x: center; background-position-y: 0px");
+is(e.style.backgroundPosition, "center 0px", "should always serialize to 2-value form if setting -x and -y with the 1-value form");
+e.setAttribute("style", "background-position-x: left; background-position-y: top");
+is(e.style.backgroundPosition, "left top", "should always serialize to 2-value form if setting -x and -y with the 1-value form");
+e.setAttribute("style", "background-position-x: left 0px; background-position-y: center");
+is(e.style.backgroundPosition, "left 0px center", "should always serialize to 3-value form if both -x and -y specified an edge");
+e.setAttribute("style", "background-position-x: right; background-position-y: top 0px");
+is(e.style.backgroundPosition, "right top 0px", "should always serialize to 3-value form if both -x and -y specified an edge");
+e.setAttribute("style", "background-position-x: left 0px; background-position-y: 0px");
+is(e.style.backgroundPosition, "left 0px top 0px", "should serialize to 4-value form if 3-value form would only have one edge");
+e.setAttribute("style", "background-position-x: 0px; background-position-y: top 0px");
+is(e.style.backgroundPosition, "left 0px top 0px", "should serialize to 4-value form if 3-value form would only have one edge");
+
// Check that we only serialize transition when the lists are the same length.
e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
isnot(e.style.transition, "", "should have transition shorthand (lists same length)");
e.setAttribute("style", "transition-property: color, width, left; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
e.setAttribute("style", "transition-property: all; transition-duration: 1s, 200ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");
is(e.style.transition, "", "should not have transition shorthand (lists different lengths)");
e.setAttribute("style", "transition-property: color, width; transition-duration: 1s, 200ms, 300ms; transition-timing-function: ease-in, linear; transition-delay: 0s, 1s");