Bug 479220 - Implement the CSS gradients proposal. r=roc,dbaron sr=roc

This commit is contained in:
Michael Ventnor 2009-07-31 13:32:32 +02:00
parent e2d0ef7a15
commit 9b44f1f16c
35 changed files with 1278 additions and 84 deletions

View File

@ -1513,9 +1513,12 @@ DetermineBackgroundColorInternal(nsPresContext* aPresContext,
aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
}
aBottomImage = aBackground.BottomLayer().mImage;
if (!aDrawBackgroundImage || !HaveCompleteBackgroundImage(aBottomImage)) {
if (aBackground.BottomLayer().mImage.GetType() == eBackgroundImage_Image) {
aBottomImage = aBackground.BottomLayer().mImage.GetImageData();
if (!aDrawBackgroundImage || !HaveCompleteBackgroundImage(aBottomImage)) {
aBottomImage = nsnull;
}
} else {
aBottomImage = nsnull;
}
@ -1555,6 +1558,84 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
bottomImage);
}
static gfxFloat
ConvertGradientValueToPixels(const nsStyleCoord& aCoord,
nscoord aFillLength,
nscoord aAppUnitsPerPixel)
{
switch (aCoord.GetUnit()) {
case eStyleUnit_Percent:
return aCoord.GetPercentValue() * aFillLength / aAppUnitsPerPixel;
case eStyleUnit_Coord:
return aCoord.GetCoordValue() / aAppUnitsPerPixel;
default:
NS_WARNING("Unexpected coord unit");
return 0;
}
}
void
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsStyleGradient* aGradient,
const nsRect& aDirtyRect,
const nsRect& aOneCellArea,
const nsRect& aFillArea,
PRBool aRepeat)
{
gfxContext *ctx = aRenderingContext.ThebesContext();
nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
gfxRect dirtyRect = RectToGfxRect(aDirtyRect, appUnitsPerPixel);
gfxRect areaToFill = RectToGfxRect(aFillArea, appUnitsPerPixel);
gfxRect oneCellArea = RectToGfxRect(aOneCellArea, appUnitsPerPixel);
gfxPoint fillOrigin = oneCellArea.TopLeft();
areaToFill = areaToFill.Intersect(dirtyRect);
if (areaToFill.IsEmpty())
return;
gfxFloat gradX0 = ConvertGradientValueToPixels(aGradient->mStartX,
aOneCellArea.width, appUnitsPerPixel);
gfxFloat gradY0 = ConvertGradientValueToPixels(aGradient->mStartY,
aOneCellArea.height, appUnitsPerPixel);
gfxFloat gradX1 = ConvertGradientValueToPixels(aGradient->mEndX,
aOneCellArea.width, appUnitsPerPixel);
gfxFloat gradY1 = ConvertGradientValueToPixels(aGradient->mEndY,
aOneCellArea.height, appUnitsPerPixel);
nsRefPtr<gfxPattern> gradientPattern;
if (aGradient->mIsRadial) {
gfxFloat gradRadius0 = double(aGradient->mStartRadius) / appUnitsPerPixel;
gfxFloat gradRadius1 = double(aGradient->mEndRadius) / appUnitsPerPixel;
gradientPattern = new gfxPattern(gradX0, gradY0, gradRadius0,
gradX1, gradY1, gradRadius1);
} else {
gradientPattern = new gfxPattern(gradX0, gradY0, gradX1, gradY1);
}
if (!gradientPattern || gradientPattern->CairoStatus())
return;
for (PRUint32 i = 0; i < aGradient->mStops.Length(); i++) {
gradientPattern->AddColorStop(aGradient->mStops[i].mPosition,
gfxRGBA(aGradient->mStops[i].mColor));
}
if (aRepeat)
gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
ctx->Save();
ctx->NewPath();
// The fill origin is part of the translate call so the pattern starts at
// the desired point, rather than (0,0).
ctx->Translate(fillOrigin);
ctx->SetPattern(gradientPattern);
ctx->Rectangle(areaToFill - fillOrigin, PR_TRUE);
ctx->Fill();
ctx->Restore();
}
void
nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -1811,26 +1892,6 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
* background-repeat
*/
// Lookup the image
imgIRequest *req = aLayer.mImage;
if (!HaveCompleteBackgroundImage(req))
return;
nsCOMPtr<imgIContainer> image;
req->GetImage(getter_AddRefs(image));
req = nsnull;
nsIntSize imageIntSize;
image->GetWidth(&imageIntSize.width);
image->GetHeight(&imageIntSize.height);
nsSize imageSize;
imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);
imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);
if (imageSize.width == 0 || imageSize.height == 0)
return;
// relative to aBorderArea
nsRect bgPositioningArea(0, 0, 0, 0);
@ -1885,6 +1946,32 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
}
}
nsSize imageSize;
nsCOMPtr<imgIContainer> image;
if (aLayer.mImage.GetType() == eBackgroundImage_Image) {
// Lookup the image
imgIRequest *req = aLayer.mImage.GetImageData();
if (!HaveCompleteBackgroundImage(req))
return;
req->GetImage(getter_AddRefs(image));
req = nsnull;
nsIntSize imageIntSize;
image->GetWidth(&imageIntSize.width);
image->GetHeight(&imageIntSize.height);
imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);
imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);
} else if (aLayer.mImage.GetType() == eBackgroundImage_Gradient) {
imageSize = bgPositioningArea.Size();
} else {
return;
}
if (imageSize.width == 0 || imageSize.height == 0)
return;
// Compute the anchor point.
//
// relative to aBorderArea.TopLeft() (which is where the top-left
@ -1994,9 +2081,16 @@ PaintBackgroundLayer(nsPresContext* aPresContext,
}
fillArea.IntersectRect(fillArea, aBGClipRect);
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame),
destArea, fillArea, anchor + aBorderArea.TopLeft(), aDirtyRect);
if (aLayer.mImage.GetType() == eBackgroundImage_Image) {
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame),
destArea, fillArea, anchor + aBorderArea.TopLeft(), aDirtyRect);
} else {
nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
aLayer.mImage.GetGradientData(),
aDirtyRect, destArea, fillArea,
(repeat != NS_STYLE_BG_REPEAT_OFF));
}
}
static void

View File

@ -127,6 +127,17 @@ struct nsCSSRendering {
const nsRect& aFocusRect,
nscolor aColor);
/**
* Render a gradient for an element.
*/
static void PaintGradient(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nsStyleGradient* aGradient,
const nsRect& aDirtyRect,
const nsRect& aOneCellArea,
const nsRect& aFillArea,
PRBool aRepeat);
/**
* Gets the root frame for the frame
*/

View File

