Bug 506826 - Parsing for background-image: -moz-element(#elementID). r=dbaron

This commit is contained in:
Markus Stange 2010-08-13 15:33:37 +02:00
Родитель 8577cd7530
Коммит f71c692289
10 изменённых файлов: 147 добавлений и 25 удалений

Просмотреть файл

@ -95,6 +95,7 @@ CSS_KEY(-moz-dialog, _moz_dialog)
CSS_KEY(-moz-dialogtext, _moz_dialogtext)
CSS_KEY(-moz-document, _moz_document)
CSS_KEY(-moz-dragtargetzone, _moz_dragtargetzone)
CSS_KEY(-moz-element, _moz_element)
CSS_KEY(-moz-eventreerow, _moz_eventreerow)
CSS_KEY(-moz-ethiopic-halehame, _moz_ethiopic_halehame)
CSS_KEY(-moz-ethiopic-numeric, _moz_ethiopic_numeric)

Просмотреть файл

@ -120,6 +120,7 @@ namespace css = mozilla::css;
#define VARIANT_ZERO_ANGLE 0x02000000 // unitless zero for angles
#define VARIANT_CALC 0x04000000 // eCSSUnit_Calc
#define VARIANT_CALC_NO_MIN_MAX 0x08000000 // no min() and max() for calc()
#define VARIANT_ELEMENT 0x10000000 // eCSSUnit_Element
// Common combinations of variants
#define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
@ -158,6 +159,8 @@ namespace css = mozilla::css;
#define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE)
#define VARIANT_TRANSFORM_LPCALC (VARIANT_LP | VARIANT_CALC | \
VARIANT_CALC_NO_MIN_MAX)
#define VARIANT_IMAGE (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \
VARIANT_IMAGE_RECT | VARIANT_ELEMENT)
//----------------------------------------------------------------------
@ -562,6 +565,7 @@ protected:
PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
float aNumber, const nsString& aUnit);
PRBool ParseImageRect(nsCSSValue& aImage);
PRBool ParseElement(nsCSSValue& aValue);
PRBool ParseColorStop(nsCSSValueGradient* aGradient);
PRBool ParseGradient(nsCSSValue& aValue, PRBool aIsRadial,
PRBool aIsRepeating);
@ -4438,6 +4442,11 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
return ParseImageRect(aValue);
}
if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
eCSSToken_Function == tk->mType &&
tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
return ParseElement(aValue);
}
if ((aVariantMask & VARIANT_COLOR) != 0) {
if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
(eCSSToken_ID == tk->mType) ||
@ -4743,6 +4752,32 @@ CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
return PR_TRUE;
}
SkipUntil(')');
return PR_FALSE;
}
// <element>: -moz-element(# <element_id> )
PRBool
CSSParserImpl::ParseElement(nsCSSValue& aValue)
{
// A non-iterative for loop to break out when an error occurs.
for (;;) {
if (!GetToken(PR_TRUE))
break;
if (mToken.mType == eCSSToken_ID) {
aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
} else {
UngetToken();
break;
}
if (!ExpectSymbol(')', PR_TRUE))
break;
return PR_TRUE;
}
// If we detect a syntax error, we must match the opening parenthesis of the
// function with the closing parenthesis and skip all the tokens in between.
SkipUntil(')');
@ -5593,9 +5628,7 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
return ParseVariant(aValue, VARIANT_HC, nsnull);
case eCSSProperty_background_image:
// Used only internally.
return ParseVariant(aValue,
VARIANT_HUO | VARIANT_GRADIENT | VARIANT_IMAGE_RECT,
nsnull);
return ParseVariant(aValue, VARIANT_IMAGE | VARIANT_INHERIT, nsnull);
case eCSSProperty__moz_background_inline_policy:
return ParseVariant(aValue, VARIANT_HK,
nsCSSProps::kBackgroundInlinePolicyKTable);
@ -6380,7 +6413,8 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem,
mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect"))) {
mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-element"))) {
if (haveImage)
return PR_FALSE;
haveImage = PR_TRUE;

Просмотреть файл

@ -741,6 +741,14 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
nsDependentString(GetOriginalURLValue()), aResult);
aResult.Append(NS_LITERAL_STRING(")"));
}
else if (eCSSUnit_Element == unit) {
aResult.Append(NS_LITERAL_STRING("-moz-element(#"));
nsAutoString tmpStr;
GetStringValue(tmpStr);
nsStyleUtil::AppendEscapedCSSIdent(
nsDependentString(tmpStr), aResult);
aResult.Append(NS_LITERAL_STRING(")"));
}
else if (eCSSUnit_Percent == unit) {
nsAutoString tmpStr;
tmpStr.AppendFloat(GetPercentValue() * 100.0f);
@ -846,6 +854,7 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
case eCSSUnit_Families: break;
case eCSSUnit_URL: break;
case eCSSUnit_Image: break;
case eCSSUnit_Element: break;
case eCSSUnit_Array: break;
case eCSSUnit_Attr:
case eCSSUnit_Cubic_Bezier:

Просмотреть файл

@ -109,6 +109,7 @@ enum nsCSSUnit {
eCSSUnit_Attr = 14, // (PRUnichar*) a attr(string) value
eCSSUnit_Local_Font = 15, // (PRUnichar*) a local font name
eCSSUnit_Font_Format = 16, // (PRUnichar*) a font format name
eCSSUnit_Element = 17, // (PRUnichar*) an element id
eCSSUnit_Array = 20, // (nsCSSValue::Array*) a list of values
eCSSUnit_Counter = 21, // (nsCSSValue::Array*) a counter(string,[string]) value
eCSSUnit_Counters = 22, // (nsCSSValue::Array*) a counters(string,string[,string]) value
@ -259,7 +260,7 @@ public:
{ return eCSSUnit_Calc <= mUnit && mUnit <= eCSSUnit_Calc_Maximum; }
PRBool UnitHasStringValue() const
{ return eCSSUnit_String <= mUnit && mUnit <= eCSSUnit_Font_Format; }
{ return eCSSUnit_String <= mUnit && mUnit <= eCSSUnit_Element; }
PRBool UnitHasArrayValue() const
{ return eCSSUnit_Array <= mUnit && mUnit <= eCSSUnit_Calc_Maximum; }

Просмотреть файл

@ -1614,6 +1614,17 @@ nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage,
aValue->SetString(gradientString);
break;
}
case eStyleImageType_Element:
{
nsAutoString elementId;
nsStyleUtil::AppendEscapedCSSIdent(
nsDependentString(aStyleImage.GetElementId()), elementId);
nsAutoString elementString = NS_LITERAL_STRING("-moz-element(#") +
elementId +
NS_LITERAL_STRING(")");
aValue->SetString(elementString);
break;
}
case eStyleImageType_Null:
aValue->SetIdent(eCSSKeyword_none);
break;

Просмотреть файл

@ -955,6 +955,9 @@ static void SetStyleImage(nsStyleContext* aStyleContext,
}
break;
}
case eCSSUnit_Element:
aResult.SetElementId(aValue.GetStringBufferValue());
break;
case eCSSUnit_None:
break;
default:

Просмотреть файл

@ -474,15 +474,15 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
DO_STRUCT_DIFFERENCE(Position);
DO_STRUCT_DIFFERENCE(TextReset);
// At this point, we know that the worst kind of damage we could do is
// a re-render (i.e., a VISUAL change).
maxHint = NS_STYLE_HINT_VISUAL;
// The following structs cause (as their maximal difference) a
// re-render to occur. VISUAL Structs: Color, Background
DO_STRUCT_DIFFERENCE(Color);
// Most backgrounds only require a re-render (i.e., a VISUAL change), but
// backgrounds using -moz-element need to reset SVG effects, too.
maxHint = nsChangeHint(NS_STYLE_HINT_VISUAL | nsChangeHint_UpdateEffects);
DO_STRUCT_DIFFERENCE(Background);
// Color only needs a repaint.
maxHint = NS_STYLE_HINT_VISUAL;
DO_STRUCT_DIFFERENCE(Color);
#undef DO_STRUCT_DIFFERENCE
// Note that we do not check whether this->RelevantLinkVisited() !=

Просмотреть файл

@ -1378,6 +1378,8 @@ nsStyleImage::DoCopy(const nsStyleImage& aOther)
SetImageData(aOther.mImage);
else if (aOther.mType == eStyleImageType_Gradient)
SetGradientData(aOther.mGradient);
else if (aOther.mType == eStyleImageType_Element)
SetElementId(aOther.mElementId);
SetCropRect(aOther.mCropRect);
}
@ -1389,6 +1391,8 @@ nsStyleImage::SetNull()
mGradient->Release();
else if (mType == eStyleImageType_Image)
NS_RELEASE(mImage);
else if (mType == eStyleImageType_Element)
nsCRT::free(mElementId);
mType = eStyleImageType_Null;
mCropRect = nsnull;
@ -1423,6 +1427,18 @@ nsStyleImage::SetGradientData(nsStyleGradient* aGradient)
}
}
void
nsStyleImage::SetElementId(const PRUnichar* aElementId)
{
if (mType != eStyleImageType_Null)
SetNull();
if (aElementId) {
mElementId = nsCRT::strdup(aElementId);
mType = eStyleImageType_Element;
}
}
void
nsStyleImage::SetCropRect(nsStyleSides* aCropRect)
{
@ -1506,6 +1522,9 @@ nsStyleImage::IsOpaque() const
return PR_FALSE;
}
if (mType == eStyleImageType_Element)
return PR_FALSE;
NS_ABORT_IF_FALSE(mType == eStyleImageType_Image, "unexpected image type");
nsCOMPtr<imgIContainer> imageContainer;
@ -1537,6 +1556,7 @@ nsStyleImage::IsComplete() const
case eStyleImageType_Null:
return PR_FALSE;
case eStyleImageType_Gradient:
case eStyleImageType_Element:
return PR_TRUE;
case eStyleImageType_Image:
{
@ -1573,6 +1593,9 @@ nsStyleImage::operator==(const nsStyleImage& aOther) const
if (mType == eStyleImageType_Gradient)
return *mGradient == *aOther.mGradient;
if (mType == eStyleImageType_Element)
return nsCRT::strcmp(mElementId, aOther.mElementId) == 0;
return PR_TRUE;
}
@ -1631,17 +1654,33 @@ nsStyleBackground::~nsStyleBackground()
nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const
{
if (mBackgroundColor != aOther.mBackgroundColor ||
mBackgroundInlinePolicy != aOther.mBackgroundInlinePolicy ||
mImageCount != aOther.mImageCount)
return NS_STYLE_HINT_VISUAL;
const nsStyleBackground* moreLayers =
mImageCount > aOther.mImageCount ? this : &aOther;
const nsStyleBackground* lessLayers =
mImageCount > aOther.mImageCount ? &aOther : this;
// We checked the image count above.
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, this) {
if (mLayers[i] != aOther.mLayers[i])
return NS_STYLE_HINT_VISUAL;
bool hasVisualDifference = false;
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, moreLayers) {
if (i < lessLayers->mImageCount) {
if (moreLayers->mLayers[i] != lessLayers->mLayers[i]) {
if ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
(lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element))
return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL);
hasVisualDifference = true;
}
} else {
if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element)
return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL);
hasVisualDifference = true;
}
}
if (hasVisualDifference ||
mBackgroundColor != aOther.mBackgroundColor ||
mBackgroundInlinePolicy != aOther.mBackgroundInlinePolicy)
return NS_STYLE_HINT_VISUAL;
return NS_STYLE_HINT_NONE;
}
@ -1649,7 +1688,7 @@ nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther)
/* static */
nsChangeHint nsStyleBackground::MaxDifference()
{
return NS_STYLE_HINT_VISUAL;
return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL);
}
#endif