@ -562,9 +562,9 @@ nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder) {
return PR_TRUE;
if (bottomLayer.mRepeat == NS_STYLE_BG_REPEAT_XY) {
if (bottomLayer.mImage) {
if (bottomLayer.mImage.GetType() == eBackgroundImage_Image) {
nsCOMPtr<imgIContainer> container;
bottomLayer.mImage->GetImage(getter_AddRefs(container));
bottomLayer.mImage.GetImageData()->GetImage(getter_AddRefs(container));
if (container) {
PRBool animated;
container->GetAnimated(&animated);
@ -591,7 +591,7 @@ nsDisplayBackground::IsUniform(nsDisplayListBuilder* aBuilder) {
nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg);
if (!hasBG)
return PR_TRUE;
if (!bg->BottomLayer().mImage &&
if (bg->BottomLayer().mImage.GetType() == eBackgroundImage_Null &&
bg->mImageCount == 1 &&
!nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius) &&
bg->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER)

View File

@ -1268,8 +1268,10 @@ nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
{
nsRefPtr<nsImageLoader> loaders;
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
imgIRequest *image = aStyleBackground->mLayers[i].mImage;
loaders = nsImageLoader::Create(aFrame, image, PR_FALSE, loaders);
if (aStyleBackground->mLayers[i].mImage.GetType() == eBackgroundImage_Image) {
imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
loaders = nsImageLoader::Create(aFrame, image, PR_FALSE, loaders);
}
}
SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
}

View File

@ -532,11 +532,8 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
const nsStyleBackground *oldBG = aOldStyleContext->GetStyleBackground();
const nsStyleBackground *newBG = GetStyleBackground();
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) {
imgIRequest *oldImage = oldBG->mLayers[i].mImage;
imgIRequest *newImage = i < newBG->mImageCount
? newBG->mLayers[i].mImage.get()
: nsnull;
if (oldImage && !EqualImages(oldImage, newImage)) {
if (i >= newBG->mImageCount ||
oldBG->mLayers[i].mImage != newBG->mLayers[i].mImage) {
// stop the image loading for the frame, the image has changed
PresContext()->SetImageLoaders(this,
nsPresContext::BACKGROUND_IMAGE, nsnull);
@ -4092,7 +4089,7 @@ nsIFrame::CheckInvalidateSizeChange(const nsRect& aOldRect,
const nsStyleBackground *bg = GetStyleBackground();
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
const nsStyleBackground::Layer &layer = bg->mLayers[i];
if (layer.mImage &&
if (layer.mImage.GetType() != eBackgroundImage_Null &&
(layer.mPosition.mXIsPercent || layer.mPosition.mYIsPercent)) {
Invalidate(nsRect(0, 0, aOldRect.width, aOldRect.height));
return;

View File

@ -0,0 +1,23 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(0,0,150,300);
grad.addColorStop(0, '#0000ff');
grad.addColorStop(1, '#000000');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(left top, center bottom, from(#0000ff), to(#000000)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,34 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(150,20,270,130);
grad.addColorStop(0, '#0000ff');
grad.addColorStop(1, '#000000');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
ctx = document.getElementById('canvas2').getContext('2d');
grad = ctx.createLinearGradient(30,300,300,50);
grad.addColorStop(0, '#00ff00');
grad.addColorStop(1, '#000000');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"></canvas>
<br/><br/>
<canvas id="canvas2" width="300" height="300"></canvas>
</body>
</html>

View File

@ -0,0 +1,3 @@
<div style="background: -moz-linear-gradient(center 20px, 90% 130px, from(#0000ff), to(#000000)) no-repeat; width: 300px; height: 300px;"><br></div>
<br>
<div style="background: -moz-linear-gradient(10% bottom, right 50px, from(#00ff00), to(#000000)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,23 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(30,60,240,270);
grad.addColorStop(0, '#0000ff');
grad.addColorStop(1, '#000000');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(10% 20%, 80% 90%, from(#0000ff), to(#000000)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,24 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(10,20,50,100);
grad.addColorStop(0, 'red');
grad.addColorStop(1, 'rgb(100, 200, 0)');
grad.addColorStop(0.5, '#7777FF');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(10px 20px, 50px 100px, from(red), to(rgb(100, 200, 0)), color-stop(0.5, #7777FF)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,21 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(0,0,150,300);
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(left top, center bottom) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,22 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(0,0,150,300);
grad.addColorStop(0.5, '#0000ff');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(left top, center bottom, color-stop(0.5, #0000ff)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,24 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
radgrad.addColorStop(0, '#A7D30C');
radgrad.addColorStop(0.9, '#019F62');
radgrad.addColorStop(1, 'rgba(1,159,98,0)');
ctx.fillStyle = radgrad;
ctx.fillRect(0,0,150,150);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-radial-gradient(45px 45px, 10px, 52px 50px, 30px, from(#a7d30c), color-stop(90%, #019f62), to(rgba(1, 159, 98, 0))) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -0,0 +1,8 @@
== linear.html linear-ref.html
== radial.html radial-ref.html
== linear-keywords.html linear-keywords-ref.html
== linear-percent.html linear-percent-ref.html
== linear-mix.html linear-mix-ref.html
== nostops.html nostops-ref.html
== onestop.html onestop-ref.html
== twostops.html twostops-ref.html

View File

@ -0,0 +1,23 @@
<html xmlns="http://www.w3.org/1999/xhtml"
class="reftest-wait">
<head>
<script>
function doDraw() {
var ctx = document.getElementById('canvas').getContext('2d');
var grad = ctx.createLinearGradient(0,0,150,300);
grad.addColorStop(0.5, '#0000ff');
grad.addColorStop(0.5, '#ff0000');
ctx.fillStyle = grad;
ctx.fillRect(0,0,300,300);
document.documentElement.removeAttribute('class');
}
</script>
</head>
<body onload="doDraw();">
<canvas id="canvas" width="300" height="300"/>
</body>
</html>

View File

@ -0,0 +1 @@
<div style="background: -moz-linear-gradient(left top, center bottom, color-stop(0.5, #0000ff), color-stop(0.5, #ff0000)) no-repeat; width: 300px; height: 300px;"><br></div>

View File

@ -41,6 +41,9 @@ include css-import/reftest.list
# css character encoding tests
include css-charset/reftest.list
# css gradients
include css-gradients/reftest.list
# css media queries (tests for print mode)
include css-mediaqueries/reftest.list

View File

@ -413,6 +413,49 @@ nsCSSDeclaration::AppendCSSValueToString(nsCSSProperty aProperty,
tmpStr.AppendFloat(aValue.GetFloatValue());
aResult.Append(tmpStr);
}
else if (eCSSUnit_Gradient == unit) {
nsCSSValueGradient* gradient = aValue.GetGradientValue();
if (gradient->mIsRadial)
aResult.AppendLiteral("-moz-radial-gradient(");
else
aResult.AppendLiteral("-moz-linear-gradient(");
AppendCSSValueToString(eCSSProperty_background_position,
gradient->mStartX, aResult);
aResult.AppendLiteral(" ");
AppendCSSValueToString(eCSSProperty_background_position,
gradient->mStartY, aResult);
aResult.AppendLiteral(", ");
if (gradient->mIsRadial) {
AppendCSSValueToString(aProperty, gradient->mStartRadius, aResult);
aResult.AppendLiteral(", ");
}
AppendCSSValueToString(eCSSProperty_background_position,
gradient->mEndX, aResult);
aResult.AppendLiteral(" ");
AppendCSSValueToString(eCSSProperty_background_position,
gradient->mEndY, aResult);
if (gradient->mIsRadial) {
aResult.AppendLiteral(", ");
AppendCSSValueToString(aProperty, gradient->mEndRadius, aResult);
}
for (PRUint32 i = 0; i < gradient->mStops.Length(); i++) {
aResult.AppendLiteral(", color-stop(");
AppendCSSValueToString(aProperty, gradient->mStops[i].mLocation, aResult);
aResult.AppendLiteral(", ");
AppendCSSValueToString(aProperty, gradient->mStops[i].mColor, aResult);
aResult.AppendLiteral(")");
}
aResult.AppendLiteral(")");
}
switch (unit) {
case eCSSUnit_Null: break;
@ -446,6 +489,7 @@ nsCSSDeclaration::AppendCSSValueToString(nsCSSProperty aProperty,
case eCSSUnit_Color: break;
case eCSSUnit_Percent: aResult.Append(PRUnichar('%')); break;
case eCSSUnit_Number: break;
case eCSSUnit_Gradient: break;
case eCSSUnit_Inch: aResult.AppendLiteral("in"); break;
case eCSSUnit_Millimeter: aResult.AppendLiteral("mm"); break;

View File

@ -66,7 +66,6 @@
#include "nsIAtom.h"
#include "nsCOMArray.h"
#include "nsColor.h"
#include "nsStyleConsts.h"
#include "nsCSSPseudoClasses.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSAnonBoxes.h"
@ -107,6 +106,7 @@
#define VARIANT_NONE 0x040000 // O
#define VARIANT_NORMAL 0x080000 // M
#define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
#define VARIANT_GRADIENT 0x200000 // eCSSUnit_Gradient
// Common combinations of variants
#define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
@ -422,7 +422,7 @@ protected:
PRBool ParseBackgroundList(nsCSSProperty aPropID); // a single value prop-id
PRBool ParseBackgroundPosition();
PRBool ParseBoxPositionValues(nsCSSValuePair& aOut);
PRBool ParseBoxPositionValues(nsCSSValuePair& aOut, PRBool aAcceptsInherit);
PRBool ParseBackgroundSize();
PRBool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
PRBool ParseBorderColor();
@ -510,6 +510,8 @@ protected:
PRBool ParseURL(nsCSSValue& aValue);
PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
float aNumber, const nsString& aUnit);
PRBool ParseGradientStop(nsCSSValueGradient* aGradient);
PRBool ParseGradient(nsCSSValue& aValue, PRBool aIsRadial);
void SetParsingCompoundProperty(PRBool aBool) {
NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
@ -4513,6 +4515,15 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
}
return PR_FALSE;
}
if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
eCSSToken_Function == tk->mType) {
// a generated gradient
if (tk->mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient"))
return ParseGradient(aValue, PR_FALSE);
if (tk->mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient"))
return ParseGradient(aValue, PR_TRUE);
}
if ((aVariantMask & VARIANT_COLOR) != 0) {
if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
(eCSSToken_ID == tk->mType) ||
@ -4762,6 +4773,181 @@ CSSParserImpl::ParseURL(nsCSSValue& aValue)
return PR_TRUE;
}
PRBool
CSSParserImpl::ParseGradientStop(nsCSSValueGradient* aGradient)
{
if (!GetToken(PR_TRUE))
return PR_FALSE;
if (eCSSToken_Function != mToken.mType) {
UngetToken();
return PR_FALSE;
}
if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
// Start of the gradient
if (!ExpectSymbol('(', PR_FALSE)) {
NS_ABORT_IF_FALSE(PR_FALSE, "function token without (");
}
nsCSSValue fromFloat(0.0f, eCSSUnit_Percent);
nsCSSValue fromColor;
if (!ParseVariant(fromColor, VARIANT_COLOR, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
if (!ExpectSymbol(')', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
aGradient->mStops.AppendElement(nsCSSValueGradientStop(fromFloat, fromColor));
return PR_TRUE;
}
if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
// End of the gradient
if (!ExpectSymbol('(', PR_FALSE)) {
NS_ABORT_IF_FALSE(PR_FALSE, "function token without (");
}
nsCSSValue toFloat(1.0f, eCSSUnit_Percent);
nsCSSValue toColor;
if (!ParseVariant(toColor, VARIANT_COLOR, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
if (!ExpectSymbol(')', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
aGradient->mStops.AppendElement(nsCSSValueGradientStop(toFloat, toColor));
return PR_TRUE;
}
if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) {
// Some kind of gradient stop, somewhere...
if (!ExpectSymbol('(', PR_FALSE)) {
NS_ABORT_IF_FALSE(PR_FALSE, "function token without (");
}
nsCSSValue stopFloat;
if (!ParseVariant(stopFloat, VARIANT_PERCENT | VARIANT_NUMBER, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
// Check for a sane position value, and clamp it if it isn't
if (stopFloat.GetUnit() == eCSSUnit_Percent) {
if (stopFloat.GetPercentValue() > 1.0)
stopFloat.SetPercentValue(1.0);
else if (stopFloat.GetPercentValue() < 0.0)
stopFloat.SetPercentValue(0.0);
} else {
if (stopFloat.GetFloatValue() > 1.0)
stopFloat.SetFloatValue(1.0, eCSSUnit_Number);
else if (stopFloat.GetFloatValue() < 0.0)
stopFloat.SetFloatValue(0.0, eCSSUnit_Number);
}
if (!ExpectSymbol(',', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
nsCSSValue stopColor;
if (!ParseVariant(stopColor, VARIANT_COLOR, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
if (!ExpectSymbol(')', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
aGradient->mStops.AppendElement(nsCSSValueGradientStop(stopFloat, stopColor));
return PR_TRUE;
}
// No idea what this is
return PR_FALSE;
}
PRBool
CSSParserImpl::ParseGradient(nsCSSValue& aValue,
PRBool aIsRadial)
{
if (!ExpectSymbol('(', PR_FALSE)) {
NS_ABORT_IF_FALSE(PR_FALSE, "function token without (");
}
nsCSSValuePair startPos;
if (!ParseBoxPositionValues(startPos, PR_FALSE))
return PR_FALSE;
if (!ExpectSymbol(',', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
nsCSSValue startRadius;
if (aIsRadial) {
if (!ParseNonNegativeVariant(startRadius, VARIANT_LENGTH, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
if (!ExpectSymbol(',', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
}
nsCSSValuePair endPos;
if (!ParseBoxPositionValues(endPos, PR_FALSE)) {
SkipUntil(')');
return PR_FALSE;
}
nsCSSValue endRadius;
if (aIsRadial) {
if (!ExpectSymbol(',', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
if (!ParseNonNegativeVariant(endRadius, VARIANT_LENGTH, nsnull)) {
SkipUntil(')');
return PR_FALSE;
}
}
nsRefPtr<nsCSSValueGradient> cssGradient =
new nsCSSValueGradient(aIsRadial, startPos.mXValue, startPos.mYValue,
startRadius, endPos.mXValue, endPos.mYValue,
endRadius);
// Do optional stop functions
while (ExpectSymbol(',', PR_TRUE)) {
if (!ParseGradientStop(cssGradient)) {
SkipUntil(')');
return PR_FALSE;
}
}
if (!ExpectSymbol(')', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
}
aValue.SetGradientValue(cssGradient);
return PR_TRUE;
}
PRInt32
CSSParserImpl::ParseChoice(nsCSSValue aValues[],
const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
@ -5421,7 +5607,7 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
return ParseVariant(aValue, VARIANT_HC, nsnull);
case eCSSProperty_background_image:
// Used only internally.
return ParseVariant(aValue, VARIANT_HUO, nsnull);
return ParseVariant(aValue, VARIANT_HUO | VARIANT_GRADIENT, nsnull);
case eCSSProperty__moz_background_inline_policy:
return ParseVariant(aValue, VARIANT_HK,
nsCSSProps::kBackgroundInlinePolicyKTable);
@ -6155,7 +6341,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem,
if (havePosition)
return PR_FALSE;
havePosition = PR_TRUE;
if (!ParseBoxPositionValues(aItem.mPosition)) {
if (!ParseBoxPositionValues(aItem.mPosition, PR_FALSE)) {
return PR_FALSE;
}
#if 0
@ -6202,7 +6388,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem,
aItem.mLastItem = PR_TRUE;
}
} else if (eCSSToken_Function == tt &&
mToken.mIdent.LowerCaseEqualsLiteral("url")) {
(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient"))) {
if (haveImage)
return PR_FALSE;
haveImage = PR_TRUE;
@ -6214,7 +6402,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem,
if (havePosition)
return PR_FALSE;
havePosition = PR_TRUE;
if (!ParseBoxPositionValues(aItem.mPosition)) {
if (!ParseBoxPositionValues(aItem.mPosition, PR_FALSE)) {
return PR_FALSE;
}
} else {
@ -6285,15 +6473,11 @@ CSSParserImpl::ParseBackgroundPosition()
nsCSSValuePair valuePair;
nsCSSValuePairList *head = nsnull, **tail = &head;
for (;;) {
if (!ParseBoxPositionValues(valuePair)) {
if (!ParseBoxPositionValues(valuePair, !head)) {
break;
}
PRBool inheritOrInitial = valuePair.mXValue.GetUnit() == eCSSUnit_Inherit ||
valuePair.mXValue.GetUnit() == eCSSUnit_Initial;
if (inheritOrInitial && head) {
// inherit and initial are only allowed on their own
break;
}
nsCSSValuePairList *item = new nsCSSValuePairList;
if (!item) {
mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
@ -6323,14 +6507,17 @@ CSSParserImpl::ParseBackgroundPosition()
* like "top," "left center," etc.
*
* @param aOut The nsCSSValuePair in which to place the result.
* @param aAcceptsInherit If true, 'inherit' and 'initial' are legal values
* @return Whether or not the operation succeeded.
*/
PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut)
PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
PRBool aAcceptsInherit)
{
// First try a percentage or a length value
nsCSSValue &xValue = aOut.mXValue,
&yValue = aOut.mYValue;
if (ParseVariant(xValue, VARIANT_HLP, nsnull)) {
PRInt32 variantMask = aAcceptsInherit ? VARIANT_HLP : VARIANT_LP;
if (ParseVariant(xValue, variantMask, nsnull)) {
if (eCSSUnit_Inherit == xValue.GetUnit() ||
eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
yValue = xValue;
@ -7585,7 +7772,7 @@ PRBool CSSParserImpl::ParseMozTransform()
PRBool CSSParserImpl::ParseMozTransformOrigin()
{
/* Read in a box position, fail if we can't. */
if (!ParseBoxPositionValues(mTempData.mDisplay.mTransformOrigin) ||
if (!ParseBoxPositionValues(mTempData.mDisplay.mTransformOrigin, PR_TRUE) ||
!ExpectEndProperty())
return PR_FALSE;

View File

@ -124,6 +124,13 @@ nsCSSValue::nsCSSValue(nsCSSValue::Image* aValue)
mValue.mImage->AddRef();
}
nsCSSValue::nsCSSValue(nsCSSValueGradient* aValue)
: mUnit(eCSSUnit_Gradient)
{
mValue.mGradient = aValue;
mValue.mGradient->AddRef();
}
nsCSSValue::nsCSSValue(const nsCSSValue& aCopy)
: mUnit(aCopy.mUnit)
{
@ -155,6 +162,10 @@ nsCSSValue::nsCSSValue(const nsCSSValue& aCopy)
mValue.mImage = aCopy.mValue.mImage;
mValue.mImage->AddRef();
}
else if (eCSSUnit_Gradient == mUnit) {
mValue.mGradient = aCopy.mValue.mGradient;
mValue.mGradient->AddRef();
}
else {
NS_NOTREACHED("unknown unit");
}
@ -194,6 +205,9 @@ PRBool nsCSSValue::operator==(const nsCSSValue& aOther) const
else if (eCSSUnit_Image == mUnit) {
return *mValue.mImage == *aOther.mValue.mImage;
}
else if (eCSSUnit_Gradient == mUnit) {
return *mValue.mGradient == *aOther.mValue.mGradient;
}
else {
return mValue.mFloat == aOther.mValue.mFloat;
}
@ -243,6 +257,8 @@ void nsCSSValue::DoReset()
mValue.mURL->Release();
} else if (eCSSUnit_Image == mUnit) {
mValue.mImage->Release();
} else if (eCSSUnit_Gradient == mUnit) {
mValue.mGradient->Release();
}
mUnit = eCSSUnit_Null;
}
@ -326,6 +342,14 @@ void nsCSSValue::SetImageValue(nsCSSValue::Image* aValue)
mValue.mImage->AddRef();
}
void nsCSSValue::SetGradientValue(nsCSSValueGradient* aValue)
{
Reset();
mUnit = eCSSUnit_Gradient;
mValue.mGradient = aValue;
mValue.mGradient->AddRef();
}
void nsCSSValue::SetAutoValue()
{
Reset();
@ -439,15 +463,12 @@ nsCSSValue::URL::URL(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
mRefCnt(0)
{
NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
mString->AddRef();
MOZ_COUNT_CTOR(nsCSSValue::URL);
}
nsCSSValue::URL::~URL()
{
mString->Release();
MOZ_COUNT_DTOR(nsCSSValue::URL);
}
PRBool
@ -485,8 +506,6 @@ nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
nsIDocument* aDocument)
: URL(aURI, aString, aReferrer, aOriginPrincipal)
{
MOZ_COUNT_CTOR(nsCSSValue::Image);
if (mURI &&
nsContentUtils::CanLoadImage(mURI, aDocument, aDocument,
aOriginPrincipal)) {
@ -498,5 +517,38 @@ nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
nsCSSValue::Image::~Image()
{
MOZ_COUNT_DTOR(nsCSSValue::Image);
}
nsCSSValueGradientStop::nsCSSValueGradientStop(const nsCSSValue& aLocation,
const nsCSSValue& aColor)
: mLocation(aLocation),
mColor(aColor)
{
MOZ_COUNT_CTOR(nsCSSValueGradientStop);
}
nsCSSValueGradientStop::nsCSSValueGradientStop(const nsCSSValueGradientStop& aOther)
: mLocation(aOther.mLocation),
mColor(aOther.mColor)
{
MOZ_COUNT_CTOR(nsCSSValueGradientStop);
}
nsCSSValueGradientStop::~nsCSSValueGradientStop()
{
MOZ_COUNT_DTOR(nsCSSValueGradientStop);
}
nsCSSValueGradient::nsCSSValueGradient(PRBool aIsRadial, const nsCSSValue& aStartX,
const nsCSSValue& aStartY, const nsCSSValue& aStartRadius, const nsCSSValue& aEndX,
const nsCSSValue& aEndY, const nsCSSValue& aEndRadius)
: mIsRadial(aIsRadial),
mStartX(aStartX),
mStartY(aStartY),
mEndX(aEndX),
mEndY(aEndY),
mStartRadius(aStartRadius),
mEndRadius(aEndRadius),
mRefCnt(0)
{
}

View File

@ -49,6 +49,7 @@
#include "nsAutoPtr.h"
#include "nsCRTGlue.h"
#include "nsStringBuffer.h"
#include "nsTArray.h"
class imgIRequest;
class nsIDocument;
@ -112,6 +113,7 @@ enum nsCSSUnit {
eCSSUnit_URL = 30, // (nsCSSValue::URL*) value
eCSSUnit_Image = 31, // (nsCSSValue::Image*) value
eCSSUnit_Gradient = 32, // (nsCSSValueGradient*) value
eCSSUnit_Integer = 50, // (int) simple value
eCSSUnit_Enumerated = 51, // (int) value has enumerated meaning
eCSSUnit_EnumColor = 80, // (int) enumerated color (kColorKTable)
@ -155,6 +157,8 @@ enum nsCSSUnit {
eCSSUnit_Milliseconds = 3001 // (float) 1/1000 second
};
struct nsCSSValueGradient;
class nsCSSValue {
public:
struct Array;
@ -180,6 +184,7 @@ public:
nsCSSValue(Array* aArray, nsCSSUnit aUnit) NS_HIDDEN;
explicit nsCSSValue(URL* aValue) NS_HIDDEN;
explicit nsCSSValue(Image* aValue) NS_HIDDEN;
explicit nsCSSValue(nsCSSValueGradient* aValue) NS_HIDDEN;
nsCSSValue(const nsCSSValue& aCopy) NS_HIDDEN;
~nsCSSValue() { Reset(); }
@ -266,6 +271,12 @@ public:
mValue.mURL->mURI : mValue.mImage->mURI;
}
nsCSSValueGradient* GetGradientValue() const
{
NS_ASSERTION(mUnit == eCSSUnit_Gradient, "not a gradient value");
return mValue.mGradient;
}
URL* GetURLStructValue() const
{
// Not allowing this for Image values, because if the caller takes
@ -307,6 +318,7 @@ public:
NS_HIDDEN_(void) SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
NS_HIDDEN_(void) SetURLValue(nsCSSValue::URL* aURI);
NS_HIDDEN_(void) SetImageValue(nsCSSValue::Image* aImage);
NS_HIDDEN_(void) SetGradientValue(nsCSSValueGradient* aGradient);
NS_HIDDEN_(void) SetAutoValue();
NS_HIDDEN_(void) SetInheritValue();
NS_HIDDEN_(void) SetInitialValue();
@ -355,17 +367,24 @@ public:
return;
}
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsCSSValue::URL", sizeof(*this));
}
void Release() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsCSSValue::URL");
return;
}
if (--mRefCnt == 0)
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsCSSValue::URL");
if (mRefCnt == 0)
delete this;
}
protected:
nsrefcnt mRefCnt;
// not to be implemented
URL(const URL& aOther);
URL& operator=(const URL& aOther);
};
struct Image : public URL {
@ -381,13 +400,25 @@ public:
nsCOMPtr<imgIRequest> mRequest; // null == image load blocked or somehow failed
// Override Release so we delete correctly without a virtual destructor
// Override AddRef and Release to not only log ourselves correctly, but
// also so that we delete correctly without a virtual destructor
void AddRef() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsCSSValue::Image");
return;
}
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsCSSValue::Image", sizeof(*this));
}
void Release() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsCSSValue::Image");
return;
}
if (--mRefCnt == 0)
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsCSSValue::Image");
if (mRefCnt == 0)
delete this;
}
};
@ -409,9 +440,105 @@ protected:
Array* mArray;
URL* mURL;
Image* mImage;
nsCSSValueGradient* mGradient;
} mValue;
};
struct nsCSSValueGradientStop {
public:
nsCSSValueGradientStop(const nsCSSValue& aLocation, const nsCSSValue& aColor) NS_HIDDEN;
// needed to keep bloat logs happy when we use the nsTArray in nsCSSValueGradient
nsCSSValueGradientStop(const nsCSSValueGradientStop& aOther) NS_HIDDEN;
~nsCSSValueGradientStop() NS_HIDDEN;
nsCSSValue mLocation;
nsCSSValue mColor;
PRBool operator==(const nsCSSValueGradientStop& aOther) const
{
return (mLocation == aOther.mLocation &&
mColor == aOther.mColor);
}
PRBool operator!=(const nsCSSValueGradientStop& aOther) const
{
return !(*this == aOther);
}
};
struct nsCSSValueGradient {
nsCSSValueGradient(PRBool aIsRadial, const nsCSSValue& aStartX, const nsCSSValue& aStartY,
const nsCSSValue& aStartRadius, const nsCSSValue& aEndX, const nsCSSValue& aEndY,
const nsCSSValue& aEndRadius) NS_HIDDEN;
// true if gradient is radial, false if it is linear
PRPackedBool mIsRadial;
nsCSSValue mStartX;
nsCSSValue mStartY;
nsCSSValue mEndX;
nsCSSValue mEndY;
// Only meaningful if mIsRadial is true
nsCSSValue mStartRadius;
nsCSSValue mEndRadius;
nsTArray<nsCSSValueGradientStop> mStops;
PRBool operator==(const nsCSSValueGradient& aOther) const
{
if (mIsRadial != aOther.mIsRadial ||
mStartX != aOther.mStartX ||
mStartY != aOther.mStartY ||
mStartRadius != aOther.mStartRadius ||
mEndX != aOther.mEndX ||
mEndY != aOther.mEndY ||
mEndRadius != aOther.mEndRadius)
return PR_FALSE;
if (mStops.Length() != aOther.mStops.Length())
return PR_FALSE;
for (PRUint32 i = 0; i < mStops.Length(); i++) {
if (mStops[i] != aOther.mStops[i])
return PR_FALSE;
}
return PR_TRUE;
}
PRBool operator!=(const nsCSSValueGradient& aOther) const
{
return !(*this == aOther);
}
void AddRef() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsCSSValue::Gradient");
return;
}
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsCSSValue::Gradient", sizeof(*this));
}
void Release() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsCSSValue::Gradient");
return;
}
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsCSSValue::Gradient");
if (mRefCnt == 0)
delete this;
}
private:
nsrefcnt mRefCnt;
// not to be implemented
nsCSSValueGradient(const nsCSSValueGradient& aOther);
nsCSSValueGradient& operator=(const nsCSSValueGradient& aOther);
};
struct nsCSSValue::Array {
// return |Array| with reference count of zero
@ -508,7 +635,9 @@ private:
#undef CSSVALUE_LIST_FOR_EXTRA_VALUES
private:
Array(const Array& aOther); // not to be implemented
// not to be implemented
Array(const Array& aOther);
Array& operator=(const Array& aOther);
};
#endif /* nsCSSValue_h___ */

View File

@ -1250,6 +1250,97 @@ nsComputedDOMStyle::GetBackgroundColor(nsIDOMCSSValue** aValue)
return CallQueryInterface(val, aValue);
}
static void
AppendCSSGradientLength(const nsStyleCoord& aValue,
nsROCSSPrimitiveValue* aPrimitive,
nsAString& aString)
{
nsAutoString tokenString;
if (aValue.GetUnit() == eStyleUnit_Coord)
aPrimitive->SetAppUnits(aValue.GetCoordValue());
else
aPrimitive->SetPercent(aValue.GetPercentValue());
aPrimitive->GetCssText(tokenString);
aString.Append(tokenString);
}
static void
AppendCSSGradientRadius(const nscoord aValue,
nsROCSSPrimitiveValue* aPrimitive,
nsAString& aString)
{
nsAutoString tokenString;
aPrimitive->SetAppUnits(aValue);
aPrimitive->GetCssText(tokenString);
aString.Append(tokenString);
}
nsresult
nsComputedDOMStyle::GetCSSGradientString(const nsStyleGradient* aGradient,
nsAString& aString)
{
if (aGradient->mIsRadial)
aString.AssignLiteral("-moz-radial-gradient(");
else
aString.AssignLiteral("-moz-linear-gradient(");
nsROCSSPrimitiveValue *tmpVal = GetROCSSPrimitiveValue();
if (!tmpVal)
return NS_ERROR_OUT_OF_MEMORY;
// starting X position
AppendCSSGradientLength(aGradient->mStartX, tmpVal, aString);
aString.AppendLiteral(" ");
// starting Y position
AppendCSSGradientLength(aGradient->mStartY, tmpVal, aString);
aString.AppendLiteral(", ");
// starting radius
if (aGradient->mIsRadial) {
AppendCSSGradientRadius(aGradient->mStartRadius, tmpVal, aString);
aString.AppendLiteral(", ");
}
// ending X position
AppendCSSGradientLength(aGradient->mEndX, tmpVal, aString);
aString.AppendLiteral(" ");
// ending Y position
AppendCSSGradientLength(aGradient->mEndY, tmpVal, aString);
// ending radius
if (aGradient->mIsRadial) {
aString.AppendLiteral(", ");
AppendCSSGradientRadius(aGradient->mStartRadius, tmpVal, aString);
}
// color stops
for (PRUint32 i = 0; i < aGradient->mStops.Length(); ++i) {
nsAutoString tokenString;
aString.AppendLiteral(", color-stop(");
tmpVal->SetPercent(aGradient->mStops[i].mPosition);
tmpVal->GetCssText(tokenString);
aString.Append(tokenString);
aString.AppendLiteral(", ");
nsresult rv = SetToRGBAColor(tmpVal, aGradient->mStops[i].mColor);
if (NS_FAILED(rv)) {
delete tmpVal;
return NS_ERROR_OUT_OF_MEMORY;
}
tmpVal->GetCssText(tokenString);
aString.Append(tokenString);
aString.AppendLiteral(")");
}
delete tmpVal;
aString.AppendLiteral(")");
return NS_OK;
}
nsresult
nsComputedDOMStyle::GetBackgroundImage(nsIDOMCSSValue** aValue)
{
@ -1266,13 +1357,27 @@ nsComputedDOMStyle::GetBackgroundImage(nsIDOMCSSValue** aValue)
return NS_ERROR_OUT_OF_MEMORY;
}
imgIRequest *image = bg->mLayers[i].mImage;
if (!image) {
val->SetIdent(eCSSKeyword_none);
const nsStyleBackground::Image &image = bg->mLayers[i].mImage;
if (image.GetType() == eBackgroundImage_Image) {
imgIRequest *req = image.GetImageData();
if (!req) {
val->SetIdent(eCSSKeyword_none);
} else {
nsCOMPtr<nsIURI> uri;
req->GetURI(getter_AddRefs(uri));
val->SetURI(uri);
}
} else if (image.GetType() == eBackgroundImage_Gradient) {
nsAutoString gradientString;
nsresult rv = GetCSSGradientString(image.GetGradientData(),
gradientString);
if (NS_FAILED(rv)) {
delete valueList;
return rv;
}
val->SetString(gradientString);
} else {
nsCOMPtr<nsIURI> uri;
image->GetURI(getter_AddRefs(uri));
val->SetURI(uri);
val->SetIdent(eCSSKeyword_none);
}
}

View File

@ -131,6 +131,9 @@ private:
const PRInt32 aTable[],
nsIDOMCSSValue** aResult);
nsresult GetCSSGradientString(const nsStyleGradient* aGradient,
nsAString& aString);
/* Properties Queryable as CSSValues */
nsresult GetAppearance(nsIDOMCSSValue** aValue);

View File

@ -471,6 +471,81 @@ static PRBool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
return result;
}
static void SetGradientCoord(const nsCSSValue& aValue, nsPresContext* aPresContext,
nsStyleContext* aContext, nsStyleCoord& aResult,
PRBool& aCanStoreInRuleTree)
{
// If coordinate is an enumerated type, handle it explicitly.
if (aValue.GetUnit() == eCSSUnit_Enumerated) {
aResult.SetPercentValue(GetFloatFromBoxPosition(aValue.GetIntValue()));
return;
}
// OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT
PRBool result = SetCoord(aValue, aResult, nsStyleCoord(), SETCOORD_LP,
aContext, aPresContext, aCanStoreInRuleTree);
NS_ABORT_IF_FALSE(result, "Incorrect data structure created by parsing code");
}
static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext,
nsStyleContext* aContext, nsStyleGradient& aResult,
PRBool& aCanStoreInRuleTree)
{
NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Gradient,
"The given data is not a gradient");
nsCSSValueGradient* gradient = aValue.GetGradientValue();
aResult.mIsRadial = gradient->mIsRadial;
// start values
SetGradientCoord(gradient->mStartX, aPresContext, aContext,
aResult.mStartX, aCanStoreInRuleTree);
SetGradientCoord(gradient->mStartY, aPresContext, aContext,
aResult.mStartY, aCanStoreInRuleTree);
if (gradient->mIsRadial) {
NS_ABORT_IF_FALSE(gradient->mStartRadius.IsLengthUnit(),
"Incorrect data structure created by parsing code");
aResult.mStartRadius = CalcLength(gradient->mStartRadius, aContext,
aPresContext, aCanStoreInRuleTree);
}
// end values
SetGradientCoord(gradient->mEndX, aPresContext, aContext,
aResult.mEndX, aCanStoreInRuleTree);
SetGradientCoord(gradient->mEndY, aPresContext, aContext,
aResult.mEndY, aCanStoreInRuleTree);
if (gradient->mIsRadial) {
NS_ABORT_IF_FALSE(gradient->mEndRadius.IsLengthUnit(),
"Incorrect data structure created by parsing code");
aResult.mEndRadius = CalcLength(gradient->mEndRadius, aContext,
aPresContext, aCanStoreInRuleTree);
}
// stops
for (PRUint32 i = 0; i < gradient->mStops.Length(); i++) {
nsStyleGradientStop stop;
nsCSSValueGradientStop &valueStop = gradient->mStops[i];
if (valueStop.mLocation.GetUnit() == eCSSUnit_Percent)
stop.mPosition = valueStop.mLocation.GetPercentValue();
else
stop.mPosition = valueStop.mLocation.GetFloatValue();
// inherit is not a valid color for stops, so we pass in a dummy
// parent color
NS_ASSERTION(valueStop.mColor.GetUnit() != eCSSUnit_Inherit,
"inherit is not a valid color for gradient stops");
SetColor(valueStop.mColor, NS_RGB(0, 0, 0), aPresContext,
aContext, stop.mColor, aCanStoreInRuleTree);
aResult.mStops.AppendElement(stop);
}
}
// flags for SetDiscrete - align values with SETCOORD_* constants
// where possible
@ -3803,20 +3878,30 @@ struct BackgroundItemComputer<nsCSSValueList, PRUint8>
};
NS_SPECIALIZE_TEMPLATE
struct BackgroundItemComputer<nsCSSValueList, nsCOMPtr<imgIRequest> >
struct BackgroundItemComputer<nsCSSValueList, nsStyleBackground::Image>
{
static void ComputeValue(nsStyleContext* aStyleContext,
const nsCSSValueList* aSpecifiedValue,
nsCOMPtr<imgIRequest>& aComputedValue,
nsStyleBackground::Image& aComputedValue,
PRBool& aCanStoreInRuleTree)
{
const nsCSSValue &value = aSpecifiedValue->mValue;
if (eCSSUnit_Image == value.GetUnit()) {
aComputedValue = value.GetImageValue();
aComputedValue.SetImageData(value.GetImageValue());
}
else if (eCSSUnit_Gradient == value.GetUnit()) {
nsStyleGradient* gradient = new nsStyleGradient();
if (gradient) {
SetGradient(value, aStyleContext->PresContext(), aStyleContext,
*gradient, aCanStoreInRuleTree);
aComputedValue.SetGradientData(gradient);
} else {
aComputedValue.SetNull();
}
}
else {
NS_ASSERTION(eCSSUnit_None == value.GetUnit(), "unexpected unit");
aComputedValue = nsnull;
aComputedValue.SetNull();
}
}
};
@ -4056,10 +4141,11 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct,
PRBool rebuild = PR_FALSE;
// background-image: url (stored as image), none, inherit [list]
nsStyleBackground::Image initialImage;
SetBackgroundList(aContext, colorData.mBackImage, bg->mLayers,
parentBG->mLayers, &nsStyleBackground::Layer::mImage,
nsCOMPtr<imgIRequest>(nsnull), parentBG->mImageCount,
bg->mImageCount, maxItemCount, rebuild, canStoreInRuleTree);
initialImage, parentBG->mImageCount, bg->mImageCount,
maxItemCount, rebuild, canStoreInRuleTree);
// background-repeat: enum, inherit, initial [list]
SetBackgroundList(aContext, colorData.mBackRepeat, bg->mLayers,

View File

@ -1206,6 +1206,51 @@ nsChangeHint nsStyleColor::MaxDifference()
}
#endif
// --------------------
// nsStyleGradient
//
PRBool
nsStyleGradient::operator==(const nsStyleGradient& aOther) const
{
NS_ABORT_IF_FALSE(mIsRadial || (mStartRadius == 0 && mEndRadius == 0),
"incorrect unused radius values");
NS_ABORT_IF_FALSE(aOther.mIsRadial ||
(aOther.mStartRadius == 0 && aOther.mEndRadius == 0),
"incorrect unused radius values");
if (mIsRadial != aOther.mIsRadial ||
mStartX != aOther.mStartX ||
mStartY != aOther.mStartY ||
mStartRadius != aOther.mStartRadius ||
mEndX != aOther.mEndX ||
mEndY != aOther.mEndY ||
mEndRadius != aOther.mEndRadius)
return PR_FALSE;
if (mStops.Length() != aOther.mStops.Length())
return PR_FALSE;
for (PRUint32 i = 0; i < mStops.Length(); i++) {
if (mStops[i].mPosition != aOther.mStops[i].mPosition ||
mStops[i].mColor != aOther.mStops[i].mColor)
return PR_FALSE;
}
return PR_TRUE;
}
nsStyleGradient::nsStyleGradient(void)
: mIsRadial(PR_FALSE)
, mStartRadius(0)
, mEndRadius(0)
, mRefCnt(0)
{
mStartX.SetCoordValue(0);
mStartY.SetCoordValue(0);
mEndX.SetCoordValue(0);
mEndY.SetCoordValue(0);
}
// --------------------
// nsStyleBackground
//
@ -1284,7 +1329,8 @@ PRBool nsStyleBackground::HasFixedBackground() const
{
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, this) {
const Layer &layer = mLayers[i];
if (layer.mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED && layer.mImage) {
if (layer.mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED &&
layer.mImage.GetType() != eBackgroundImage_Null) {
return PR_TRUE;
}
}
@ -1293,7 +1339,8 @@ PRBool nsStyleBackground::HasFixedBackground() const
PRBool nsStyleBackground::IsTransparent() const
{
return !BottomLayer().mImage && mImageCount == 1 &&
return BottomLayer().mImage.GetType() == eBackgroundImage_Null &&
mImageCount == 1 &&
NS_GET_A(mBackgroundColor) == 0;
}
@ -1363,7 +1410,7 @@ nsStyleBackground::Layer::SetInitialValues()
mRepeat = NS_STYLE_BG_REPEAT_XY;
mPosition.SetInitialValues();
mSize.SetInitialValues();
mImage = nsnull;
mImage.SetNull();
}
PRBool nsStyleBackground::Layer::operator==(const Layer& aOther) const
@ -1374,7 +1421,99 @@ PRBool nsStyleBackground::Layer::operator==(const Layer& aOther) const
mRepeat == aOther.mRepeat &&
mPosition == aOther.mPosition &&
mSize == aOther.mSize &&
EqualImages(mImage, aOther.mImage);
mImage == aOther.mImage;
}
nsStyleBackground::Image::Image()
{
MOZ_COUNT_CTOR(nsStyleBackground::Image);
mType = eBackgroundImage_Null;
}
nsStyleBackground::Image::~Image()
{
MOZ_COUNT_DTOR(nsStyleBackground::Image);
if (mType != eBackgroundImage_Null)
SetNull();
}
nsStyleBackground::Image::Image(const nsStyleBackground::Image& aOther)
{
// We need our own copy constructor because we don't want
// to copy the reference count
MOZ_COUNT_CTOR(nsStyleBackground::Image);
DoCopy(aOther);
}
nsStyleBackground::Image&
nsStyleBackground::Image::operator=(const nsStyleBackground::Image& aOther)
{
DoCopy(aOther);
return *this;
}
void nsStyleBackground::Image::DoCopy(const nsStyleBackground::Image& aOther)
{
if (this == &aOther)
return;
SetNull();
if (aOther.mType == eBackgroundImage_Image)
SetImageData(aOther.mImage);
else if (aOther.mType == eBackgroundImage_Gradient)
SetGradientData(aOther.mGradient);
}
void nsStyleBackground::Image::SetImageData(imgIRequest* aImage)
{
NS_IF_ADDREF(aImage);
if (mType != eBackgroundImage_Null)
SetNull();
if (aImage) {
mImage = aImage;
mType = eBackgroundImage_Image;
}
}
void nsStyleBackground::Image::SetGradientData(nsStyleGradient* aGradient)
{
if (aGradient)
aGradient->AddRef();
if (mType != eBackgroundImage_Null)
SetNull();
if (aGradient) {
mGradient = aGradient;
mType = eBackgroundImage_Gradient;
}
}
void nsStyleBackground::Image::SetNull()
{
if (mType == eBackgroundImage_Gradient)
mGradient->Release();
else if (mType == eBackgroundImage_Image)
NS_RELEASE(mImage);
mType = eBackgroundImage_Null;
}
PRBool nsStyleBackground::Image::operator==(const Image& aOther) const
{
if (mType != aOther.mType)
return PR_FALSE;
if (mType == eBackgroundImage_Image)
return EqualImages(mImage, aOther.mImage);
if (mType == eBackgroundImage_Gradient)
return *mGradient == *aOther.mGradient;
return PR_TRUE;
}
// --------------------

View File

@ -124,6 +124,68 @@ struct nsStyleFont {
#endif
};
struct nsStyleGradientStop {
nscolor mColor;
float mPosition; // 0.0 - 1.0
};
class nsStyleGradient {
public:
nsStyleGradient();
PRPackedBool mIsRadial;
nsStyleCoord mStartX; // percent or coord
nsStyleCoord mStartY; // percent or coord
nsStyleCoord mEndX; // percent or coord
nsStyleCoord mEndY; // percent or coord
nscoord mStartRadius;
nscoord mEndRadius;
// stops are in the order specified in the stylesheet
nsTArray<nsStyleGradientStop> mStops;
nsrefcnt AddRef() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsStyleGradient");
return mRefCnt;
}
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsStyleGradient", sizeof(*this));
return mRefCnt;
}
nsrefcnt Release() {
if (mRefCnt == PR_UINT32_MAX) {
NS_WARNING("refcount overflow, leaking nsStyleGradient");
return mRefCnt;
}
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsStyleGradient");
if (mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
PRBool operator==(const nsStyleGradient& aOther) const;
PRBool operator!=(const nsStyleGradient& aOther) const {
return !(*this == aOther);
};
private:
nsrefcnt mRefCnt;
~nsStyleGradient() {}
// Not to be implemented
nsStyleGradient(const nsStyleGradient& aOther);
nsStyleGradient& operator=(const nsStyleGradient& aOther);
};
struct nsStyleColor {
nsStyleColor(nsPresContext* aPresContext);
nsStyleColor(const nsStyleColor& aOther);
@ -147,6 +209,12 @@ struct nsStyleColor {
nscolor mColor; // [inherited]
};
enum nsStyleBackgroundImageType {
eBackgroundImage_Null,
eBackgroundImage_Image,
eBackgroundImage_Gradient
};
struct nsStyleBackground {
nsStyleBackground();
nsStyleBackground(const nsStyleBackground& aOther);
@ -228,6 +296,46 @@ struct nsStyleBackground {
}
};
struct Image;
friend struct Image;
struct Image {
public:
Image();
~Image();
Image(const Image& aOther);
Image& operator=(const Image& aOther);
void SetImageData(imgIRequest* aImage);
void SetGradientData(nsStyleGradient* aGradient);
void SetNull();
nsStyleBackgroundImageType GetType() const {
return mType;
};
imgIRequest* GetImageData() const {
NS_ASSERTION(mType == eBackgroundImage_Image, "Data is not an image!");
return mImage;
};
nsStyleGradient* GetGradientData() const {
NS_ASSERTION(mType == eBackgroundImage_Gradient, "Data is not a gradient!");
return mGradient;
};
PRBool operator==(const Image& aOther) const;
PRBool operator!=(const Image& aOther) const {
return !(*this == aOther);
}
private:
void DoCopy(const Image& aOther);
nsStyleBackgroundImageType mType;
union {
imgIRequest* mImage;
nsStyleGradient* mGradient;
};
};
struct Layer;
friend struct Layer;
struct Layer {
@ -236,7 +344,7 @@ struct nsStyleBackground {
PRUint8 mOrigin; // [reset] See nsStyleConsts.h
PRUint8 mRepeat; // [reset] See nsStyleConsts.h
Position mPosition; // [reset]
nsCOMPtr<imgIRequest> mImage; // [reset]
Image mImage; // [reset]
Size mSize; // [reset]
// Initializes only mImage

View File

@ -625,7 +625,7 @@ var gCSSProperties = {
initial_values: [ "transparent", "none", "repeat", "scroll", "0% 0%", "top left", "left top", "transparent none", "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "transparent none repeat scroll top left", "left top repeat none scroll transparent" ],
other_values: [
/* without multiple backgrounds */
"green", "none green repeat scroll left top", "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') transparent left top scroll", "repeat-x", "repeat-y", "no-repeat", "none repeat-y transparent scroll 0% 0%", "fixed", "0% top transparent fixed repeat none", "top", "left", "50% 50%", "center", "bottom right scroll none transparent repeat", "50% transparent", "transparent 50%", "50%",
"green", "none green repeat scroll left top", "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') transparent left top scroll", "repeat-x", "repeat-y", "no-repeat", "none repeat-y transparent scroll 0% 0%", "fixed", "0% top transparent fixed repeat none", "top", "left", "50% 50%", "center", "bottom right scroll none transparent repeat", "50% transparent", "transparent 50%", "50%", "-moz-radial-gradient(10% bottom, 30px, 20px 20px, 10px, from(#ffffff), to(black)) scroll no-repeat", "-moz-linear-gradient(10px 10px, 20px 20px, from(red), to(blue)) repeat",
/* multiple backgrounds */
"url(404.png), url(404.png)",
"url(404.png), url(404.png) transparent",
@ -633,6 +633,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, 30px 30px, from(blue), to(green)) black",
/* test cases with clip+origin in the shorthand */
// This is commented out for now until we change
// -moz-background-clip to background-clip, -moz-background-origin
@ -688,9 +689,30 @@ var gCSSProperties = {
"none, none, none, none, none",
"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==)"
"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)",
"-moz-radial-gradient(45px 60px, 10px, 52px 50px, 30px, from(red), color-stop(90%, #019f62), to(rgba(1, 159, 98, 0)))",
"-moz-linear-gradient(20% center, left bottom, color-stop(0, #ffff00), to(green))",
"-moz-radial-gradient(50% 50%, 60px, 10% 20%, 20px, to(red), from(pink), color-stop(0.94, goldenrod))",
"-moz-linear-gradient(20px 10px, 100px top, from(rgb(10, 100, 0)), color-stop(1, rgba(10, 20, 30, 0.4)))",
"-moz-radial-gradient(center top, 10px, right bottom, 20px, color-stop(0.7, green), color-stop(20%, #ccddee), from(rgb(1, 2, 3)), color-stop(0.95, #aa0012))",
"-moz-linear-gradient(10px 20%, -50px -10%, from(green))",
"-moz-linear-gradient(10px 10px, 20px 20px)",
"-moz-linear-gradient(10px 10px, 20px 20px, color-stop(314%, blue))",
"-moz-linear-gradient(10px 10px, 20px 20px, color-stop(-0.2, green))",
"-moz-linear-gradient( 10px 10px , 40px 20px , from(blue) )"
],
invalid_values: []
invalid_values: [ "-moz-linear-gradient(10px 10px, 20px, 30px 30px, 40px, from(blue), to(red))",
"-moz-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))",
"-moz-radial-gradient(10px 10px, 20%, 40px 40px, 10px, from(green), to(#ff00ff))",
"-moz-linear-gradient(10px, 20px, 30px, 40px, color-stop(0.5, #00ccff))",
"-moz-linear-gradient(20px 20px, from(blue), to(red))",
"-moz-linear-gradient(40px 40px, 10px 10px, from(blue) to(red) color-stop(10%, fuchsia))",
"-moz-linear-gradient(20px 20px 30px, 10px 10px, from(red), to(#ff0000))",
"-moz-radial-gradient(left top, center, 20px 20px, 10px, from(blue), to(red))",
"-moz-linear-gradient(left left, top top, from(blue))",
"-moz-linear-gradient(inherit, 10px 10px, from(blue))",
"-moz-linear-gradient()" ]
},
"background-position": {
domProp: "backgroundPosition",

View File

@ -202,7 +202,7 @@ TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder()
}
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, mBackground) {
if (mBackground->mLayers[i].mImage)
if (mBackground->mLayers[i].mImage.GetType() != eBackgroundImage_Null)
return PR_TRUE;
}
return PR_FALSE;

View File

@ -4118,7 +4118,7 @@ nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, PRInt32 aRow)
// See if we have a transparent background or a background image.
// If we do, then we cannot blit.
const nsStyleBackground* background = GetStyleBackground();
if (background->BottomLayer().mImage ||
if (background->BottomLayer().mImage.GetType() != eBackgroundImage_Null ||
background->mImageCount > 1 ||
NS_GET_A(background->mBackgroundColor) < 255 ||
PR_ABS(delta)*mRowHeight >= mRect.height) {
@ -4175,7 +4175,7 @@ nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, PRInt32 aPosition
// See if we have a transparent background or a background image.
// If we do, then we cannot blit.
const nsStyleBackground* background = GetStyleBackground();
if (background->BottomLayer().mImage ||
if (background->BottomLayer().mImage.GetType() != eBackgroundImage_Null ||
background->mImageCount > 1 ||
NS_GET_A(background->mBackgroundColor) < 255 ||
PR_ABS(delta) >= mRect.width) {