Просмотреть файл

@ -186,13 +186,16 @@ private:
enum nsStyleImageType {
eStyleImageType_Null,
eStyleImageType_Image,
eStyleImageType_Gradient
eStyleImageType_Gradient,
eStyleImageType_Element
};
/**
* Represents a paintable image of one of the following types.
* (1) A real image loaded from an external source.
* (2) A CSS linear or radial gradient.
* (3) An element within a document, or an <img>, <video>, or <canvas> element
* not in a document.
* (*) Optionally a crop rect can be set to paint a partial (rectangular)
* region of an image. (Currently, this feature is only supported with an
* image of type (1)).
@ -210,6 +213,7 @@ struct nsStyleImage {
void SetNull();
void SetImageData(imgIRequest* aImage);
void SetGradientData(nsStyleGradient* aGradient);
void SetElementId(const PRUnichar* aElementId);
void SetCropRect(nsStyleSides* aCropRect);
nsStyleImageType GetType() const {
@ -223,6 +227,10 @@ struct nsStyleImage {
NS_ASSERTION(mType == eStyleImageType_Gradient, "Data is not a gradient!");
return mGradient;
}
const PRUnichar* GetElementId() const {
NS_ASSERTION(mType == eStyleImageType_Element, "Data is not an element!");
return mElementId;
}
nsStyleSides* GetCropRect() const {
NS_ASSERTION(mType == eStyleImageType_Image,
"Only image data can have a crop rect");
@ -252,7 +260,8 @@ struct nsStyleImage {
PRBool IsOpaque() const;
/**
* @return PR_TRUE if this image is fully loaded, and its size is calculated;
* always returns PR_TRUE if |mType| is |eStyleImageType_Gradient|.
* always returns PR_TRUE if |mType| is |eStyleImageType_Gradient| or
* |eStyleImageType_Element|.
*/
PRBool IsComplete() const;
/**
@ -282,6 +291,7 @@ private:
union {
imgIRequest* mImage;
nsStyleGradient* mGradient;
PRUnichar* mElementId;
};
// This is _currently_ used only in conjunction with eStyleImageType_Image.
nsAutoPtr<nsStyleSides> mCropRect;
@ -399,11 +409,15 @@ struct nsStyleBackground {
// frame size when their dimensions are 'auto', images don't; both
// types depend on the frame size when their dimensions are
// 'contain', 'cover', or a percentage.
// -moz-element also depends on the frame size when the dimensions
// are 'auto' since it could be an SVG gradient or pattern which
// behaves exactly like a CSS gradient.
PRBool DependsOnFrameSize(nsStyleImageType aType) const {
if (aType == eStyleImageType_Image) {
return mWidthType <= ePercentage || mHeightType <= ePercentage;
} else {
NS_ABORT_IF_FALSE(aType == eStyleImageType_Gradient,
NS_ABORT_IF_FALSE(aType == eStyleImageType_Gradient ||
aType == eStyleImageType_Element,
"unrecognized image type");
return mWidthType <= eAuto || mHeightType <= eAuto;
}

Просмотреть файл

@ -802,6 +802,7 @@ var gCSSProperties = {
"-moz-linear-gradient(10px 10px -45deg, red, blue) repeat",
"-moz-repeating-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat",
"-moz-repeating-linear-gradient(10px 10px -45deg, red, blue) repeat",
"-moz-element(#test) lime",
/* multiple backgrounds */
"url(404.png), url(404.png)",
"url(404.png), url(404.png) transparent",
@ -809,7 +810,7 @@ var gCSSProperties = {
"repeat-x, fixed, none",
"0% top url(404.png), url(404.png) 0% top",
"fixed repeat-y top left url(404.png), repeat-x green",
"url(404.png), -moz-linear-gradient(20px 20px -45deg, blue, green) black",
"url(404.png), -moz-linear-gradient(20px 20px -45deg, blue, green), -moz-element(#a) black",
/* test cases with clip+origin in the shorthand */
"url(404.png) green padding-box",
"url(404.png) border-box transparent",
@ -876,6 +877,10 @@ var gCSSProperties = {
"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none",
"none, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none",
"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)",
"-moz-element(#a)",
"-moz-element( #a )",
"-moz-element(#a-1)",
"-moz-element(#a\\:1)",
/* gradient torture test */
"-moz-linear-gradient(red, blue)",
"-moz-linear-gradient(red, yellow, blue)",
@ -1037,6 +1042,11 @@ var gCSSProperties = {
"-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 10, 50%, 30%, 0)",
],
invalid_values: [
"-moz-element(#a:1)",
"-moz-element(a#a)",
"-moz-element(#a a)",
"-moz-element(#a+a)",
"-moz-element(#a()",
/* Old syntax */
"-moz-linear-gradient(10px 10px, 20px, 30px 30px, 40px, from(blue), to(red))",
"-moz-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))",