From 94595f2f88ccf3cc5becebfbd2571bac94f6fbba Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Thu, 19 Feb 2009 21:29:21 -0800 Subject: [PATCH] Implement multiple background layers, implement fallback background color, and make -moz-background-inline-policy no longer be part of the background shorthand. (Bug 322475) r+sr=roc,bzbarsky --- accessible/src/base/nsTextAttrs.cpp | 4 +- .../html/content/src/nsGenericHTMLElement.cpp | 22 +- .../mathml/content/src/nsMathMLElement.cpp | 9 +- gfx/thebes/public/gfxContext.h | 10 + layout/base/nsCSSRendering.cpp | 416 +++++++++++------- layout/base/nsDisplayList.cpp | 8 +- layout/base/nsLayoutUtils.cpp | 7 +- layout/base/nsPresContext.cpp | 10 +- layout/base/nsStyleConsts.h | 12 +- layout/generic/nsFrame.cpp | 33 +- layout/reftests/backgrounds/aqua-32x32.png | Bin 0 -> 110 bytes layout/reftests/backgrounds/blue-32x32.png | Bin 0 -> 110 bytes .../backgrounds/fallback-color-1.xhtml | 25 ++ .../backgrounds/fallback-color-2.xhtml | 25 ++ .../backgrounds/fallback-color-3.xhtml | 25 ++ .../backgrounds/fallback-color-4.xhtml | 25 ++ .../backgrounds/fallback-color-5.xhtml | 25 ++ .../backgrounds/fallback-color-6.xhtml | 25 ++ .../backgrounds/fallback-color-7.xhtml | 25 ++ .../backgrounds/fallback-color-8.xhtml | 25 ++ .../backgrounds/fallback-color-9.xhtml | 25 ++ .../backgrounds/fallback-color-ref.xhtml | 24 + layout/reftests/backgrounds/fuchsia-32x32.png | Bin 0 -> 110 bytes .../layers-layer-count-1-ref.xhtml | 35 ++ .../layers-layer-count-2-ref.xhtml | 35 ++ .../layers-layer-count-cascade-1.xhtml | 36 ++ .../layers-layer-count-cascade-2.xhtml | 36 ++ .../layers-layer-count-inheritance-1.xhtml | 39 ++ .../layers-layer-count-inheritance-2.xhtml | 39 ++ .../layers-stacking-order-ref.xhtml | 28 ++ .../backgrounds/layers-stacking-order.xhtml | 27 ++ layout/reftests/backgrounds/lime-32x32.png | Bin 0 -> 110 bytes layout/reftests/backgrounds/malformed.png | 1 + layout/reftests/backgrounds/red-32x32.png | Bin 0 -> 110 bytes layout/reftests/backgrounds/reftest.list | 15 + .../backgrounds/transparent-32x32.png | Bin 0 -> 96 bytes layout/reftests/backgrounds/yellow-32x32.png | Bin 0 -> 110 bytes layout/reftests/bugs/289480.html | 2 +- layout/reftests/reftest.list | 3 + layout/style/nsCSSDataBlock.cpp | 77 ++-- layout/style/nsCSSDeclaration.cpp | 138 ++++-- layout/style/nsCSSParser.cpp | 369 ++++++++++++---- layout/style/nsCSSPropList.h | 14 +- layout/style/nsCSSProps.cpp | 5 +- layout/style/nsCSSStruct.cpp | 13 + layout/style/nsCSSStruct.h | 19 +- layout/style/nsComputedDOMStyle.cpp | 213 +++++---- layout/style/nsComputedDOMStyle.h | 6 + layout/style/nsRuleNode.cpp | 383 +++++++++++----- layout/style/nsStyleStruct.cpp | 157 +++++-- layout/style/nsStyleStruct.h | 131 +++++- layout/style/test/property_database.js | 76 +++- .../test/test_shorthand_property_getters.html | 16 +- layout/tables/nsTablePainter.cpp | 10 +- .../xul/base/src/tree/src/nsTreeBodyFrame.cpp | 11 +- xpcom/glue/nsTArray.h | 14 + 56 files changed, 2090 insertions(+), 638 deletions(-) create mode 100644 layout/reftests/backgrounds/aqua-32x32.png create mode 100644 layout/reftests/backgrounds/blue-32x32.png create mode 100644 layout/reftests/backgrounds/fallback-color-1.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-2.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-3.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-4.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-5.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-6.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-7.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-8.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-9.xhtml create mode 100644 layout/reftests/backgrounds/fallback-color-ref.xhtml create mode 100644 layout/reftests/backgrounds/fuchsia-32x32.png create mode 100644 layout/reftests/backgrounds/layers-layer-count-1-ref.xhtml create mode 100644 layout/reftests/backgrounds/layers-layer-count-2-ref.xhtml create mode 100644 layout/reftests/backgrounds/layers-layer-count-cascade-1.xhtml create mode 100644 layout/reftests/backgrounds/layers-layer-count-cascade-2.xhtml create mode 100644 layout/reftests/backgrounds/layers-layer-count-inheritance-1.xhtml create mode 100644 layout/reftests/backgrounds/layers-layer-count-inheritance-2.xhtml create mode 100644 layout/reftests/backgrounds/layers-stacking-order-ref.xhtml create mode 100644 layout/reftests/backgrounds/layers-stacking-order.xhtml create mode 100644 layout/reftests/backgrounds/lime-32x32.png create mode 100644 layout/reftests/backgrounds/malformed.png create mode 100644 layout/reftests/backgrounds/red-32x32.png create mode 100644 layout/reftests/backgrounds/reftest.list create mode 100644 layout/reftests/backgrounds/transparent-32x32.png create mode 100644 layout/reftests/backgrounds/yellow-32x32.png diff --git a/accessible/src/base/nsTextAttrs.cpp b/accessible/src/base/nsTextAttrs.cpp index b7ee8c3615b..8f0959a2579 100644 --- a/accessible/src/base/nsTextAttrs.cpp +++ b/accessible/src/base/nsTextAttrs.cpp @@ -526,8 +526,8 @@ nsBGColorTextAttr::GetColor(nsIFrame *aFrame, nscolor *aColor) { const nsStyleBackground *styleBackground = aFrame->GetStyleBackground(); - if (!styleBackground->IsTransparent()) { - *aColor = styleBackground->mBackgroundColor; + if (NS_GET_A(styleBackground->mFallbackBackgroundColor) > 0) { + *aColor = styleBackground->mFallbackBackgroundColor; return PR_TRUE; } diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 644671ef60a..8d2fef0bee0 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -1941,8 +1941,7 @@ nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes, return; nsPresContext* presContext = aData->mPresContext; - if (aData->mColorData->mBackImage.GetUnit() == eCSSUnit_Null && - presContext->UseDocumentColors()) { + if (!aData->mColorData->mBackImage && presContext->UseDocumentColors()) { // background const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::background); if (value && value->Type() == nsAttrValue::eString) { @@ -1972,7 +1971,11 @@ nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes, doc->NodePrincipal(), doc); buffer->Release(); if (NS_LIKELY(img != 0)) { - aData->mColorData->mBackImage.SetImageValue(img); + // Use nsRuleDataColor's temporary mTempBackImage to + // make a value list. + aData->mColorData->mTempBackImage.mValue.SetImageValue(img); + aData->mColorData->mBackImage = + &aData->mColorData->mTempBackImage; } } } @@ -1980,7 +1983,10 @@ nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes, else if (presContext->CompatibilityMode() == eCompatibility_NavQuirks) { // in NavQuirks mode, allow the empty string to set the // background to empty - aData->mColorData->mBackImage.SetNoneValue(); + // Use nsRuleDataColor's temporary mTempBackImage to make a value list. + aData->mColorData->mBackImage = nsnull; + aData->mColorData->mTempBackImage.mValue.SetNoneValue(); + aData->mColorData->mBackImage = &aData->mColorData->mTempBackImage; } } } @@ -1993,12 +1999,16 @@ nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes, if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Background))) return; - if (aData->mColorData->mBackColor.GetUnit() == eCSSUnit_Null && + if (aData->mColorData->mBackColor.mXValue.GetUnit() == eCSSUnit_Null && aData->mPresContext->UseDocumentColors()) { + NS_ASSERTION(aData->mColorData->mBackColor.mYValue.GetUnit() == + eCSSUnit_Null, + "half a property?"); const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bgcolor); nscolor color; if (value && value->GetColorValue(color)) { - aData->mColorData->mBackColor.SetColorValue(color); + aData->mColorData->mBackColor.mXValue.SetColorValue(color); + aData->mColorData->mBackColor.mYValue.SetColorValue(color); } } } diff --git a/content/mathml/content/src/nsMathMLElement.cpp b/content/mathml/content/src/nsMathMLElement.cpp index 4fc83012f38..60c1e7a68cf 100644 --- a/content/mathml/content/src/nsMathMLElement.cpp +++ b/content/mathml/content/src/nsMathMLElement.cpp @@ -377,10 +377,15 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, if (!value) { value = aAttributes->GetAttr(nsGkAtoms::background); } - if (value && aData->mColorData->mBackColor.GetUnit() == eCSSUnit_Null) { + if (value && + aData->mColorData->mBackColor.mXValue.GetUnit() == eCSSUnit_Null) { + NS_ASSERTION(aData->mColorData->mBackColor.mYValue.GetUnit() + == eCSSUnit_Null, + "half a property?"); nscolor color; if (value->GetColorValue(color)) { - aData->mColorData->mBackColor.SetColorValue(color); + aData->mColorData->mBackColor.mXValue.SetColorValue(color); + aData->mColorData->mBackColor.mYValue.SetColorValue(color); } } } diff --git a/gfx/thebes/public/gfxContext.h b/gfx/thebes/public/gfxContext.h index 8362505a9d3..c5df2c534b3 100644 --- a/gfx/thebes/public/gfxContext.h +++ b/gfx/thebes/public/gfxContext.h @@ -676,6 +676,16 @@ public: mContext->Save(); } + void Reset(gfxContext *aContext) { + // Do the equivalent of destroying and re-creating this object. + NS_PRECONDITION(aContext, "must provide a context"); + if (mContext) { + mContext->Restore(); + } + mContext = aContext; + mContext->Save(); + } + private: gfxContext *mContext; }; diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 54ae91257c4..f2250261a89 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -262,6 +262,17 @@ protected: }; /* Local functions */ +static void PaintBackgroundLayer(nsPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aDirtyRect, + const nsRect& aBorderArea, + const nsRect& aBGClipRect, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer, + const nsStyleBorder& aBorder, + PRBool aUsePrintSettings); + static void DrawBorderImage(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIFrame* aForFrame, @@ -800,35 +811,29 @@ nsCSSRendering::PaintFocus(nsPresContext* aPresContext, * Points are returned relative to aOriginBounds. */ static void -ComputeBackgroundAnchorPoint(const nsStyleBackground& aColor, +ComputeBackgroundAnchorPoint(const nsStyleBackground::Layer& aLayer, const nsSize& aOriginBounds, const nsSize& aImageSize, nsPoint* aTopLeft, nsPoint* aAnchorPoint) { - if (NS_STYLE_BG_X_POSITION_LENGTH & aColor.mBackgroundFlags) { - aTopLeft->x = aAnchorPoint->x = aColor.mBackgroundXPosition.mCoord; + if (!aLayer.mPosition.mXIsPercent) { + aTopLeft->x = aAnchorPoint->x = aLayer.mPosition.mXPosition.mCoord; } - else if (NS_STYLE_BG_X_POSITION_PERCENT & aColor.mBackgroundFlags) { - double percent = aColor.mBackgroundXPosition.mFloat; + else { + double percent = aLayer.mPosition.mXPosition.mFloat; aAnchorPoint->x = NSToCoordRound(percent*aOriginBounds.width); aTopLeft->x = NSToCoordRound(percent*(aOriginBounds.width - aImageSize.width)); } - else { - aTopLeft->x = aAnchorPoint->x = 0; - } - if (NS_STYLE_BG_Y_POSITION_LENGTH & aColor.mBackgroundFlags) { - aTopLeft->y = aAnchorPoint->y = aColor.mBackgroundYPosition.mCoord; + if (!aLayer.mPosition.mYIsPercent) { + aTopLeft->y = aAnchorPoint->y = aLayer.mPosition.mYPosition.mCoord; } - else if (NS_STYLE_BG_Y_POSITION_PERCENT & aColor.mBackgroundFlags) { - double percent = aColor.mBackgroundYPosition.mFloat; + else { + double percent = aLayer.mPosition.mYPosition.mFloat; aAnchorPoint->y = NSToCoordRound(percent*aOriginBounds.height); aTopLeft->y = NSToCoordRound(percent*(aOriginBounds.height - aImageSize.height)); } - else { - aTopLeft->y = aAnchorPoint->y = 0; - } } nsStyleContext* @@ -1342,6 +1347,103 @@ IsSolidBorder(const nsStyleBorder& aBorder) return PR_TRUE; } +static PRBool +UseImageRequestForBackground(imgIRequest *aRequest) +{ + if (!aRequest) + return PR_FALSE; + + PRUint32 status = imgIRequest::STATUS_ERROR; + aRequest->GetImageStatus(&status); + + return (status & imgIRequest::STATUS_FRAME_COMPLETE) && + (status & imgIRequest::STATUS_SIZE_AVAILABLE); +} + +static inline void +SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect, + nscoord aAppUnitsPerPixel, + /* OUT: */ + nsRect* aDirtyRect, gfxRect* aDirtyRectGfx) +{ + aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect); + + // Compute the Thebes equivalent of the dirtyRect. + *aDirtyRectGfx = RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel); + NS_WARN_IF_FALSE(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(), + "converted dirty rect should not be empty"); + NS_ABORT_IF_FALSE(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(), + "second should be empty if first is"); +} + +static void +SetupBackgroundClip(gfxContext *aCtx, PRUint8 aBackgroundClip, + nsIFrame* aForFrame, const nsRect& aBorderArea, + const nsRect& aCallerDirtyRect, PRBool aHaveRoundedCorners, + const gfxCornerSizes& aBGRadii, nscoord aAppUnitsPerPixel, + gfxContextAutoSaveRestore* aAutoSR, + /* OUT: */ + nsRect* aBGClipArea, nsRect* aDirtyRect, + gfxRect* aDirtyRectGfx) +{ + *aBGClipArea = aBorderArea; + PRBool radiiAreOuter = PR_TRUE; + gfxCornerSizes clippedRadii = aBGRadii; + if (aBackgroundClip != NS_STYLE_BG_CLIP_BORDER) { + NS_ASSERTION(aBackgroundClip == NS_STYLE_BG_CLIP_PADDING, + "unexpected background-clip"); + nsMargin border = aForFrame->GetUsedBorder(); + aForFrame->ApplySkipSides(border); + aBGClipArea->Deflate(border); + + if (aHaveRoundedCorners) { + gfxFloat borderSizes[4] = { + border.top / aAppUnitsPerPixel, border.right / aAppUnitsPerPixel, + border.bottom / aAppUnitsPerPixel, border.left / aAppUnitsPerPixel + }; + nsCSSBorderRenderer::ComputeInnerRadii(aBGRadii, borderSizes, + &clippedRadii); + radiiAreOuter = PR_FALSE; + } + } + + SetupDirtyRects(*aBGClipArea, aCallerDirtyRect, aAppUnitsPerPixel, + aDirtyRect, aDirtyRectGfx); + + if (aDirtyRectGfx->IsEmpty()) { + // Our caller won't draw anything under this condition, so no need + // to set more up. + return; + } + + // If we have rounded corners, clip all subsequent drawing to the + // rounded rectangle defined by bgArea and bgRadii (we don't know + // whether the rounded corners intrude on the dirtyRect or not). + // Do not do this if we have a caller-provided clip rect -- + // as above with bgArea, arguably a bug, but table painting seems + // to depend on it. + + if (aHaveRoundedCorners) { + gfxRect bgAreaGfx(RectToGfxRect(*aBGClipArea, aAppUnitsPerPixel)); + bgAreaGfx.Round(); + bgAreaGfx.Condition(); + + if (bgAreaGfx.IsEmpty()) { + // I think it's become possible to hit this since + // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed. + NS_WARNING("converted background area should not be empty"); + // Make our caller not do anything. + aDirtyRectGfx->size.SizeTo(0.0, 0.0); + return; + } + + aAutoSR->Reset(aCtx); + aCtx->NewPath(); + aCtx->RoundedRectangle(bgAreaGfx, clippedRadii, radiiAreOuter); + aCtx->Clip(); + } +} + void nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -1382,11 +1484,16 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, drawBackgroundColor = aPresContext->GetBackgroundColorDraw(); } - if ((aColor.mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) || - !aColor.mBackgroundImage) { - NS_ASSERTION((aColor.mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) && - !aColor.mBackgroundImage, "background flags/image mismatch"); - drawBackgroundImage = PR_FALSE; + nsStyleBackground::Image bottomImage(aColor.BottomLayer().mImage); + PRBool useFallbackColor = PR_FALSE; + if (bottomImage.mSpecified) { + if (!drawBackgroundImage || + !UseImageRequestForBackground(bottomImage.mRequest)) { + bottomImage.mRequest = nsnull; + } + useFallbackColor = bottomImage.mRequest == nsnull; + } else { + NS_ASSERTION(bottomImage.mRequest == nsnull, "malformed image struct"); } // If GetBackgroundColorDraw() is false, we are still expected to @@ -1395,7 +1502,8 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // color was specified. nscolor bgColor; if (drawBackgroundColor) { - bgColor = aColor.mBackgroundColor; + bgColor = useFallbackColor ? aColor.mFallbackBackgroundColor + : aColor.mBackgroundColor; if (NS_GET_A(bgColor) == 0) drawBackgroundColor = PR_FALSE; } else { @@ -1415,10 +1523,8 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel(); // Same coordinate space as aBorderArea & aBGClipRect - nsRect bgArea; gfxCornerSizes bgRadii; PRBool haveRoundedCorners; - PRBool radiiAreOuter = PR_TRUE; { nscoord radii[8]; haveRoundedCorners = @@ -1429,79 +1535,38 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, appUnitsPerPixel, &bgRadii); } - // The background is rendered over the 'background-clip' area, - // which is normally equal to the border area but may be reduced - // to the padding area by CSS. Also, if the border is solid, we - // don't need to draw outside the padding area. In either case, - // if the borders are rounded, make sure we use the same inner - // radii as the border code will. - bgArea = aBorderArea; - if (aColor.mBackgroundClip != NS_STYLE_BG_CLIP_BORDER || - IsSolidBorder(aBorder)) { - nsMargin border = aForFrame->GetUsedBorder(); - aForFrame->ApplySkipSides(border); - bgArea.Deflate(border); - if (haveRoundedCorners) { - gfxCornerSizes outerRadii = bgRadii; - gfxFloat borderSizes[4] = { - border.top / appUnitsPerPixel, border.right / appUnitsPerPixel, - border.bottom / appUnitsPerPixel, border.left / appUnitsPerPixel - }; - nsCSSBorderRenderer::ComputeInnerRadii(outerRadii, borderSizes, - &bgRadii); - radiiAreOuter = PR_FALSE; - } - } - // The 'bgClipArea' (used only by the image tiling logic, far below) - // is the caller-provided aBGClipRect if any, or else the bgArea - // computed above. (Arguably it should be the intersection, but - // that breaks the table painter -- in particular, honoring the - // bgArea when we have aBGClipRect breaks reftests/bugs/403429-1[ab].) - // The dirtyRect is the intersection of that rectangle with the - // caller-provided aDirtyRect. If the dirtyRect is empty there is - // nothing to draw. - - nsRect bgClipArea; - if (aBGClipRect) - bgClipArea = *aBGClipRect; - else - bgClipArea = bgArea; - - nsRect dirtyRect; - dirtyRect.IntersectRect(bgClipArea, aDirtyRect); - - if (dirtyRect.IsEmpty()) - return; - - // Compute the Thebes equivalent of the dirtyRect. - gfxRect dirtyRectGfx(RectToGfxRect(dirtyRect, appUnitsPerPixel)); - if (dirtyRectGfx.IsEmpty()) { - NS_WARNING("converted dirty rect should not be empty"); - return; - } - - // If we have rounded corners, clip all subsequent drawing to the - // rounded rectangle defined by bgArea and bgRadii (we don't know - // whether the rounded corners intrude on the dirtyRect or not). - // Do not do this if we have a caller-provided clip rect -- - // as above with bgArea, arguably a bug, but table painting seems - // to depend on it. - + // is the caller-provided aBGClipRect if any, or else the area + // determined by the value of 'background-clip' in + // SetupCurrentBackgroundClip. (Arguably it should be the + // intersection, but that breaks the table painter -- in particular, + // taking the intersection breaks reftests/bugs/403429-1[ab].) + nsRect bgClipArea, dirtyRect; + gfxRect dirtyRectGfx; + PRUint8 currentBackgroundClip; + PRBool isSolidBorder; gfxContextAutoSaveRestore autoSR; - if (haveRoundedCorners && !aBGClipRect) { - gfxRect bgAreaGfx(RectToGfxRect(bgArea, appUnitsPerPixel)); - bgAreaGfx.Round(); - bgAreaGfx.Condition(); - if (bgAreaGfx.IsEmpty()) { - NS_WARNING("converted background area should not be empty"); - return; - } - - autoSR.SetContext(ctx); - ctx->NewPath(); - ctx->RoundedRectangle(bgAreaGfx, bgRadii, radiiAreOuter); - ctx->Clip(); + if (aBGClipRect) { + bgClipArea = *aBGClipRect; + SetupDirtyRects(bgClipArea, aDirtyRect, appUnitsPerPixel, + &dirtyRect, &dirtyRectGfx); + } else { + // The background is rendered over the 'background-clip' area, + // which is normally equal to the border area but may be reduced + // to the padding area by CSS. Also, if the border is solid, we + // don't need to draw outside the padding area. In either case, + // if the borders are rounded, make sure we use the same inner + // radii as the border code will. + // The background-color is drawn based on the bottom + // background-clip. + currentBackgroundClip = aColor.BottomLayer().mClip; + isSolidBorder = IsSolidBorder(aBorder); + if (isSolidBorder) + currentBackgroundClip = NS_STYLE_BG_CLIP_PADDING; + SetupBackgroundClip(ctx, currentBackgroundClip, aForFrame, + aBorderArea, aDirtyRect, haveRoundedCorners, + bgRadii, appUnitsPerPixel, &autoSR, + &bgClipArea, &dirtyRect, &dirtyRectGfx); } // If we might be using a background color, go ahead and set it now. @@ -1512,9 +1577,11 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // neither a background image nor a color, we wouldn't have gotten // this far.) if (!drawBackgroundImage) { - ctx->NewPath(); - ctx->Rectangle(dirtyRectGfx, PR_TRUE); - ctx->Fill(); + if (!dirtyRectGfx.IsEmpty()) { + ctx->NewPath(); + ctx->Rectangle(dirtyRectGfx, PR_TRUE); + ctx->Fill(); + } return; } @@ -1523,21 +1590,83 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // association of the style data with the frame. aPresContext->SetupBackgroundImageLoaders(aForFrame, &aColor); - imgIRequest *req = aColor.mBackgroundImage; + if (bottomImage.mRequest && + aColor.BottomLayer().mRepeat == NS_STYLE_BG_REPEAT_XY && + drawBackgroundColor) { + nsCOMPtr image; + bottomImage.mRequest->GetImage(getter_AddRefs(image)); + // If the image is completely opaque, we may not need to paint + // the background color. + nsCOMPtr gfxImgFrame; + image->GetCurrentFrame(getter_AddRefs(gfxImgFrame)); + if (gfxImgFrame) { + gfxImgFrame->GetNeedsBackground(&drawBackgroundColor); + if (!drawBackgroundColor) { + // If the current frame is smaller than its container, we + // need to paint the background color even if the frame + // itself is opaque. + nsIntSize iSize; + image->GetWidth(&iSize.width); + image->GetHeight(&iSize.height); + nsIntRect iframeRect; + gfxImgFrame->GetRect(iframeRect); + if (iSize.width != iframeRect.width || + iSize.height != iframeRect.height) { + drawBackgroundColor = PR_TRUE; + } + } + } + } - PRUint32 status = imgIRequest::STATUS_ERROR; - if (req) - req->GetImageStatus(&status); - - // While waiting for the image, draw a color, if any. - if (!req || - !(status & imgIRequest::STATUS_FRAME_COMPLETE) || - !(status & imgIRequest::STATUS_SIZE_AVAILABLE)) { - if (drawBackgroundColor) { + // The background color is rendered over the entire dirty area, + // even if the image isn't. + if (drawBackgroundColor) { + if (!dirtyRectGfx.IsEmpty()) { ctx->NewPath(); ctx->Rectangle(dirtyRectGfx, PR_TRUE); ctx->Fill(); } + } + + if (drawBackgroundImage) { + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, &aColor) { + const nsStyleBackground::Layer &layer = aColor.mLayers[i]; + if (!aBGClipRect) { + PRUint8 newBackgroundClip = + isSolidBorder ? NS_STYLE_BG_CLIP_PADDING : layer.mClip; + if (currentBackgroundClip != newBackgroundClip) { + currentBackgroundClip = newBackgroundClip; + SetupBackgroundClip(ctx, currentBackgroundClip, aForFrame, + aBorderArea, aDirtyRect, haveRoundedCorners, + bgRadii, appUnitsPerPixel, &autoSR, + &bgClipArea, &dirtyRect, &dirtyRectGfx); + } + } + if (!dirtyRectGfx.IsEmpty()) { + PaintBackgroundLayer(aPresContext, aRenderingContext, aForFrame, + dirtyRect, aBorderArea, bgClipArea, aColor, + layer, aBorder, aUsePrintSettings); + } + } + } +} + +static void +PaintBackgroundLayer(nsPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aDirtyRect, // intersected with aBGClipRect + const nsRect& aBorderArea, + const nsRect& aBGClipRect, + const nsStyleBackground& aBackground, + const nsStyleBackground::Layer& aLayer, + const nsStyleBorder& aBorder, + PRBool aUsePrintSettings) +{ + // Lookup the image + imgIRequest *req = aLayer.mImage.mRequest; + if (!UseImageRequestForBackground(req)) { + // There's no image or it's not ready to be painted. return; } @@ -1561,7 +1690,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nsIFrame* geometryFrame = aForFrame; if (frameType == nsGkAtoms::inlineFrame || frameType == nsGkAtoms::positionedInlineFrame) { - switch (aColor.mBackgroundInlinePolicy) { + switch (aBackground.mBackgroundInlinePolicy) { case NS_STYLE_BG_INLINE_POLICY_EACH_BOX: bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size()); break; @@ -1588,71 +1717,25 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // Background images are tiled over the 'background-clip' area // but the origin of the tiling is based on the 'background-origin' area - if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_BORDER) { + if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_BORDER) { nsMargin border = geometryFrame->GetUsedBorder(); geometryFrame->ApplySkipSides(border); bgOriginRect.Deflate(border); - if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_PADDING) { + if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_PADDING) { nsMargin padding = geometryFrame->GetUsedPadding(); geometryFrame->ApplySkipSides(padding); bgOriginRect.Deflate(padding); - NS_ASSERTION(aColor.mBackgroundOrigin == NS_STYLE_BG_ORIGIN_CONTENT, + NS_ASSERTION(aLayer.mOrigin == NS_STYLE_BG_ORIGIN_CONTENT, "unknown background-origin value"); } } - PRIntn repeat = aColor.mBackgroundRepeat; - switch (repeat) { - case NS_STYLE_BG_REPEAT_X: - break; - case NS_STYLE_BG_REPEAT_Y: - break; - case NS_STYLE_BG_REPEAT_XY: - if (drawBackgroundColor) { - // If the image is completely opaque, we may not need to paint - // the background color. - nsCOMPtr gfxImgFrame; - image->GetCurrentFrame(getter_AddRefs(gfxImgFrame)); - if (gfxImgFrame) { - gfxImgFrame->GetNeedsBackground(&drawBackgroundColor); - if (!drawBackgroundColor) { - // If the current frame is smaller than its container, we - // need to paint the background color even if the frame - // itself is opaque. - nsIntSize iSize; - image->GetWidth(&iSize.width); - image->GetHeight(&iSize.height); - nsIntRect iframeRect; - gfxImgFrame->GetRect(iframeRect); - if (iSize.width != iframeRect.width || - iSize.height != iframeRect.height) { - drawBackgroundColor = PR_TRUE; - } - } - } - } - break; - case NS_STYLE_BG_REPEAT_OFF: - default: - NS_ASSERTION(repeat == NS_STYLE_BG_REPEAT_OFF, - "unknown background-repeat value"); - break; - } - - // The background color is rendered over the entire dirty area, - // even if the image isn't. - if (drawBackgroundColor) { - ctx->NewPath(); - ctx->Rectangle(dirtyRectGfx, PR_TRUE); - ctx->Fill(); - } - // Compute the anchor point. // // relative to aBorderArea.TopLeft() (which is where the top-left // of aForFrame's border-box will be rendered) nsPoint imageTopLeft, anchor; - if (NS_STYLE_BG_ATTACHMENT_FIXED == aColor.mBackgroundAttachment) { + if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) { // If it's a fixed background attachment, then the image is placed // relative to the viewport, which is the area of the root frame // in a screen context or the page content frame in a print context. @@ -1686,7 +1769,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, } // Get the anchor point, relative to the viewport. - ComputeBackgroundAnchorPoint(aColor, viewportArea.Size(), imageSize, + ComputeBackgroundAnchorPoint(aLayer, viewportArea.Size(), imageSize, &imageTopLeft, &anchor); // Convert the anchor point from viewport coordinates to aForFrame @@ -1695,7 +1778,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, imageTopLeft += offset; anchor += offset; } else { - ComputeBackgroundAnchorPoint(aColor, bgOriginRect.Size(), imageSize, + ComputeBackgroundAnchorPoint(aLayer, bgOriginRect.Size(), imageSize, &imageTopLeft, &anchor); imageTopLeft += bgOriginRect.TopLeft(); anchor += bgOriginRect.TopLeft(); @@ -1703,18 +1786,19 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize); nsRect fillArea = destArea; + PRIntn repeat = aLayer.mRepeat; if (repeat & NS_STYLE_BG_REPEAT_X) { - fillArea.x = bgClipArea.x; - fillArea.width = bgClipArea.width; + fillArea.x = aBGClipRect.x; + fillArea.width = aBGClipRect.width; } if (repeat & NS_STYLE_BG_REPEAT_Y) { - fillArea.y = bgClipArea.y; - fillArea.height = bgClipArea.height; + fillArea.y = aBGClipRect.y; + fillArea.height = aBGClipRect.height; } - fillArea.IntersectRect(fillArea, bgClipArea); + fillArea.IntersectRect(fillArea, aBGClipRect); nsLayoutUtils::DrawImage(&aRenderingContext, image, - destArea, fillArea, anchor + aBorderArea.TopLeft(), dirtyRect); + destArea, fillArea, anchor + aBorderArea.TopLeft(), aDirtyRect); } static void diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index e14525a9d01..fe524048c04 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -497,7 +497,8 @@ nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder) { nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg); return (hasBG && NS_GET_A(bg->mBackgroundColor) == 255 && - bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER && + // bottom layer's clip is used for the color + bg->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER && !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()-> mBorderRadius)); } @@ -513,9 +514,10 @@ nsDisplayBackground::IsUniform(nsDisplayListBuilder* aBuilder) { nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg); if (!hasBG) return PR_TRUE; - if ((bg->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) && + if (!bg->BottomLayer().mImage.mRequest && + bg->mImageCount == 1 && !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius) && - bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER) + bg->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER) return PR_TRUE; return PR_FALSE; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 07b1d70b949..deeb47b4b1f 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3066,9 +3066,10 @@ nsLayoutUtils::GetFrameTransparency(nsIFrame* aFrame) { const nsStyleBackground* bg; if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg)) return eTransparencyTransparent; - if (NS_GET_A(bg->mBackgroundColor) < 255) - return eTransparencyTransparent; - if (bg->mBackgroundClip != NS_STYLE_BG_CLIP_BORDER) + if (NS_GET_A(bg->mBackgroundColor) < 255 || + NS_GET_A(bg->mFallbackBackgroundColor) < 255 || + // bottom layer's clip is used for the color + bg->BottomLayer().mClip != NS_STYLE_BG_CLIP_BORDER) return eTransparencyTransparent; return eTransparencyOpaque; } diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 0cf10e60b04..99b9cf7d943 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1252,10 +1252,12 @@ void nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame, const nsStyleBackground* aStyleBackground) { - nsRefPtr loader = - nsImageLoader::Create(aFrame, aStyleBackground->mBackgroundImage, - PR_FALSE, nsnull); - SetImageLoaders(aFrame, BACKGROUND_IMAGE, loader); + nsRefPtr loaders; + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) { + imgIRequest *image = aStyleBackground->mLayers[i].mImage.mRequest; + loaders = nsImageLoader::Create(aFrame, image, PR_FALSE, loaders); + } + SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders); } void diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h index 16c27ad187e..4903ad30735 100644 --- a/layout/base/nsStyleConsts.h +++ b/layout/base/nsStyleConsts.h @@ -242,21 +242,16 @@ #define NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT -3 #define NS_COLOR_CURRENTCOLOR -4 -// See nsStyleBackground -// 0x01 was background-color:transparent -#define NS_STYLE_BG_IMAGE_NONE 0x02 -#define NS_STYLE_BG_X_POSITION_PERCENT 0x04 -#define NS_STYLE_BG_X_POSITION_LENGTH 0x08 -#define NS_STYLE_BG_Y_POSITION_PERCENT 0x10 -#define NS_STYLE_BG_Y_POSITION_LENGTH 0x20 - // See nsStyleBackground #define NS_STYLE_BG_ATTACHMENT_SCROLL 0 #define NS_STYLE_BG_ATTACHMENT_FIXED 1 // See nsStyleBackground +// Code depends on these constants having the same values as BG_ORIGIN_* #define NS_STYLE_BG_CLIP_BORDER 0 #define NS_STYLE_BG_CLIP_PADDING 1 +// When we add NS_STYLE_BG_CLIP_CONTENT, we should add the PR_STATIC_ASSERTs +// to the places that assert equality for BORDER and PADDING. // See nsStyleBackground #define NS_STYLE_BG_INLINE_POLICY_EACH_BOX 0 @@ -264,6 +259,7 @@ #define NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX 2 // See nsStyleBackground +// Code depends on these constants having the same values as BG_CLIP_* #define NS_STYLE_BG_ORIGIN_BORDER 0 #define NS_STYLE_BG_ORIGIN_PADDING 1 #define NS_STYLE_BG_ORIGIN_CONTENT 2 diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index ce347cd2654..d67f30fe51c 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -547,14 +547,18 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) // to clear those notifiers unless we have to. (They'll be reset // when we paint, although we could miss a notification in that // interval.) - imgIRequest *oldBackgroundImage = - aOldStyleContext->GetStyleBackground()->mBackgroundImage; - if (oldBackgroundImage && - !EqualImages(oldBackgroundImage, - GetStyleBackground()->mBackgroundImage)) { - // stop the image loading for the frame, the image has changed - PresContext()->SetImageLoaders(this, - nsPresContext::BACKGROUND_IMAGE, nsnull); + 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.mRequest; + imgIRequest *newImage = + (i < newBG->mImageCount) ? newBG->mLayers[i].mImage.mRequest : nsnull; + if (oldImage && !EqualImages(oldImage, newImage)) { + // stop the image loading for the frame, the image has changed + PresContext()->SetImageLoaders(this, + nsPresContext::BACKGROUND_IMAGE, nsnull); + break; + } } } @@ -4028,11 +4032,14 @@ nsIFrame::CheckInvalidateSizeChange(const nsRect& aOldRect, // Invalidate the old frame background if the frame has a background // whose position depends on the size of the frame - const nsStyleBackground* background = GetStyleBackground(); - if (background->mBackgroundFlags & - (NS_STYLE_BG_X_POSITION_PERCENT | NS_STYLE_BG_Y_POSITION_PERCENT)) { - Invalidate(nsRect(0, 0, aOldRect.width, aOldRect.height)); - return; + const nsStyleBackground *bg = GetStyleBackground(); + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { + const nsStyleBackground::Layer &layer = bg->mLayers[i]; + if (layer.mImage.mRequest && + (layer.mPosition.mXIsPercent || layer.mPosition.mYIsPercent)) { + Invalidate(nsRect(0, 0, aOldRect.width, aOldRect.height)); + return; + } } } diff --git a/layout/reftests/backgrounds/aqua-32x32.png b/layout/reftests/backgrounds/aqua-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..7de00c8f79c983562e0c03af031a3a8c348612a4 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)ASWfq{i(!{YjMuMfAm))-CppPSCq=%66L!NSz|;1tuA7$&=W8h2*{^)Pt4`njxg HN@xNAzkweE literal 0 HcmV?d00001 diff --git a/layout/reftests/backgrounds/blue-32x32.png b/layout/reftests/backgrounds/blue-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..deefd19b2ac53bef91c82ed2f6f4ea5f53a9e34f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)ASWfq{i(!{YjMuMfAm)=WCN{G4&4gMt7D3sa**K`P_IV~qB%vRdu|^)Pt4`njxg HN@xNA-ozi} literal 0 HcmV?d00001 diff --git a/layout/reftests/backgrounds/fallback-color-1.xhtml b/layout/reftests/backgrounds/fallback-color-1.xhtml new file mode 100644 index 00000000000..203359702e1 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-1.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-2.xhtml b/layout/reftests/backgrounds/fallback-color-2.xhtml new file mode 100644 index 00000000000..30755f7f994 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-2.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-3.xhtml b/layout/reftests/backgrounds/fallback-color-3.xhtml new file mode 100644 index 00000000000..3bfe61cf520 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-3.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-4.xhtml b/layout/reftests/backgrounds/fallback-color-4.xhtml new file mode 100644 index 00000000000..63b50dd036c --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-4.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-5.xhtml b/layout/reftests/backgrounds/fallback-color-5.xhtml new file mode 100644 index 00000000000..1e6999345c5 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-5.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-6.xhtml b/layout/reftests/backgrounds/fallback-color-6.xhtml new file mode 100644 index 00000000000..564a762ef9f --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-6.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-7.xhtml b/layout/reftests/backgrounds/fallback-color-7.xhtml new file mode 100644 index 00000000000..334847673ed --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-7.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-8.xhtml b/layout/reftests/backgrounds/fallback-color-8.xhtml new file mode 100644 index 00000000000..ab55ba7ec1a --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-8.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-9.xhtml b/layout/reftests/backgrounds/fallback-color-9.xhtml new file mode 100644 index 00000000000..7d01cfc1930 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-9.xhtml @@ -0,0 +1,25 @@ + + + + css3-background: fallback colors + + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fallback-color-ref.xhtml b/layout/reftests/backgrounds/fallback-color-ref.xhtml new file mode 100644 index 00000000000..6e5b24e7a37 --- /dev/null +++ b/layout/reftests/backgrounds/fallback-color-ref.xhtml @@ -0,0 +1,24 @@ + + + + css3-background: fallback colors + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/fuchsia-32x32.png b/layout/reftests/backgrounds/fuchsia-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..7902bc31e09b58fbe224fb8047e84332eb132cce GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)ASWfq{i(!{YjMuMfAm)~HSPpPSCq=%66L!NSz|;1tu=XeK+WW4m;KdKf%i{an^L HB{Ts5wL2bP literal 0 HcmV?d00001 diff --git a/layout/reftests/backgrounds/layers-layer-count-1-ref.xhtml b/layout/reftests/backgrounds/layers-layer-count-1-ref.xhtml new file mode 100644 index 00000000000..633dab13cb3 --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-1-ref.xhtml @@ -0,0 +1,35 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+ +
+
+ + + + +
+
+ + + +
+ + + diff --git a/layout/reftests/backgrounds/layers-layer-count-2-ref.xhtml b/layout/reftests/backgrounds/layers-layer-count-2-ref.xhtml new file mode 100644 index 00000000000..6887a1301e7 --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-2-ref.xhtml @@ -0,0 +1,35 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+ +
+
+ + + + +
+
+ + + +
+ + + diff --git a/layout/reftests/backgrounds/layers-layer-count-cascade-1.xhtml b/layout/reftests/backgrounds/layers-layer-count-cascade-1.xhtml new file mode 100644 index 00000000000..d2dd3e8fcf0 --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-cascade-1.xhtml @@ -0,0 +1,36 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+
+
+ + + diff --git a/layout/reftests/backgrounds/layers-layer-count-cascade-2.xhtml b/layout/reftests/backgrounds/layers-layer-count-cascade-2.xhtml new file mode 100644 index 00000000000..6fd0ae125a4 --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-cascade-2.xhtml @@ -0,0 +1,36 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+
+
+ + + diff --git a/layout/reftests/backgrounds/layers-layer-count-inheritance-1.xhtml b/layout/reftests/backgrounds/layers-layer-count-inheritance-1.xhtml new file mode 100644 index 00000000000..45b458983d9 --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-inheritance-1.xhtml @@ -0,0 +1,39 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/layers-layer-count-inheritance-2.xhtml b/layout/reftests/backgrounds/layers-layer-count-inheritance-2.xhtml new file mode 100644 index 00000000000..13bf3f9f4fc --- /dev/null +++ b/layout/reftests/backgrounds/layers-layer-count-inheritance-2.xhtml @@ -0,0 +1,39 @@ + + + + css3-background: number of background layers determined by background-image + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/layers-stacking-order-ref.xhtml b/layout/reftests/backgrounds/layers-stacking-order-ref.xhtml new file mode 100644 index 00000000000..0e7abcc66aa --- /dev/null +++ b/layout/reftests/backgrounds/layers-stacking-order-ref.xhtml @@ -0,0 +1,28 @@ + + + + css3-background: stacking order of layers + + + + + + + + +
+ + + + +
+ + + diff --git a/layout/reftests/backgrounds/layers-stacking-order.xhtml b/layout/reftests/backgrounds/layers-stacking-order.xhtml new file mode 100644 index 00000000000..1dbaf2217f0 --- /dev/null +++ b/layout/reftests/backgrounds/layers-stacking-order.xhtml @@ -0,0 +1,27 @@ + + + + css3-background: stacking order of layers + + + + + + + + +
+ + + diff --git a/layout/reftests/backgrounds/lime-32x32.png b/layout/reftests/backgrounds/lime-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..c85b56ca8e2fd27b73d5e0ea3cacad5ed093643a GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)AScfq{c%!R3E*Oxu1l+ixz#hVnM9tKZWKbLh* G2~7Z|vmSK- literal 0 HcmV?d00001 diff --git a/layout/reftests/backgrounds/malformed.png b/layout/reftests/backgrounds/malformed.png new file mode 100644 index 00000000000..57a2dca5c0b --- /dev/null +++ b/layout/reftests/backgrounds/malformed.png @@ -0,0 +1 @@ +This is not a PNG file. diff --git a/layout/reftests/backgrounds/red-32x32.png b/layout/reftests/backgrounds/red-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..191e13ea11d03f0446f6f3bc185d7ef2ab2946be GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)AScfq{c%!R3E*Oug~*v05RXQ2`DfyPX8W$9mrwuboFyt=akR{0M@M+c>n+a literal 0 HcmV?d00001 diff --git a/layout/reftests/backgrounds/yellow-32x32.png b/layout/reftests/backgrounds/yellow-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a45f8111b446ceaea47b2ef50c46742333c177b1 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`Xn49fhIkx* zd)AScfq{c%!R3E*OGetUnit() == eCSSUnit_Null) { const nsCSSValue *val = ValueAtCursor(cursor); NS_ASSERTION(val->GetUnit() != eCSSUnit_Null, "oops"); - if (iProp == eCSSProperty_background_image || - iProp == eCSSProperty_list_style_image) { + if (iProp == eCSSProperty_list_style_image) { if (val->GetUnit() == eCSSUnit_URL) { val->StartImageLoad( aRuleData->mPresContext->Document()); @@ -217,8 +216,6 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const aRuleData->mFontData->mFamilyFromHTML = PR_FALSE; } else if (iProp == eCSSProperty_color || - iProp == eCSSProperty_background_color || - iProp == eCSSProperty_background_image || iProp == eCSSProperty_border_top_color || iProp == eCSSProperty_border_right_color_value || iProp == eCSSProperty_border_right_color_ltr_source || @@ -230,32 +227,8 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const iProp == eCSSProperty__moz_column_rule_color || iProp == eCSSProperty_outline_color) { if (ShouldIgnoreColors(aRuleData)) { - if (iProp == eCSSProperty_background_color) { - // Force non-'transparent' background - // colors to the user's default. - // We have the value in the form it was - // specified at this point, so we have to - // look for both the keyword 'transparent' - // and its equivalent in rgba notation. - nsCSSUnit u = target->GetUnit(); - nsDependentString buf; - - if ((u == eCSSUnit_Color && - NS_GET_A(target->GetColorValue()) - > 0) || - (u == eCSSUnit_String && - !nsGkAtoms::transparent-> - Equals(target->GetStringValue(buf))) || - (u == eCSSUnit_EnumColor)) { - target->SetColorValue(aRuleData-> - mPresContext-> - DefaultBackgroundColor()); - } - } else { - // Ignore 'color', 'border-*-color', and - // 'background-image' - *target = nsCSSValue(); - } + // Ignore 'color', 'border-*-color', etc. + *target = nsCSSValue(); } } } @@ -282,15 +255,50 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const NS_ASSERTION(val->mXValue.GetUnit() != eCSSUnit_Null || val->mYValue.GetUnit() != eCSSUnit_Null, "oops"); nsCSSValuePair* target = static_cast(prop); - if (target->mXValue.GetUnit() == eCSSUnit_Null) + NS_ASSERTION((target->mXValue.GetUnit() == eCSSUnit_Null) + == (target->mYValue.GetUnit() == eCSSUnit_Null), + "half a property?"); + if (target->mXValue.GetUnit() == eCSSUnit_Null) { target->mXValue = val->mXValue; - if (target->mYValue.GetUnit() == eCSSUnit_Null) target->mYValue = val->mYValue; + if (iProp == eCSSProperty_background_color && + ShouldIgnoreColors(aRuleData)) { + // Force non-'transparent' background colors + // to the user's default. We have the value + // in the form it was specified at this + // point, so we have to look for both the + // keyword 'transparent' and its equivalent + // in rgba notation. + nsCSSValue &colorVal = target->mXValue; + nsCSSUnit u = colorVal.GetUnit(); + nsDependentString buf; + + if ((u == eCSSUnit_Color && + NS_GET_A(colorVal.GetColorValue()) + > 0) || + (u == eCSSUnit_String && + !nsGkAtoms::transparent-> + Equals(colorVal.GetStringValue(buf))) || + (u == eCSSUnit_EnumColor)) { + colorVal.SetColorValue(aRuleData-> + mPresContext-> + DefaultBackgroundColor()); + } + // We could consider using the fallback + // background color for both values, but it + // might not make sense if the author didn't + // specify an image. But since we're + // dropping author images, we'll just use + // the non-fallback for both. + target->mYValue = target->mXValue; + } + } cursor += CDBValuePairStorage_advance; } break; case eCSSType_ValueList: - if (iProp == eCSSProperty_content) { + if (iProp == eCSSProperty_background_image || + iProp == eCSSProperty_content) { for (nsCSSValueList* l = ValueListAtCursor(cursor); l; l = l->mNext) if (l->mValue.GetUnit() == eCSSUnit_URL) @@ -317,7 +325,8 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const NS_ASSERTION(val, "oops"); *target = val; - if (iProp == eCSSProperty_border_top_colors || + if (iProp == eCSSProperty_background_image || + iProp == eCSSProperty_border_top_colors || iProp == eCSSProperty_border_right_colors || iProp == eCSSProperty_border_bottom_colors || iProp == eCSSProperty_border_left_colors) { diff --git a/layout/style/nsCSSDeclaration.cpp b/layout/style/nsCSSDeclaration.cpp index 032f9497023..d544a862f3a 100644 --- a/layout/style/nsCSSDeclaration.cpp +++ b/layout/style/nsCSSDeclaration.cpp @@ -209,12 +209,17 @@ PRBool nsCSSDeclaration::AppendValueToString(nsCSSProperty aProperty, nsAString& NS_ASSERTION(item->mXValue.GetUnit() != eCSSUnit_Null, "unexpected null unit"); AppendCSSValueToString(aProperty, item->mXValue, aResult); - if (item->mYValue.GetUnit() != eCSSUnit_Null) { + if (item->mXValue.GetUnit() != eCSSUnit_Inherit && + item->mXValue.GetUnit() != eCSSUnit_Initial && + item->mYValue.GetUnit() != eCSSUnit_Null) { aResult.Append(PRUnichar(' ')); AppendCSSValueToString(aProperty, item->mYValue, aResult); } item = item->mNext; if (item) { + if (nsCSSProps::PropHasFlags(aProperty, + CSS_PROPERTY_VALUE_LIST_USES_COMMAS)) + aResult.Append(PRUnichar(',')); aResult.Append(PRUnichar(' ')); } } while (item); @@ -761,50 +766,97 @@ nsCSSDeclaration::GetValue(nsCSSProperty aProperty, break; } case eCSSProperty_background: { - // The -moz-background-clip, -moz-background-origin, and - // -moz-background-inline-policy properties are reset by this - // shorthand property to their initial values, but can't be - // represented in its syntax. - const nsCSSValue *clipValue = static_cast( - data->StorageFor(eCSSProperty__moz_background_clip)); - const nsCSSValue *originValue = static_cast( - data->StorageFor(eCSSProperty__moz_background_origin)); - const nsCSSValue *inlinePolicyValue = static_cast( - data->StorageFor(eCSSProperty__moz_background_inline_policy)); - if (*clipValue != - nsCSSValue(NS_STYLE_BG_CLIP_BORDER, eCSSUnit_Enumerated) || - *originValue != - nsCSSValue(NS_STYLE_BG_ORIGIN_PADDING, eCSSUnit_Enumerated) || - *inlinePolicyValue != - nsCSSValue(NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, - eCSSUnit_Enumerated)) { - return NS_OK; - } - - PRBool appendedSomething = PR_FALSE; - if (AppendValueToString(eCSSProperty_background_color, aValue)) { - appendedSomething = PR_TRUE; + // 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. + // We also need to bail out if an item has background-clip and + // background-origin that are different and not the default + // values. (We omit them if they're both default.) + const nsCSSValueList *image = + * data->ValueListStorageFor(eCSSProperty_background_image); + const nsCSSValueList *repeat = + * data->ValueListStorageFor(eCSSProperty_background_repeat); + const nsCSSValueList *attachment = + * data->ValueListStorageFor(eCSSProperty_background_attachment); + const nsCSSValuePairList *position = + * data->ValuePairListStorageFor(eCSSProperty_background_position); + const nsCSSValueList *clip = + * data->ValueListStorageFor(eCSSProperty__moz_background_clip); + const nsCSSValueList *origin = + * data->ValueListStorageFor(eCSSProperty__moz_background_origin); + for (;;) { + AppendCSSValueToString(eCSSProperty_background_image, + image->mValue, aValue); + aValue.Append(PRUnichar(' ')); + AppendCSSValueToString(eCSSProperty_background_repeat, + repeat->mValue, aValue); + aValue.Append(PRUnichar(' ')); + AppendCSSValueToString(eCSSProperty_background_attachment, + attachment->mValue, aValue); + aValue.Append(PRUnichar(' ')); + AppendCSSValueToString(eCSSProperty_background_position, + position->mXValue, aValue); + aValue.Append(PRUnichar(' ')); + AppendCSSValueToString(eCSSProperty_background_position, + position->mYValue, aValue); + NS_ASSERTION(clip->mValue.GetUnit() == eCSSUnit_Enumerated && + origin->mValue.GetUnit() == eCSSUnit_Enumerated, + "should not be inherit/initial within list and " + "should have returned early for real inherit/initial"); + if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER || + origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) { +#if 0 + // This is commented out for now until we change + // -moz-background-clip to background-clip, -moz-background-origin + // to background-origin, change their value names to *-box, and add + // support for content-box on background-clip. + PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER == + NS_STYLE_BG_ORIGIN_BORDER); + PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_PADDING == + NS_STYLE_BG_ORIGIN_PADDING); + // PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_CONTENT == /* does not exist */ + // NS_STYLE_BG_ORIGIN_CONTENT); + if (clip->mValue != origin->mValue) { + aValue.Truncate(); + return NS_OK; + } + + aValue.Append(PRUnichar(' ')); + AppendCSSValueToString(eCSSProperty__moz_background_clip, + clip->mValue, aValue); +#else + aValue.Truncate(); + return NS_OK; +#endif + } + + image = image->mNext; + repeat = repeat->mNext; + attachment = attachment->mNext; + position = position->mNext; + clip = clip->mNext; + origin = origin->mNext; + + if (!image) { + if (repeat || attachment || position || clip || origin) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return NS_OK; + } + break; + } + if (!repeat || !attachment || !position || !clip || !origin) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return NS_OK; + } + aValue.Append(PRUnichar(',')); aValue.Append(PRUnichar(' ')); } - if (AppendValueToString(eCSSProperty_background_image, aValue)) { - aValue.Append(PRUnichar(' ')); - appendedSomething = PR_TRUE; - } - if (AppendValueToString(eCSSProperty_background_repeat, aValue)) { - aValue.Append(PRUnichar(' ')); - appendedSomething = PR_TRUE; - } - if (AppendValueToString(eCSSProperty_background_attachment, aValue)) { - aValue.Append(PRUnichar(' ')); - appendedSomething = PR_TRUE; - } - if (!AppendValueToString(eCSSProperty_background_position, aValue) && - appendedSomething) { - NS_ASSERTION(!aValue.IsEmpty() && aValue.Last() == PRUnichar(' '), - "We appended a space before!"); - // We appended an extra space. Let's get rid of it - aValue.Truncate(aValue.Length() - 1); - } + + aValue.Append(PRUnichar(' ')); + AppendValueToString(eCSSProperty_background_color, aValue); break; } case eCSSProperty_cue: { diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 0b584717e51..1947c077734 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -84,6 +84,7 @@ #include "nsDOMError.h" #include "nsAutoPtr.h" #include "nsTArray.h" +#include "prlog.h" // Flags for ParseVariant method #define VARIANT_KEYWORD 0x000001 // K @@ -390,9 +391,30 @@ protected: // Property specific parsing routines PRBool ParseAzimuth(nsCSSValue& aValue); PRBool ParseBackground(); + + struct BackgroundItem; + friend struct BackgroundItem; + struct BackgroundItem { + nsCSSValue mImage; + nsCSSValue mRepeat; + nsCSSValue mAttachment; + nsCSSValuePair mPosition; + nsCSSValue mClip; + nsCSSValue mOrigin; + // The background-color is set as a side-effect, and if so, mLastItem + // is set to true. + PRBool mLastItem; + }; + struct BackgroundItemSimpleValueInfo { + nsCSSValue BackgroundItem::*member; + nsCSSProperty propID; + }; + + PRBool ParseBackgroundItem(BackgroundItem& aItem, PRBool aFirstItem); + + PRBool ParseBackgroundList(nsCSSProperty aPropID); // a single value prop-id + PRBool ParseBackgroundColor(PRBool aInShorthand); PRBool ParseBackgroundPosition(); - PRBool ParseBackgroundPositionValues(); - PRBool ParseBoxPosition(nsCSSValuePair& aOut); PRBool ParseBoxPositionValues(nsCSSValuePair& aOut); PRBool ParseBorderColor(); PRBool ParseBorderColors(nsCSSValueList** aResult, @@ -4940,8 +4962,16 @@ CSSParserImpl::ParseProperty(nsCSSProperty aPropID) switch (aPropID) { // handle shorthand or multiple properties case eCSSProperty_background: return ParseBackground(); + case eCSSProperty_background_color: + return ParseBackgroundColor(PR_FALSE); case eCSSProperty_background_position: return ParseBackgroundPosition(); + case eCSSProperty_background_attachment: + case eCSSProperty__moz_background_clip: + case eCSSProperty_background_image: + case eCSSProperty__moz_background_origin: + case eCSSProperty_background_repeat: + return ParseBackgroundList(aPropID); case eCSSProperty_border: return ParseBorderSide(kBorderTopIDs, PR_TRUE); case eCSSProperty_border_color: @@ -5193,6 +5223,7 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, switch (aPropID) { case eCSSProperty_UNKNOWN: case eCSSProperty_background: + case eCSSProperty_background_color: case eCSSProperty_background_position: case eCSSProperty_border: case eCSSProperty_border_color: @@ -5305,22 +5336,25 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, case eCSSProperty_azimuth: return ParseAzimuth(aValue); case eCSSProperty_background_attachment: + // Used only internally. return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kBackgroundAttachmentKTable); case eCSSProperty__moz_background_clip: + // Used only internally. return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kBackgroundClipKTable); - case eCSSProperty_background_color: - return ParseVariant(aValue, VARIANT_HC, nsnull); case eCSSProperty_background_image: + // Used only internally. return ParseVariant(aValue, VARIANT_HUO, nsnull); case eCSSProperty__moz_background_inline_policy: return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kBackgroundInlinePolicyKTable); case eCSSProperty__moz_background_origin: + // Used only internally. return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kBackgroundOriginKTable); case eCSSProperty_background_repeat: + // Used only internally. return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kBackgroundRepeatKTable); case eCSSProperty_binding: @@ -5858,42 +5892,108 @@ CSSParserImpl::ParseBackground() { nsAutoParseCompoundProperty compound(this); + // These two are set through side-effects of ParseBackgroundItem. + mTempData.mColor.mBackColor.mXValue.SetColorValue(NS_RGBA(0, 0, 0, 0)); + mTempData.mColor.mBackColor.mYValue.SetColorValue(NS_RGBA(0, 0, 0, 0)); + + BackgroundItem bgitem; + nsCSSValuePairList *positionHead = nsnull, **positionTail = &positionHead; + static const BackgroundItemSimpleValueInfo simpleValues[] = { + { &BackgroundItem::mImage, eCSSProperty_background_image }, + { &BackgroundItem::mRepeat, eCSSProperty_background_repeat }, + { &BackgroundItem::mAttachment, eCSSProperty_background_attachment }, + { &BackgroundItem::mClip, eCSSProperty__moz_background_clip }, + { &BackgroundItem::mOrigin, eCSSProperty__moz_background_origin } + }; + nsCSSValueList *simpleHeads[NS_ARRAY_LENGTH(simpleValues)]; + nsCSSValueList **simpleTails[NS_ARRAY_LENGTH(simpleValues)]; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { + simpleHeads[i] = nsnull; + simpleTails[i] = &simpleHeads[i]; + } + for (;;) { + if (!ParseBackgroundItem(bgitem, !positionHead)) { + break; + } + nsCSSValuePairList *positionItem = new nsCSSValuePairList; + if (!positionItem) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + break; + } + positionItem->mXValue = bgitem.mPosition.mXValue; + positionItem->mYValue = bgitem.mPosition.mYValue; + *positionTail = positionItem; + positionTail = &positionItem->mNext; + + PRBool fail = PR_FALSE; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { + nsCSSValueList *item = new nsCSSValueList; + if (!item) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + fail = PR_TRUE; + break; + } + item->mValue = bgitem.*(simpleValues[i].member); + *simpleTails[i] = item; + simpleTails[i] = &item->mNext; + } + if (fail) { + break; + } + + if (!bgitem.mLastItem && ExpectSymbol(',', PR_TRUE)) { + continue; + } + if (!ExpectEndProperty()) { + break; + } + + mTempData.mColor.mBackPosition = positionHead; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { + nsCSSValueList **source = static_cast( + mTempData.PropertyAt(simpleValues[i].propID)); + *source = simpleHeads[i]; + } + + mTempData.SetPropertyBit(eCSSProperty_background_color); + mTempData.SetPropertyBit(eCSSProperty_background_image); + mTempData.SetPropertyBit(eCSSProperty_background_repeat); + mTempData.SetPropertyBit(eCSSProperty_background_attachment); + mTempData.SetPropertyBit(eCSSProperty_background_position); + mTempData.SetPropertyBit(eCSSProperty__moz_background_clip); + mTempData.SetPropertyBit(eCSSProperty__moz_background_origin); + return PR_TRUE; + } + delete positionHead; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { + delete simpleHeads[i]; + } + return PR_FALSE; +} + +// Parse one item of the background shorthand property. +PRBool +CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem, + PRBool aFirstItem) +{ // Fill in the values that the shorthand will set if we don't find // other values. - mTempData.mColor.mBackColor.SetColorValue(NS_RGBA(0, 0, 0, 0)); - mTempData.SetPropertyBit(eCSSProperty_background_color); - mTempData.mColor.mBackImage.SetNoneValue(); - mTempData.SetPropertyBit(eCSSProperty_background_image); - mTempData.mColor.mBackRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY, - eCSSUnit_Enumerated); - mTempData.SetPropertyBit(eCSSProperty_background_repeat); - mTempData.mColor.mBackAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL, - eCSSUnit_Enumerated); - mTempData.SetPropertyBit(eCSSProperty_background_attachment); - mTempData.mColor.mBackPosition.mXValue.SetPercentValue(0.0f); - mTempData.mColor.mBackPosition.mYValue.SetPercentValue(0.0f); - mTempData.SetPropertyBit(eCSSProperty_background_position); - // including the ones that we can't set from the shorthand. - mTempData.mColor.mBackClip.SetIntValue(NS_STYLE_BG_CLIP_BORDER, - eCSSUnit_Enumerated); - mTempData.SetPropertyBit(eCSSProperty__moz_background_clip); - mTempData.mColor.mBackOrigin.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING, - eCSSUnit_Enumerated); - mTempData.SetPropertyBit(eCSSProperty__moz_background_origin); - mTempData.mColor.mBackInlinePolicy.SetIntValue( - NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, eCSSUnit_Enumerated); - mTempData.SetPropertyBit(eCSSProperty__moz_background_inline_policy); - - // XXX If ParseSingleValueProperty were table-driven (bug 376079) and - // automatically filled in the right field of mTempData, we could move - // ParseBackgroundPosition to it (as a special case) and switch back - // to using ParseChoice here. + aItem.mImage.SetNoneValue(); + aItem.mRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY, eCSSUnit_Enumerated); + aItem.mAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL, + eCSSUnit_Enumerated); + aItem.mPosition.mXValue.SetPercentValue(0.0f); + aItem.mPosition.mYValue.SetPercentValue(0.0f); + aItem.mClip.SetIntValue(NS_STYLE_BG_CLIP_BORDER, eCSSUnit_Enumerated); + aItem.mOrigin.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING, eCSSUnit_Enumerated); + aItem.mLastItem = PR_FALSE; PRBool haveColor = PR_FALSE, haveImage = PR_FALSE, haveRepeat = PR_FALSE, haveAttach = PR_FALSE, - havePosition = PR_FALSE; + havePosition = PR_FALSE, + haveSomething = PR_FALSE; while (GetToken(PR_TRUE)) { nsCSSTokenType tt = mToken.mType; UngetToken(); // ...but we'll still cheat and use mToken @@ -5908,7 +6008,7 @@ CSSParserImpl::ParseBackground() PRInt32 dummy; if (keyword == eCSSKeyword_inherit || keyword == eCSSKeyword__moz_initial) { - if (haveColor || haveImage || haveRepeat || haveAttach || havePosition) + if (haveSomething || !aFirstItem) return PR_FALSE; haveColor = haveImage = haveRepeat = haveAttach = havePosition = PR_TRUE; @@ -5919,24 +6019,21 @@ CSSParserImpl::ParseBackground() } else { val.SetInitialValue(); } - mTempData.mColor.mBackColor = val; - mTempData.mColor.mBackImage = val; - mTempData.mColor.mBackRepeat = val; - mTempData.mColor.mBackAttachment = val; - mTempData.mColor.mBackPosition.mXValue = val; - mTempData.mColor.mBackPosition.mYValue = val; - // Reset (for 'inherit') the 3 properties that can't be - // specified, although it's not entirely clear in the spec: - // http://lists.w3.org/Archives/Public/www-style/2007Mar/0110 - mTempData.mColor.mBackClip = val; - mTempData.mColor.mBackOrigin = val; - mTempData.mColor.mBackInlinePolicy = val; + mTempData.mColor.mBackColor.SetBothValuesTo(val); + aItem.mImage = val; + aItem.mRepeat = val; + aItem.mAttachment = val; + aItem.mPosition.SetBothValuesTo(val); + aItem.mClip = val; + aItem.mOrigin = val; + aItem.mLastItem = PR_TRUE; + haveSomething = PR_TRUE; break; } else if (keyword == eCSSKeyword_none) { if (haveImage) return PR_FALSE; haveImage = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackImage, + if (!ParseSingleValueProperty(aItem.mImage, eCSSProperty_background_image)) { NS_NOTREACHED("should be able to parse"); return PR_FALSE; @@ -5946,7 +6043,7 @@ CSSParserImpl::ParseBackground() if (haveAttach) return PR_FALSE; haveAttach = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackAttachment, + if (!ParseSingleValueProperty(aItem.mAttachment, eCSSProperty_background_attachment)) { NS_NOTREACHED("should be able to parse"); return PR_FALSE; @@ -5956,7 +6053,7 @@ CSSParserImpl::ParseBackground() if (haveRepeat) return PR_FALSE; haveRepeat = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackRepeat, + if (!ParseSingleValueProperty(aItem.mRepeat, eCSSProperty_background_repeat)) { NS_NOTREACHED("should be able to parse"); return PR_FALSE; @@ -5966,24 +6063,57 @@ CSSParserImpl::ParseBackground() if (havePosition) return PR_FALSE; havePosition = PR_TRUE; - if (!ParseBackgroundPositionValues()) { + if (!ParseBoxPositionValues(aItem.mPosition)) { return PR_FALSE; } +#if 0 + // This is commented out for now until we change + // -moz-background-clip to background-clip, -moz-background-origin + // to background-origin, change their value names to *-box, and add + // support for content-box on background-clip. + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kBackgroundClipKTable, dummy)) { + // For now, we use the background-clip table, because we don't + // support 'content' on background-clip. But that's dangerous + // if we eventually support no-clip. + NS_ASSERTION( + nsCSSProps::kBackgroundClipKTable[0] == eCSSKeyword_border && + nsCSSProps::kBackgroundClipKTable[2] == eCSSKeyword_padding && + nsCSSProps::kBackgroundClipKTable[4] == eCSSKeyword_UNKNOWN, + "need to rewrite this code"); + if (haveOrigin) + return PR_FALSE; + haveOrigin = PR_TRUE; + if (!ParseSingleValueProperty(aItem.mOrigin, + eCSSProperty__moz_background_origin)) { + NS_NOTREACHED("should be able to parse"); + return PR_FALSE; + } + PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER == + NS_STYLE_BG_ORIGIN_BORDER); + PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_PADDING == + NS_STYLE_BG_ORIGIN_PADDING); + // PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_CONTENT == /* does not exist */ + // NS_STYLE_BG_ORIGIN_CONTENT); + // When we support 'no-clip', this needs to be conditional on haveClip: + aItem.mClip = aItem.mOrigin; + // We'd support 'no-clip' as an additional |else| here. +#endif } else { if (haveColor) return PR_FALSE; haveColor = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackColor, - eCSSProperty_background_color)) { + if (!ParseBackgroundColor(PR_TRUE)) { return PR_FALSE; } + aItem.mLastItem = PR_TRUE; } } else if (eCSSToken_Function == tt && mToken.mIdent.LowerCaseEqualsLiteral("url")) { if (haveImage) return PR_FALSE; haveImage = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackImage, + if (!ParseSingleValueProperty(aItem.mImage, eCSSProperty_background_image)) { return PR_FALSE; } @@ -5991,37 +6121,131 @@ CSSParserImpl::ParseBackground() if (havePosition) return PR_FALSE; havePosition = PR_TRUE; - if (!ParseBackgroundPositionValues()) { + if (!ParseBoxPositionValues(aItem.mPosition)) { return PR_FALSE; } } else { if (haveColor) return PR_FALSE; haveColor = PR_TRUE; - if (!ParseSingleValueProperty(mTempData.mColor.mBackColor, - eCSSProperty_background_color)) { + // Note: ParseBackgroundColor parses 'inherit' and 'initial', but + // we've already checked for them, so it's ok. + if (!ParseBackgroundColor(PR_TRUE)) { return PR_FALSE; } + aItem.mLastItem = PR_TRUE; } + haveSomething = PR_TRUE; } - return ExpectEndProperty() && - (haveColor || haveImage || haveRepeat || haveAttach || havePosition); + return haveSomething; } +// This function is very similar to ParseBackgroundPosition. +PRBool +CSSParserImpl::ParseBackgroundList(nsCSSProperty aPropID) +{ + // aPropID is a single value prop-id + nsCSSValue value; + nsCSSValueList *head = nsnull, **tail = &head; + for (;;) { + if (!ParseSingleValueProperty(value, aPropID)) { + break; + } + PRBool inheritOrInitial = value.GetUnit() == eCSSUnit_Inherit || + value.GetUnit() == eCSSUnit_Initial; + if (inheritOrInitial && head) { + // inherit and initial are only allowed on their own + break; + } + nsCSSValueList *item = new nsCSSValueList; + if (!item) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + break; + } + item->mValue = value; + *tail = item; + tail = &item->mNext; + if (!inheritOrInitial && ExpectSymbol(',', PR_TRUE)) { + continue; + } + if (!ExpectEndProperty()) { + break; + } + nsCSSValueList **source = + static_cast(mTempData.PropertyAt(aPropID)); + *source = head; + mTempData.SetPropertyBit(aPropID); + return PR_TRUE; + } + delete head; + return PR_FALSE; +} + +PRBool +CSSParserImpl::ParseBackgroundColor(PRBool aInShorthand) +{ + nsCSSValuePair &backColor = mTempData.mColor.mBackColor; + mTempData.SetPropertyBit(eCSSProperty_background_color); + if (!ParseVariant(backColor.mXValue, + aInShorthand ? VARIANT_COLOR : VARIANT_HC, nsnull)) { + return PR_FALSE; + } + backColor.mYValue = backColor.mXValue; + switch (backColor.mXValue.GetUnit()) { + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + NS_ASSERTION(!aInShorthand, + "should not get inherit or initial in shorthand"); + return ExpectEndProperty(); // we're done + default: + break; + } + + // Ignore success, since the value is optional. + ParseVariant(backColor.mYValue, VARIANT_COLOR, nsnull); + + return aInShorthand || ExpectEndProperty(); +} + +// This function is very similar to ParseBackgroundList. PRBool CSSParserImpl::ParseBackgroundPosition() { - if (!ParseBoxPosition(mTempData.mColor.mBackPosition)) - return PR_FALSE; - mTempData.SetPropertyBit(eCSSProperty_background_position); - return PR_TRUE; -} - -PRBool -CSSParserImpl::ParseBackgroundPositionValues() -{ - return ParseBoxPositionValues(mTempData.mColor.mBackPosition); + // aPropID is a single value prop-id + nsCSSValuePair valuePair; + nsCSSValuePairList *head = nsnull, **tail = &head; + for (;;) { + if (!ParseBoxPositionValues(valuePair)) { + 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); + break; + } + item->mXValue = valuePair.mXValue; + item->mYValue = valuePair.mYValue; + *tail = item; + tail = &item->mNext; + if (!inheritOrInitial && ExpectSymbol(',', PR_TRUE)) { + continue; + } + if (!ExpectEndProperty()) { + break; + } + mTempData.mColor.mBackPosition = head; + mTempData.SetPropertyBit(eCSSProperty_background_position); + return PR_TRUE; + } + delete head; + return PR_FALSE; } /** @@ -6032,12 +6256,6 @@ CSSParserImpl::ParseBackgroundPositionValues() * @param aOut The nsCSSValuePair where to place the result. * @return Whether or not the operation succeeded. */ -PRBool CSSParserImpl::ParseBoxPosition(nsCSSValuePair &aOut) -{ - // Need to read the box positions and the end of the property. - return ParseBoxPositionValues(aOut) && ExpectEndProperty(); -} - PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut) { // First try a percentage or a length value @@ -7214,7 +7432,8 @@ PRBool CSSParserImpl::ParseMozTransform() PRBool CSSParserImpl::ParseMozTransformOrigin() { /* Read in a box position, fail if we can't. */ - if (!ParseBoxPosition(mTempData.mDisplay.mTransformOrigin)) + if (!ParseBoxPositionValues(mTempData.mDisplay.mTransformOrigin) || + !ExpectEndProperty()) return PR_FALSE; /* Set the property bit and return. */ diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 36c6f7e0176..d784042e7b8 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -297,14 +297,14 @@ CSS_PROP_FONT(-x-system-font, _x_system_font, X, CSS_PROPERTY_APPLIES_TO_FIRST_L #endif CSS_PROP_BACKENDONLY(azimuth, azimuth, Azimuth, 0, Aural, mAzimuth, eCSSType_Value, kAzimuthKTable) CSS_PROP_SHORTHAND(background, background, Background, 0) -CSS_PROP_BACKGROUND(background-attachment, background_attachment, BackgroundAttachment, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackAttachment, eCSSType_Value, kBackgroundAttachmentKTable) -CSS_PROP_BACKGROUND(-moz-background-clip, _moz_background_clip, MozBackgroundClip, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackClip, eCSSType_Value, kBackgroundClipKTable) -CSS_PROP_BACKGROUND(background-color, background_color, BackgroundColor, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackColor, eCSSType_Value, nsnull) -CSS_PROP_BACKGROUND(background-image, background_image, BackgroundImage, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackImage, eCSSType_Value, nsnull) +CSS_PROP_BACKGROUND(background-attachment, background_attachment, BackgroundAttachment, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackAttachment, eCSSType_ValueList, kBackgroundAttachmentKTable) +CSS_PROP_BACKGROUND(-moz-background-clip, _moz_background_clip, MozBackgroundClip, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackClip, eCSSType_ValueList, kBackgroundClipKTable) +CSS_PROP_BACKGROUND(background-color, background_color, BackgroundColor, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackColor, eCSSType_ValuePair, nsnull) +CSS_PROP_BACKGROUND(background-image, background_image, BackgroundImage, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackImage, eCSSType_ValueList, nsnull) CSS_PROP_BACKGROUND(-moz-background-inline-policy, _moz_background_inline_policy, MozBackgroundInlinePolicy, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackInlinePolicy, eCSSType_Value, kBackgroundInlinePolicyKTable) -CSS_PROP_BACKGROUND(-moz-background-origin, _moz_background_origin, MozBackgroundOrigin, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackOrigin, eCSSType_Value, kBackgroundOriginKTable) -CSS_PROP_BACKGROUND(background-position, background_position, BackgroundPosition, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackPosition, eCSSType_ValuePair, kBackgroundPositionKTable) -CSS_PROP_BACKGROUND(background-repeat, background_repeat, BackgroundRepeat, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mBackRepeat, eCSSType_Value, kBackgroundRepeatKTable) +CSS_PROP_BACKGROUND(-moz-background-origin, _moz_background_origin, MozBackgroundOrigin, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackOrigin, eCSSType_ValueList, kBackgroundOriginKTable) +CSS_PROP_BACKGROUND(background-position, background_position, BackgroundPosition, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackPosition, eCSSType_ValuePairList, kBackgroundPositionKTable) +CSS_PROP_BACKGROUND(background-repeat, background_repeat, BackgroundRepeat, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Color, mBackRepeat, eCSSType_ValueList, kBackgroundRepeatKTable) CSS_PROP_DISPLAY(-moz-binding, binding, MozBinding, 0, Display, mBinding, eCSSType_Value, nsnull) // XXX bug 3935 CSS_PROP_SHORTHAND(border, border, Border, 0) CSS_PROP_SHORTHAND(border-bottom, border_bottom, BorderBottom, 0) diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index c88b3512395..0389c54ce21 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1489,9 +1489,8 @@ static const nsCSSProperty gBackgroundSubpropTable[] = { eCSSProperty_background_repeat, eCSSProperty_background_attachment, eCSSProperty_background_position, - eCSSProperty__moz_background_clip, // XXX Added LDB. - eCSSProperty__moz_background_origin, // XXX Added LDB. - eCSSProperty__moz_background_inline_policy, // XXX Added LDB. + eCSSProperty__moz_background_clip, + eCSSProperty__moz_background_origin, eCSSProperty_UNKNOWN }; diff --git a/layout/style/nsCSSStruct.cpp b/layout/style/nsCSSStruct.cpp index a08ac29a57a..71805fa2f91 100644 --- a/layout/style/nsCSSStruct.cpp +++ b/layout/style/nsCSSStruct.cpp @@ -106,6 +106,12 @@ nsCSSValueList::Equal(nsCSSValueList* aList1, nsCSSValueList* aList2) // --- nsCSSColor ----------------- nsCSSColor::nsCSSColor(void) + : mBackImage(nsnull) + , mBackRepeat(nsnull) + , mBackAttachment(nsnull) + , mBackPosition(nsnull) + , mBackClip(nsnull) + , mBackOrigin(nsnull) { MOZ_COUNT_CTOR(nsCSSColor); } @@ -113,6 +119,13 @@ nsCSSColor::nsCSSColor(void) nsCSSColor::~nsCSSColor(void) { MOZ_COUNT_DTOR(nsCSSColor); + + delete mBackImage; + delete mBackRepeat; + delete mBackAttachment; + delete mBackPosition; + delete mBackClip; + delete mBackOrigin; } // --- nsCSSText ----------------- diff --git a/layout/style/nsCSSStruct.h b/layout/style/nsCSSStruct.h index c2b12c8d98b..546f76c3e1a 100644 --- a/layout/style/nsCSSStruct.h +++ b/layout/style/nsCSSStruct.h @@ -307,13 +307,13 @@ struct nsCSSColor : public nsCSSStruct { ~nsCSSColor(void); nsCSSValue mColor; - nsCSSValue mBackColor; - nsCSSValue mBackImage; - nsCSSValue mBackRepeat; - nsCSSValue mBackAttachment; - nsCSSValuePair mBackPosition; - nsCSSValue mBackClip; - nsCSSValue mBackOrigin; + nsCSSValuePair mBackColor; + nsCSSValueList* mBackImage; + nsCSSValueList* mBackRepeat; + nsCSSValueList* mBackAttachment; + nsCSSValuePairList* mBackPosition; + nsCSSValueList* mBackClip; + nsCSSValueList* mBackOrigin; nsCSSValue mBackInlinePolicy; private: nsCSSColor(const nsCSSColor& aOther); // NOT IMPLEMENTED @@ -321,6 +321,11 @@ private: struct nsRuleDataColor : public nsCSSColor { nsRuleDataColor() {} + + // A little bit of a hack here: now that background-image is + // represented by a value list, attribute mapping code needs a place + // to store one item in a value list in order to map a simple value. + nsCSSValueList mTempBackImage; private: nsRuleDataColor(const nsRuleDataColor& aOther); // NOT IMPLEMENTED }; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 599432aa3e5..0948384c39c 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1165,69 +1165,117 @@ nsComputedDOMStyle::GetFontVariant(nsIDOMCSSValue** aValue) return CallQueryInterface(val, aValue); } +nsresult +nsComputedDOMStyle::GetBackgroundList(PRUint8 nsStyleBackground::Layer::* aMember, + PRUint32 nsStyleBackground::* aCount, + const PRInt32 aTable[], + nsIDOMCSSValue** aResult) +{ + const nsStyleBackground* bg = GetStyleBackground(); + + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE); + NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); + + for (PRUint32 i = 0, i_end = bg->*aCount; i < i_end; ++i) { + nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); + if (!val || !valueList->AppendCSSValue(val)) { + delete val; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + val->SetIdent(nsCSSProps::ValueToKeywordEnum(bg->mLayers[i].*aMember, + aTable)); + } + + return CallQueryInterface(valueList, aResult); +} + nsresult nsComputedDOMStyle::GetBackgroundAttachment(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); - - const nsStyleBackground *background = GetStyleBackground(); - - val->SetIdent( - nsCSSProps::ValueToKeywordEnum(background->mBackgroundAttachment, - nsCSSProps::kBackgroundAttachmentKTable)); - - return CallQueryInterface(val, aValue); + return GetBackgroundList(&nsStyleBackground::Layer::mAttachment, + &nsStyleBackground::mAttachmentCount, + nsCSSProps::kBackgroundAttachmentKTable, + aValue); } nsresult nsComputedDOMStyle::GetBackgroundClip(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); - - val->SetIdent( - nsCSSProps::ValueToKeywordEnum(GetStyleBackground()->mBackgroundClip, - nsCSSProps::kBackgroundClipKTable)); - - return CallQueryInterface(val, aValue); + return GetBackgroundList(&nsStyleBackground::Layer::mClip, + &nsStyleBackground::mClipCount, + nsCSSProps::kBackgroundClipKTable, + aValue); } nsresult nsComputedDOMStyle::GetBackgroundColor(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); + const nsStyleBackground* bg = GetStyleBackground(); + nsresult rv; - const nsStyleBackground* color = GetStyleBackground(); - nsresult rv = SetToRGBAColor(val, color->mBackgroundColor); - if (NS_FAILED(rv)) { - delete val; - return rv; + if (bg->mBackgroundColor == bg->mFallbackBackgroundColor) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); + + rv = SetToRGBAColor(val, bg->mBackgroundColor); + if (NS_FAILED(rv)) { + delete val; + return rv; + } + rv = CallQueryInterface(val, aValue); + } else { + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_FALSE); + NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); + + for (PRUint32 i = 0; i < 2; ++i) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + if (!val || !valueList->AppendCSSValue(val)) { + delete val; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = SetToRGBAColor(val, (i == 0) ? bg->mBackgroundColor + : bg->mFallbackBackgroundColor); + if (NS_FAILED(rv)) { + delete valueList; + return rv; + } + } + rv = CallQueryInterface(valueList, aValue); } - return CallQueryInterface(val, aValue); + return rv; } nsresult nsComputedDOMStyle::GetBackgroundImage(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); + const nsStyleBackground* bg = GetStyleBackground(); - const nsStyleBackground* color = GetStyleBackground(); + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE); + NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); - if (color->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) { - val->SetIdent(eCSSKeyword_none); - } else { - nsCOMPtr uri; - if (color->mBackgroundImage) { - color->mBackgroundImage->GetURI(getter_AddRefs(uri)); + for (PRUint32 i = 0, i_end = bg->mImageCount; i < i_end; ++i) { + nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); + if (!val || !valueList->AppendCSSValue(val)) { + delete val; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + + imgIRequest *image = bg->mLayers[i].mImage.mRequest; + if (!image) { + val->SetIdent(eCSSKeyword_none); + } else { + nsCOMPtr uri; + image->GetURI(getter_AddRefs(uri)); + val->SetURI(uri); } - val->SetURI(uri); } - return CallQueryInterface(val, aValue); + return CallQueryInterface(valueList, aValue); } nsresult @@ -1246,56 +1294,55 @@ nsComputedDOMStyle::GetBackgroundInlinePolicy(nsIDOMCSSValue** aValue) nsresult nsComputedDOMStyle::GetBackgroundOrigin(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); - - val->SetIdent( - nsCSSProps::ValueToKeywordEnum(GetStyleBackground()->mBackgroundOrigin, - nsCSSProps::kBackgroundOriginKTable)); - - return CallQueryInterface(val, aValue); + return GetBackgroundList(&nsStyleBackground::Layer::mOrigin, + &nsStyleBackground::mOriginCount, + nsCSSProps::kBackgroundOriginKTable, + aValue); } nsresult nsComputedDOMStyle::GetBackgroundPosition(nsIDOMCSSValue** aValue) { - nsDOMCSSValueList *valueList = GetROCSSValueList(PR_FALSE); + const nsStyleBackground* bg = GetStyleBackground(); + + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE); NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); - nsROCSSPrimitiveValue *valX = GetROCSSPrimitiveValue(); - if (!valX || !valueList->AppendCSSValue(valX)) { - delete valueList; - delete valX; - return NS_ERROR_OUT_OF_MEMORY; - } + for (PRUint32 i = 0, i_end = bg->mPositionCount; i < i_end; ++i) { + nsDOMCSSValueList *itemList = GetROCSSValueList(PR_FALSE); + if (!itemList || !valueList->AppendCSSValue(itemList)) { + delete valueList; + delete itemList; + return NS_ERROR_OUT_OF_MEMORY; + } - nsROCSSPrimitiveValue *valY = GetROCSSPrimitiveValue(); - if (!valY || !valueList->AppendCSSValue(valY)) { - delete valueList; - delete valY; - return NS_ERROR_OUT_OF_MEMORY; - } + nsROCSSPrimitiveValue *valX = GetROCSSPrimitiveValue(); + if (!valX || !itemList->AppendCSSValue(valX)) { + delete valueList; + delete valX; + return NS_ERROR_OUT_OF_MEMORY; + } - const nsStyleBackground *bg = GetStyleBackground(); + nsROCSSPrimitiveValue *valY = GetROCSSPrimitiveValue(); + if (!valY || !itemList->AppendCSSValue(valY)) { + delete valueList; + delete valY; + return NS_ERROR_OUT_OF_MEMORY; + } - if (NS_STYLE_BG_X_POSITION_LENGTH & bg->mBackgroundFlags) { - valX->SetAppUnits(bg->mBackgroundXPosition.mCoord); - } - else if (NS_STYLE_BG_X_POSITION_PERCENT & bg->mBackgroundFlags) { - valX->SetPercent(bg->mBackgroundXPosition.mFloat); - } - else { - valX->SetPercent(0.0f); - } + const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition; - if (NS_STYLE_BG_Y_POSITION_LENGTH & bg->mBackgroundFlags) { - valY->SetAppUnits(bg->mBackgroundYPosition.mCoord); - } - else if (NS_STYLE_BG_Y_POSITION_PERCENT & bg->mBackgroundFlags) { - valY->SetPercent(bg->mBackgroundYPosition.mFloat); - } - else { - valY->SetPercent(0.0f); + if (pos.mXIsPercent) { + valX->SetPercent(pos.mXPosition.mFloat); + } else { + valX->SetAppUnits(pos.mXPosition.mCoord); + } + + if (pos.mYIsPercent) { + valY->SetPercent(pos.mYPosition.mFloat); + } else { + valY->SetAppUnits(pos.mYPosition.mCoord); + } } return CallQueryInterface(valueList, aValue); @@ -1304,14 +1351,10 @@ nsComputedDOMStyle::GetBackgroundPosition(nsIDOMCSSValue** aValue) nsresult nsComputedDOMStyle::GetBackgroundRepeat(nsIDOMCSSValue** aValue) { - nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); - NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY); - - val->SetIdent( - nsCSSProps::ValueToKeywordEnum(GetStyleBackground()->mBackgroundRepeat, - nsCSSProps::kBackgroundRepeatKTable)); - - return CallQueryInterface(val, aValue); + return GetBackgroundList(&nsStyleBackground::Layer::mRepeat, + &nsStyleBackground::mRepeatCount, + nsCSSProps::kBackgroundRepeatKTable, + aValue); } nsresult @@ -1831,7 +1874,7 @@ nsComputedDOMStyle::GetCSSShadowArray(nsCSSShadowArray* aArray, NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); for (nsCSSShadowItem *item = aArray->ShadowAt(0), - *item_end = item + aArray->Length(); + *item_end = item + aArray->Length(); item < item_end; ++item) { nsDOMCSSValueList *itemList = GetROCSSValueList(PR_FALSE); if (!itemList || !valueList->AppendCSSValue(itemList)) { diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 7e063aa6c8a..0a04ceea012 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -55,6 +55,7 @@ #include "nsCOMPtr.h" #include "nsWeakReference.h" #include "nsAutoPtr.h" +#include "nsStyleStruct.h" class nsComputedDOMStyle : public nsIComputedDOMStyle { @@ -116,6 +117,11 @@ private: PRBool aIsBoxShadow, nsIDOMCSSValue** aValue); + nsresult GetBackgroundList(PRUint8 nsStyleBackground::Layer::* aMember, + PRUint32 nsStyleBackground::* aCount, + const PRInt32 aTable[], + nsIDOMCSSValue** aResult); + /* Properties Queryable as CSSValues */ nsresult GetAppearance(nsIDOMCSSValue** aValue); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 39a2a2824c4..740e660cf97 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1420,8 +1420,7 @@ nsRuleNode::GetUIResetData(nsStyleContext* aContext) nsRuleData ruleData(NS_STYLE_INHERIT_BIT(UIReset), mPresContext, aContext); ruleData.mUserInterfaceData = &uiData; - const void* res = WalkRuleTree(eStyleStruct_UIReset, aContext, &ruleData, &uiData); - return res; + return WalkRuleTree(eStyleStruct_UIReset, aContext, &ruleData, &uiData); } const void* @@ -1455,7 +1454,17 @@ nsRuleNode::GetBackgroundData(nsStyleContext* aContext) // null in HasAuthorSpecifiedRules (look at mBoxShadow in GetBorderData // and HasAuthorSpecifiedRules). - return WalkRuleTree(eStyleStruct_Background, aContext, &ruleData, &colorData); + const void *res = WalkRuleTree(eStyleStruct_Background, aContext, &ruleData, &colorData); + + // We are sharing with some style rule. It really owns the data. + colorData.mBackImage = nsnull; + colorData.mBackRepeat = nsnull; + colorData.mBackAttachment = nsnull; + colorData.mBackPosition = nsnull; + colorData.mBackClip = nsnull; + colorData.mBackOrigin = nsnull; + + return res; } const void* @@ -3771,6 +3780,189 @@ nsRuleNode::ComputeColorData(void* aStartStruct, COMPUTE_END_INHERITED(Color, color) } +// information about how to compute values for background-* properties +template +struct InitialInheritLocationFor { +}; + +NS_SPECIALIZE_TEMPLATE +struct InitialInheritLocationFor { + static nsCSSValue nsCSSValueList::* Location() { + return &nsCSSValueList::mValue; + } +}; + +NS_SPECIALIZE_TEMPLATE +struct InitialInheritLocationFor { + static nsCSSValue nsCSSValuePairList::* Location() { + return &nsCSSValuePairList::mXValue; + } +}; + +template +struct BackgroundItemComputer { +}; + +NS_SPECIALIZE_TEMPLATE +struct BackgroundItemComputer +{ + static void ComputeValue(nsStyleContext* aStyleContext, + const nsCSSValueList* aSpecifiedValue, + PRUint8& aComputedValue, + PRBool& aCanStoreInRuleTree) + { + SetDiscrete(aSpecifiedValue->mValue, aComputedValue, aCanStoreInRuleTree, + SETDSC_ENUMERATED, PRUint8(0), 0, 0, 0, 0, 0); + } +}; + +NS_SPECIALIZE_TEMPLATE +struct BackgroundItemComputer +{ + static void ComputeValue(nsStyleContext* aStyleContext, + const nsCSSValueList* aSpecifiedValue, + nsStyleBackground::Image& aComputedValue, + PRBool& aCanStoreInRuleTree) + { + const nsCSSValue &value = aSpecifiedValue->mValue; + if (eCSSUnit_Image == value.GetUnit()) { + aComputedValue.mRequest = value.GetImageValue(); + aComputedValue.mSpecified = PR_TRUE; + } + else { + NS_ASSERTION(eCSSUnit_None == value.GetUnit(), "unexpected unit"); + aComputedValue.mRequest = nsnull; + aComputedValue.mSpecified = PR_FALSE; + } + } +}; + +struct BackgroundPositionAxis { + nsCSSValue nsCSSValuePairList::*specified; + nsStyleBackground::Position::PositionCoord + nsStyleBackground::Position::*result; + PRPackedBool nsStyleBackground::Position::*isPercent; +}; + +static const BackgroundPositionAxis gBGPosAxes[] = { + { &nsCSSValuePairList::mXValue, + &nsStyleBackground::Position::mXPosition, + &nsStyleBackground::Position::mXIsPercent }, + { &nsCSSValuePairList::mYValue, + &nsStyleBackground::Position::mYPosition, + &nsStyleBackground::Position::mYIsPercent } +}; + +NS_SPECIALIZE_TEMPLATE +struct BackgroundItemComputer +{ + static void ComputeValue(nsStyleContext* aStyleContext, + const nsCSSValuePairList* aSpecifiedValue, + nsStyleBackground::Position& aComputedValue, + PRBool& aCanStoreInRuleTree) + { + nsStyleBackground::Position &position = aComputedValue; + for (const BackgroundPositionAxis *axis = gBGPosAxes, + *axis_end = gBGPosAxes + NS_ARRAY_LENGTH(gBGPosAxes); + axis != axis_end; ++axis) { + const nsCSSValue &specified = aSpecifiedValue->*(axis->specified); + if (eCSSUnit_Percent == specified.GetUnit()) { + (position.*(axis->result)).mFloat = specified.GetPercentValue(); + position.*(axis->isPercent) = PR_TRUE; + } + else if (specified.IsLengthUnit()) { + (position.*(axis->result)).mCoord = + CalcLength(specified, aStyleContext, aStyleContext->PresContext(), + aCanStoreInRuleTree); + position.*(axis->isPercent) = PR_FALSE; + } + else if (eCSSUnit_Enumerated == specified.GetUnit()) { + (position.*(axis->result)).mFloat = + GetFloatFromBoxPosition(specified.GetIntValue()); + position.*(axis->isPercent) = PR_TRUE; + } else { + NS_NOTREACHED("unexpected unit"); + } + } + } +}; + + +template +static void +SetBackgroundList(nsStyleContext* aStyleContext, + const SpecifiedValueItem* aValueList, + nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, + const nsAutoTArray + &aParentLayers, + ComputedValueItem nsStyleBackground::Layer::* aResultLocation, + ComputedValueItem aInitialValue, + PRUint32 aParentItemCount, + PRUint32& aItemCount, + PRUint32& aMaxItemCount, + PRBool& aRebuild, + PRBool& aCanStoreInRuleTree) +{ + if (aValueList) { + aRebuild = PR_TRUE; + nsCSSValue SpecifiedValueItem::* initialInherit = + InitialInheritLocationFor::Location(); + if (eCSSUnit_Inherit == (aValueList->*initialInherit).GetUnit()) { + NS_ASSERTION(!aValueList->mNext, "should have only one value"); + aCanStoreInRuleTree = PR_FALSE; + if (!aLayers.EnsureLengthAtLeast(aParentItemCount)) { + NS_WARNING("out of memory"); + aParentItemCount = aLayers.Length(); + } + aItemCount = aParentItemCount; + for (PRUint32 i = 0; i < aParentItemCount; ++i) { + aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; + } + } else if (eCSSUnit_Initial == (aValueList->*initialInherit).GetUnit()) { + NS_ASSERTION(!aValueList->mNext, "should have only one value"); + aItemCount = 1; + aLayers[0].*aResultLocation = aInitialValue; + } else { + const SpecifiedValueItem *item = aValueList; + aItemCount = 0; + do { + NS_ASSERTION((item->*initialInherit).GetUnit() != eCSSUnit_Inherit && + (item->*initialInherit).GetUnit() != eCSSUnit_Initial, + "unexpected unit"); + ++aItemCount; + if (!aLayers.EnsureLengthAtLeast(aItemCount)) { + NS_WARNING("out of memory"); + --aItemCount; + break; + } + BackgroundItemComputer + ::ComputeValue(aStyleContext, item, + aLayers[aItemCount-1].*aResultLocation, + aCanStoreInRuleTree); + item = item->mNext; + } while (item); + } + } + + if (aItemCount > aMaxItemCount) + aMaxItemCount = aItemCount; +} + +template +static void +FillBackgroundList(nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, + ComputedValueItem nsStyleBackground::Layer::* aResultLocation, + PRUint32 aItemCount, PRUint32 aFillCount) +{ + NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length"); + for (PRUint32 sourceLayer = 0, destLayer = aItemCount; + destLayer < aFillCount; + ++sourceLayer, ++destLayer) { + aLayers[destLayer].*aResultLocation = + aLayers[sourceLayer].*aResultLocation; + } +} + const void* nsRuleNode::ComputeBackgroundData(void* aStartStruct, const nsRuleDataStruct& aData, @@ -3781,52 +3973,55 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, { COMPUTE_START_RESET(Background, (), bg, parentBG, Color, colorData) - // save parentFlags in case bg == parentBG and we clobber them later - PRUint8 parentFlags = parentBG->mBackgroundFlags; - - // background-color: color, string, inherit - if (eCSSUnit_Initial == colorData.mBackColor.GetUnit()) { + // background-color: color, string, inherit [pair] + if (eCSSUnit_Initial == colorData.mBackColor.mXValue.GetUnit()) { bg->mBackgroundColor = NS_RGBA(0, 0, 0, 0); - } else if (!SetColor(colorData.mBackColor, parentBG->mBackgroundColor, - mPresContext, aContext, bg->mBackgroundColor, - canStoreInRuleTree)) { - NS_ASSERTION(eCSSUnit_Null == colorData.mBackColor.GetUnit(), + } else if (!SetColor(colorData.mBackColor.mXValue, + parentBG->mBackgroundColor, mPresContext, + aContext, bg->mBackgroundColor, canStoreInRuleTree)) { + NS_ASSERTION(eCSSUnit_Null == colorData.mBackColor.mXValue.GetUnit(), "unexpected color unit"); } - // background-image: url (stored as image), none, inherit - if (eCSSUnit_Image == colorData.mBackImage.GetUnit()) { - bg->mBackgroundImage = colorData.mBackImage.GetImageValue(); - } - else if (eCSSUnit_None == colorData.mBackImage.GetUnit() || - eCSSUnit_Initial == colorData.mBackImage.GetUnit()) { - bg->mBackgroundImage = nsnull; - } - else if (eCSSUnit_Inherit == colorData.mBackImage.GetUnit()) { - canStoreInRuleTree = PR_FALSE; - bg->mBackgroundImage = parentBG->mBackgroundImage; + if (eCSSUnit_Initial == colorData.mBackColor.mYValue.GetUnit()) { + bg->mFallbackBackgroundColor = NS_RGBA(0, 0, 0, 0); + } else if (!SetColor(colorData.mBackColor.mYValue, + parentBG->mFallbackBackgroundColor, mPresContext, + aContext, bg->mFallbackBackgroundColor, + canStoreInRuleTree)) { + NS_ASSERTION(eCSSUnit_Null == colorData.mBackColor.mYValue.GetUnit(), + "unexpected color unit"); } - if (bg->mBackgroundImage) { - bg->mBackgroundFlags &= ~NS_STYLE_BG_IMAGE_NONE; - } else { - bg->mBackgroundFlags |= NS_STYLE_BG_IMAGE_NONE; - } + PRUint32 maxItemCount = 1; + PRBool rebuild = PR_FALSE; - // background-repeat: enum, inherit, initial - SetDiscrete(colorData.mBackRepeat, bg->mBackgroundRepeat, canStoreInRuleTree, - SETDSC_ENUMERATED, parentBG->mBackgroundRepeat, - NS_STYLE_BG_REPEAT_XY, 0, 0, 0, 0); + // background-image: url (stored as image), none, inherit [list] + SetBackgroundList(aContext, colorData.mBackImage, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mImage, + nsStyleBackground::Image(), parentBG->mImageCount, + bg->mImageCount, maxItemCount, rebuild, canStoreInRuleTree); - // background-attachment: enum, inherit, initial - SetDiscrete(colorData.mBackAttachment, bg->mBackgroundAttachment, canStoreInRuleTree, - SETDSC_ENUMERATED, parentBG->mBackgroundAttachment, - NS_STYLE_BG_ATTACHMENT_SCROLL, 0, 0, 0, 0); + // background-repeat: enum, inherit, initial [list] + SetBackgroundList(aContext, colorData.mBackRepeat, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mRepeat, + PRUint8(NS_STYLE_BG_REPEAT_XY), parentBG->mRepeatCount, + bg->mRepeatCount, maxItemCount, rebuild, canStoreInRuleTree); - // background-clip: enum, inherit, initial - SetDiscrete(colorData.mBackClip, bg->mBackgroundClip, canStoreInRuleTree, - SETDSC_ENUMERATED, parentBG->mBackgroundClip, - NS_STYLE_BG_CLIP_BORDER, 0, 0, 0, 0); + // background-attachment: enum, inherit, initial [list] + SetBackgroundList(aContext, colorData.mBackAttachment, bg->mLayers, + parentBG->mLayers, + &nsStyleBackground::Layer::mAttachment, + PRUint8(NS_STYLE_BG_ATTACHMENT_SCROLL), + parentBG->mAttachmentCount, + bg->mAttachmentCount, maxItemCount, rebuild, + canStoreInRuleTree); + + // background-clip: enum, inherit, initial [list] + SetBackgroundList(aContext, colorData.mBackClip, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mClip, + PRUint8(NS_STYLE_BG_CLIP_BORDER), parentBG->mClipCount, + bg->mClipCount, maxItemCount, rebuild, canStoreInRuleTree); // background-inline-policy: enum, inherit, initial SetDiscrete(colorData.mBackInlinePolicy, bg->mBackgroundInlinePolicy, @@ -3834,66 +4029,40 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, parentBG->mBackgroundInlinePolicy, NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, 0, 0, 0, 0); - // background-origin: enum, inherit, initial - SetDiscrete(colorData.mBackOrigin, bg->mBackgroundOrigin, canStoreInRuleTree, - SETDSC_ENUMERATED, parentBG->mBackgroundOrigin, - NS_STYLE_BG_ORIGIN_PADDING, 0, 0, 0, 0); + // background-origin: enum, inherit, initial [list] + SetBackgroundList(aContext, colorData.mBackOrigin, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mOrigin, + PRUint8(NS_STYLE_BG_ORIGIN_PADDING), parentBG->mOriginCount, + bg->mOriginCount, maxItemCount, rebuild, + canStoreInRuleTree); - // background-position: enum, length, percent (flags), inherit - if (eCSSUnit_Percent == colorData.mBackPosition.mXValue.GetUnit()) { - bg->mBackgroundXPosition.mFloat = colorData.mBackPosition.mXValue.GetPercentValue(); - bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_PERCENT; - bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_LENGTH; - } - else if (colorData.mBackPosition.mXValue.IsLengthUnit()) { - bg->mBackgroundXPosition.mCoord = CalcLength(colorData.mBackPosition.mXValue, - aContext, mPresContext, canStoreInRuleTree); - bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_LENGTH; - bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_PERCENT; - } - else if (eCSSUnit_Enumerated == colorData.mBackPosition.mXValue.GetUnit()) { - bg->mBackgroundXPosition.mFloat = - GetFloatFromBoxPosition(colorData.mBackPosition.mXValue.GetIntValue()); + // background-position: enum, length, percent (flags), inherit [pair list] + nsStyleBackground::Position initialPosition; + initialPosition.SetInitialValues(); + SetBackgroundList(aContext, colorData.mBackPosition, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mPosition, + initialPosition, parentBG->mPositionCount, + bg->mPositionCount, maxItemCount, rebuild, + canStoreInRuleTree); - bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_PERCENT; - bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_LENGTH; - } - else if (eCSSUnit_Inherit == colorData.mBackPosition.mXValue.GetUnit()) { - canStoreInRuleTree = PR_FALSE; - bg->mBackgroundXPosition = parentBG->mBackgroundXPosition; - bg->mBackgroundFlags &= ~(NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT); - bg->mBackgroundFlags |= (parentFlags & (NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT)); - } - else if (eCSSUnit_Initial == colorData.mBackPosition.mXValue.GetUnit()) { - bg->mBackgroundFlags &= ~(NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT); - } + if (rebuild) { + // Delete any extra items. We need to keep layers in which any + // property was specified. + bg->mLayers.TruncateLength(maxItemCount); - if (eCSSUnit_Percent == colorData.mBackPosition.mYValue.GetUnit()) { - bg->mBackgroundYPosition.mFloat = colorData.mBackPosition.mYValue.GetPercentValue(); - bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_PERCENT; - bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_LENGTH; - } - else if (colorData.mBackPosition.mYValue.IsLengthUnit()) { - bg->mBackgroundYPosition.mCoord = CalcLength(colorData.mBackPosition.mYValue, - aContext, mPresContext, canStoreInRuleTree); - bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_LENGTH; - bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_PERCENT; - } - else if (eCSSUnit_Enumerated == colorData.mBackPosition.mYValue.GetUnit()) { - bg->mBackgroundYPosition.mFloat = - GetFloatFromBoxPosition(colorData.mBackPosition.mYValue.GetIntValue()); - - bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_PERCENT; - bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_LENGTH; - } - else if (eCSSUnit_Inherit == colorData.mBackPosition.mYValue.GetUnit()) { - canStoreInRuleTree = PR_FALSE; - bg->mBackgroundYPosition = parentBG->mBackgroundYPosition; - bg->mBackgroundFlags &= ~(NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT); - bg->mBackgroundFlags |= (parentFlags & (NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT)); - } - else if (eCSSUnit_Initial == colorData.mBackPosition.mYValue.GetUnit()) { - bg->mBackgroundFlags &= ~(NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT); + PRUint32 fillCount = bg->mImageCount; + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mImage, + bg->mImageCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mRepeat, + bg->mRepeatCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mAttachment, + bg->mAttachmentCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mClip, + bg->mClipCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mOrigin, + bg->mOriginCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mPosition, + bg->mPositionCount, fillCount); } COMPUTE_END_RESET(Background, bg) @@ -5499,6 +5668,7 @@ nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, { nsRuleDataColor colorData; nsRuleDataMargin marginData; + nsCSSValue firstBackgroundImage; PRUint32 nValues = 0; PRUint32 inheritBits = 0; @@ -5518,8 +5688,9 @@ nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, ruleData.mMarginData = &marginData; nsCSSValue* backgroundValues[] = { - &colorData.mBackColor, - &colorData.mBackImage + &colorData.mBackColor.mXValue, + &colorData.mBackColor.mYValue, + &firstBackgroundImage }; nsCSSValue* borderValues[] = { @@ -5583,11 +5754,25 @@ nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); + rule->MapRuleInfoInto(&ruleData); + + if ((ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) && + colorData.mBackImage && + firstBackgroundImage.GetUnit() == eCSSUnit_Null) { + // Handle background-image being a value list + firstBackgroundImage = colorData.mBackImage->mValue; + } // Do the same nulling out as in GetBorderData, GetBackgroundData // or GetPaddingData. // We are sharing with some style rule. It really owns the data. marginData.mBoxShadow = nsnull; + colorData.mBackImage = nsnull; + colorData.mBackRepeat = nsnull; + colorData.mBackAttachment = nsnull; + colorData.mBackPosition = nsnull; + colorData.mBackClip = nsnull; + colorData.mBackOrigin = nsnull; if (ruleData.mLevel == nsStyleSet::eAgentSheet || ruleData.mLevel == nsStyleSet::eUserSheet) { diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index a73e9c9252c..5f5cda6f3d5 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1207,28 +1207,44 @@ nsChangeHint nsStyleColor::MaxDifference() // nsStyleBackground::nsStyleBackground() - : mBackgroundFlags(NS_STYLE_BG_IMAGE_NONE), - mBackgroundAttachment(NS_STYLE_BG_ATTACHMENT_SCROLL), - mBackgroundClip(NS_STYLE_BG_CLIP_BORDER), - mBackgroundInlinePolicy(NS_STYLE_BG_INLINE_POLICY_CONTINUOUS), - mBackgroundOrigin(NS_STYLE_BG_ORIGIN_PADDING), - mBackgroundRepeat(NS_STYLE_BG_REPEAT_XY), - mBackgroundColor(NS_RGBA(0, 0, 0, 0)) + : mAttachmentCount(1) + , mClipCount(1) + , mOriginCount(1) + , mRepeatCount(1) + , mPositionCount(1) + , mImageCount(1) + , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) + , mFallbackBackgroundColor(NS_RGBA(0, 0, 0, 0)) + , mBackgroundInlinePolicy(NS_STYLE_BG_INLINE_POLICY_CONTINUOUS) { + Layer *onlyLayer = mLayers.AppendElement(); + NS_ASSERTION(onlyLayer, "auto array must have room for 1 element"); + onlyLayer->SetInitialValues(); } nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) - : mBackgroundFlags(aSource.mBackgroundFlags), - mBackgroundAttachment(aSource.mBackgroundAttachment), - mBackgroundClip(aSource.mBackgroundClip), - mBackgroundInlinePolicy(aSource.mBackgroundInlinePolicy), - mBackgroundOrigin(aSource.mBackgroundOrigin), - mBackgroundRepeat(aSource.mBackgroundRepeat), - mBackgroundXPosition(aSource.mBackgroundXPosition), - mBackgroundYPosition(aSource.mBackgroundYPosition), - mBackgroundColor(aSource.mBackgroundColor), - mBackgroundImage(aSource.mBackgroundImage) + : mAttachmentCount(aSource.mAttachmentCount) + , mClipCount(aSource.mClipCount) + , mOriginCount(aSource.mOriginCount) + , mRepeatCount(aSource.mRepeatCount) + , mPositionCount(aSource.mPositionCount) + , mImageCount(aSource.mImageCount) + , mLayers(aSource.mLayers) // deep copy + , mBackgroundColor(aSource.mBackgroundColor) + , mFallbackBackgroundColor(aSource.mFallbackBackgroundColor) + , mBackgroundInlinePolicy(aSource.mBackgroundInlinePolicy) { + // If the deep copy of mLayers failed, truncate the counts. + PRUint32 count = mLayers.Length(); + if (count != aSource.mLayers.Length()) { + NS_WARNING("truncating counts due to out-of-memory"); + mAttachmentCount = PR_MAX(mAttachmentCount, count); + mClipCount = PR_MAX(mClipCount, count); + mOriginCount = PR_MAX(mOriginCount, count); + mRepeatCount = PR_MAX(mRepeatCount, count); + mPositionCount = PR_MAX(mPositionCount, count); + mImageCount = PR_MAX(mImageCount, count); + } } nsStyleBackground::~nsStyleBackground() @@ -1237,24 +1253,19 @@ nsStyleBackground::~nsStyleBackground() nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const { - if ((mBackgroundAttachment == aOther.mBackgroundAttachment) && - (mBackgroundFlags == aOther.mBackgroundFlags) && - (mBackgroundRepeat == aOther.mBackgroundRepeat) && - (mBackgroundColor == aOther.mBackgroundColor) && - (mBackgroundClip == aOther.mBackgroundClip) && - (mBackgroundInlinePolicy == aOther.mBackgroundInlinePolicy) && - (mBackgroundOrigin == aOther.mBackgroundOrigin) && - EqualImages(mBackgroundImage, aOther.mBackgroundImage) && - ((!(mBackgroundFlags & NS_STYLE_BG_X_POSITION_PERCENT) || - (mBackgroundXPosition.mFloat == aOther.mBackgroundXPosition.mFloat)) && - (!(mBackgroundFlags & NS_STYLE_BG_X_POSITION_LENGTH) || - (mBackgroundXPosition.mCoord == aOther.mBackgroundXPosition.mCoord))) && - ((!(mBackgroundFlags & NS_STYLE_BG_Y_POSITION_PERCENT) || - (mBackgroundYPosition.mFloat == aOther.mBackgroundYPosition.mFloat)) && - (!(mBackgroundFlags & NS_STYLE_BG_Y_POSITION_LENGTH) || - (mBackgroundYPosition.mCoord == aOther.mBackgroundYPosition.mCoord)))) - return NS_STYLE_HINT_NONE; - return NS_STYLE_HINT_VISUAL; + if (mBackgroundColor != aOther.mBackgroundColor || + mFallbackBackgroundColor != aOther.mFallbackBackgroundColor || + mBackgroundInlinePolicy != aOther.mBackgroundInlinePolicy || + mImageCount != aOther.mImageCount) + return NS_STYLE_HINT_VISUAL; + + // 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; + } + + return NS_STYLE_HINT_NONE; } #ifdef DEBUG @@ -1267,8 +1278,80 @@ nsChangeHint nsStyleBackground::MaxDifference() PRBool nsStyleBackground::HasFixedBackground() const { - return mBackgroundAttachment == NS_STYLE_BG_ATTACHMENT_FIXED && - mBackgroundImage; + 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.mRequest) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +PRBool nsStyleBackground::IsTransparent() const +{ + return !BottomLayer().mImage.mRequest && mImageCount == 1 && + NS_GET_A(mBackgroundColor) == 0; +} + +void +nsStyleBackground::Position::SetInitialValues() +{ + mXPosition.mFloat = 0.0f; + mYPosition.mFloat = 0.0f; + mXIsPercent = PR_TRUE; + mYIsPercent = PR_TRUE; +} + +// Initialize to initial values +nsStyleBackground::Image::Image() +{ + SetInitialValues(); +} + +nsStyleBackground::Image::~Image() +{ +} + +void nsStyleBackground::Image::SetInitialValues() +{ + mRequest = nsnull; + mSpecified = PR_FALSE; +} + +PRBool nsStyleBackground::Image::operator==(const Image& aOther) const +{ + return mSpecified == aOther.mSpecified && + EqualImages(mRequest, aOther.mRequest); +} + +nsStyleBackground::Layer::Layer() +{ +} + +nsStyleBackground::Layer::~Layer() +{ +} + +void +nsStyleBackground::Layer::SetInitialValues() +{ + mAttachment = NS_STYLE_BG_ATTACHMENT_SCROLL; + mClip = NS_STYLE_BG_CLIP_BORDER; + mOrigin = NS_STYLE_BG_ORIGIN_PADDING; + mRepeat = NS_STYLE_BG_REPEAT_XY; + mPosition.SetInitialValues(); + mImage.SetInitialValues(); +} + +PRBool nsStyleBackground::Layer::operator==(const Layer& aOther) const +{ + return mAttachment == aOther.mAttachment && + mClip == aOther.mClip && + mOrigin == aOther.mOrigin && + mRepeat == aOther.mRepeat && + mPosition == aOther.mPosition && + mImage == aOther.mImage; } // -------------------- diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index de7b1cd35fb..13b8614ea18 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -58,6 +58,7 @@ #include "nsIPresShell.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" +#include "nsTArray.h" #include "nsIAtom.h" #include "nsIURI.h" #include "nsCSSValue.h" @@ -164,30 +165,122 @@ struct nsStyleBackground { static nsChangeHint MaxDifference(); #endif - PRUint8 mBackgroundFlags; // [reset] See nsStyleConsts.h - PRUint8 mBackgroundAttachment; // [reset] See nsStyleConsts.h - PRUint8 mBackgroundClip; // [reset] See nsStyleConsts.h - PRUint8 mBackgroundInlinePolicy; // [reset] See nsStyleConsts.h - PRUint8 mBackgroundOrigin; // [reset] See nsStyleConsts.h - PRUint8 mBackgroundRepeat; // [reset] See nsStyleConsts.h + struct Position; + friend struct Position; + struct Position { + typedef union { + nscoord mCoord; // for lengths + float mFloat; // for percents + } PositionCoord; + PositionCoord mXPosition, mYPosition; + PRPackedBool mXIsPercent, mYIsPercent; - // Note: a member of this union is valid IFF the appropriate bit flag - // is set in mBackgroundFlags. - union { - nscoord mCoord; - float mFloat; - } mBackgroundXPosition, // [reset] - mBackgroundYPosition; // [reset] + // Initialize nothing + Position() {} + + // Initialize to initial values + void SetInitialValues(); + + PRBool operator==(const Position& aOther) const { + return mXIsPercent == aOther.mXIsPercent && + (mXIsPercent ? (mXPosition.mFloat == aOther.mXPosition.mFloat) + : (mXPosition.mCoord == aOther.mXPosition.mCoord)) && + mYIsPercent == aOther.mYIsPercent && + (mYIsPercent ? (mYPosition.mFloat == aOther.mYPosition.mFloat) + : (mYPosition.mCoord == aOther.mYPosition.mCoord)); + } + PRBool operator!=(const Position& aOther) const { + return !(*this == aOther); + } + }; + + /** + * We represent images as as struct because we need to distinguish the + * case where the imgIRequest is null because the winning + * background-image declaration specified no image from the case where + * the imgIRequest is null because the image that was specified was + * blocked or missing (e.g., missing file). + */ + struct Image; + friend struct Image; + struct Image { + nsCOMPtr mRequest; + PRBool mSpecified; // if false, mRequest is guaranteed to be null + + // These are not inline so that we can avoid #include "imgIRequest.h" + + // Initialize to initial values + Image(); + ~Image(); + void SetInitialValues(); + + // An equality operator that compares the images using URL-equality + // rather than pointer-equality. + PRBool operator==(const Image& aOther) const; + PRBool operator!=(const Image& aOther) const { + return !(*this == aOther); + } + }; + + struct Layer; + friend struct Layer; + struct Layer { + PRUint8 mAttachment; // [reset] See nsStyleConsts.h + PRUint8 mClip; // [reset] See nsStyleConsts.h + PRUint8 mOrigin; // [reset] See nsStyleConsts.h + PRUint8 mRepeat; // [reset] See nsStyleConsts.h + Position mPosition; // [reset] + Image mImage; // [reset] + + // Initializes only mImage + Layer(); + ~Layer(); + + void SetInitialValues(); + + // An equality operator that compares the images using URL-equality + // rather than pointer-equality. + PRBool operator==(const Layer& aOther) const; + PRBool operator!=(const Layer& aOther) const { + return !(*this == aOther); + } + }; + + // The (positive) number of computed values of each property, since + // the lengths of the lists are independent. + PRUint32 mAttachmentCount, + mClipCount, + mOriginCount, + mRepeatCount, + mPositionCount, + mImageCount; + // 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 + // should come from the background-image property. We create + // additional |Layer| objects for *any* property, not just + // background-image. This means that the bottommost layer that + // callers in layout care about (which is also the one whose + // background-clip applies to the background-color) may not be last + // layer. In layers below the bottom layer, properties will be + // unitialized unless their count, above, indicates that they are + // present. + nsAutoTArray mLayers; + + const Layer& BottomLayer() const { return mLayers[mImageCount - 1]; } + + #define NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(var_, stylebg_) \ + for (PRUint32 var_ = (stylebg_)->mImageCount; var_-- != 0; ) nscolor mBackgroundColor; // [reset] - nsCOMPtr mBackgroundImage; // [reset] + nscolor mFallbackBackgroundColor; // [reset] + + // FIXME: This (now background-break in css3-background) should + // probably move into a different struct so that everything in + // nsStyleBackground is set by the background shorthand. + PRUint8 mBackgroundInlinePolicy; // [reset] See nsStyleConsts.h // True if this background is completely transparent. - PRBool IsTransparent() const - { - return (NS_GET_A(mBackgroundColor) == 0 && - (mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE)); - } + PRBool IsTransparent() const; // We have to take slower codepaths for fixed background attachment, // but we don't want to do that when there's no image. diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index bdaaf2f4477..02697b51e89 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -72,12 +72,17 @@ var gCSSProperties = { invalid_values: [] }, "-moz-background-clip": { + /* + * When we rename this to 'background-clip', we also + * need to rename the values to match the spec. + */ domProp: "MozBackgroundClip", inherited: false, type: CSS_TYPE_LONGHAND, + /* XXX Need to add support for "content" -- important for symmetry when handling background shorthand */ initial_values: [ "border" ], - other_values: [ "padding" ], - invalid_values: [ "content", "margin" ] + other_values: [ "padding", "border, padding", "padding, padding, padding", "border, border" ], + invalid_values: [ "content", "margin", "border border" ] }, "-moz-background-inline-policy": { domProp: "MozBackgroundInlinePolicy", @@ -92,8 +97,8 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "padding" ], - other_values: [ "border", "content" ], - invalid_values: [ "margin" ] + other_values: [ "border", "content", "border, padding", "padding, padding, padding", "border, border" ], + invalid_values: [ "margin", "padding padding" ] }, "-moz-binding": { domProp: "MozBinding", @@ -608,14 +613,45 @@ var gCSSProperties = { domProp: "background", inherited: false, type: CSS_TYPE_TRUE_SHORTHAND, - subproperties: [ "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "-moz-background-clip", "-moz-background-inline-policy", "-moz-background-origin" ], + subproperties: [ "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "-moz-background-clip", "-moz-background-origin" ], 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: [ "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%" ], + 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%", + /* multiple backgrounds */ + "url(404.png), url(404.png)", + "url(404.png), url(404.png) transparent", + "url(404.png), url(404.png) transparent red", + "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", + /* 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 + // to background-origin, change their value names to *-box, and add + // support for content-box on background-clip. + /* + "url(404.png) green padding-box", + "url(404.png) border-box transparent", + "content-box url(404.png) blue", + */ + ], invalid_values: [ /* mixes with keywords have to be in correct order */ "50% left", "top 50%", /* bug 258080: don't accept background-position separated */ - "left url(404.png) top", "top url(404.png) left" + "left url(404.png) top", "top url(404.png) left", + /* not allowed to have color in non-bottom layer */ + "url(404.png) transparent, url(404.png)", + "url(404.png) red, url(404.png)", + "url(404.png) transparent, url(404.png) transparent", + "url(404.png) transparent red, url(404.png) transparent red", + "url(404.png) red, url(404.png) red", + "url(404.png) rgba(0, 0, 0, 0), url(404.png)", + "url(404.png) rgb(255, 0, 0), url(404.png)", + "url(404.png) rgba(0, 0, 0, 0), url(404.png) rgba(0, 0, 0, 0)", + "url(404.png) rgba(0, 0, 0, 0) rgb(255, 0, 0), url(404.png) rgba(0, 0, 0, 0) rgb(255, 0, 0)", + "url(404.png) rgb(255, 0, 0), url(404.png) rgb(255, 0, 0)", ] }, "background-attachment": { @@ -623,15 +659,15 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "scroll" ], - other_values: [ "fixed" ], + other_values: [ "fixed", "scroll,scroll", "fixed, scroll", "scroll, fixed, scroll", "fixed, fixed" ], invalid_values: [] }, "background-color": { domProp: "backgroundColor", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "transparent", "rgba(255, 127, 15, 0)", "hsla(240, 97%, 50%, 0.0)", "rgba(0, 0, 0, 0)", "rgba(255,255,255,-3.7)" ], - other_values: [ "green", "rgb(255, 0, 128)", "#fc2", "#96ed2a", "black", "rgba(255,255,0,3)" ], + initial_values: [ "transparent", "transparent transparent", "rgba(255, 127, 15, 0)", "hsla(240, 97%, 50%, 0.0)", "rgba(0, 0, 0, 0)", "rgba(255,255,255,-3.7)" ], + other_values: [ "green", "rgb(255, 0, 128)", "#fc2", "#96ed2a", "black", "rgba(255,255,0,3)", "transparent green", "green transparent", "blue fuchsia", "rgb(3,4,5) hsl(240, 50%, 50%)" ], invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "rgb(255.0,0.387,3489)" ] }, "background-image": { @@ -639,15 +675,22 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "none" ], - other_values: [ "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==")', ], + other_values: [ "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==")', + "none, none", + "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==)" + ], invalid_values: [] }, "background-position": { domProp: "backgroundPosition", inherited: false, type: CSS_TYPE_LONGHAND, + /* is "0px 0px" an initial value or not? */ initial_values: [ "top left", "left top", "0% 0%", "0% top", "left 0%" ], - other_values: [ "top", "left", "right", "bottom", "center", "center bottom", "bottom center", "center right", "right center", "center top", "top center", "center left", "left center", "right bottom", "bottom right", "50%" ], + other_values: [ "top", "left", "right", "bottom", "center", "center bottom", "bottom center", "center right", "right center", "center top", "top center", "center left", "left center", "right bottom", "bottom right", "50%", "top left, top left", "top left, top right", "top right, top left", "left top, 0% 0%", "10% 20%, 30%, 40%", "top left, bottom right", "right bottom, left top", "0%", "0px", "30px", "0%, 10%, 20%, 30%", "top, top, top, top, top" ], invalid_values: [ "50% left", "top 50%" ] }, "background-repeat": { @@ -655,8 +698,13 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "repeat" ], - other_values: [ "repeat-x", "repeat-y", "no-repeat" ], - invalid_values: [] + other_values: [ "repeat-x", "repeat-y", "no-repeat", + "repeat-x, repeat-x", + "repeat, no-repeat", + "repeat-y, no-repeat, repeat-y", + "repeat, repeat, repeat" + ], + invalid_values: [ "repeat repeat" ] }, "border": { domProp: "border", diff --git a/layout/style/test/test_shorthand_property_getters.html b/layout/style/test/test_shorthand_property_getters.html index 574664d1ef1..fbfbee931d7 100644 --- a/layout/style/test/test_shorthand_property_getters.html +++ b/layout/style/test/test_shorthand_property_getters.html @@ -106,14 +106,24 @@ is(e.style.font, "", "should not have font shorthand"); e.setAttribute("style", "font: medium serif; font-stretch: condensed"); is(e.style.font, "", "should not have font shorthand"); +// For background, we can only express the value as a shorthand if +// origin and clip are both their default, or if they're both the same. +// ... or at least we will once we support them in the shorthand. e.setAttribute("style", "background: red"); isnot(e.style.background, "", "should have background shorthand"); e.setAttribute("style", "background: red; -moz-background-origin: border"); -is(e.style.background, "", "should not have background shorthand"); +is(e.style.background, "", "should not have background shorthand (origin:border)"); e.setAttribute("style", "background: red; -moz-background-clip: padding"); -is(e.style.background, "", "should not have background shorthand"); +is(e.style.background, "", "should not have background shorthand (clip:padding)"); +e.setAttribute("style", "background: red; -moz-background-origin: content"); +is(e.style.background, "", "should not have background shorthand (origin:content)"); +// -moz-background-clip:content not yet supported +//e.setAttribute("style", "background: red; -moz-background-clip: content"); +//is(e.style.background, "", "should not have background shorthand (clip:content)"); +//e.setAttribute("style", "background: red; -moz-background-clip: content; -moz-background-origin: content;"); +//isnot(e.style.background, "", "should have background shorthand (clip:content;origin:content)"); e.setAttribute("style", "background: red; -moz-background-inline-policy: each-box"); -is(e.style.background, "", "should not have background shorthand"); +isnot(e.style.background, "", "should have background shorthand (-moz-background-inline-policy not relevant)"); diff --git a/layout/tables/nsTablePainter.cpp b/layout/tables/nsTablePainter.cpp index 2e277894fd3..37d661ec51a 100644 --- a/layout/tables/nsTablePainter.cpp +++ b/layout/tables/nsTablePainter.cpp @@ -197,7 +197,15 @@ inline PRBool TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder() { /* we only need accurate border data when positioning background images*/ - return mBackground && !(mBackground->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE); + if (!mBackground) { + return PR_FALSE; + } + + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, mBackground) { + if (mBackground->mLayers[i].mImage.mRequest) + return PR_TRUE; + } + return PR_FALSE; } nsresult diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index 2e2bad0fc4e..9d040faada1 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -4114,7 +4114,9 @@ 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->mBackgroundImage || background->IsTransparent() || + if (background->BottomLayer().mImage.mRequest || + background->mImageCount > 1 || + NS_GET_A(background->mBackgroundColor) < 255 || PR_ABS(delta)*mRowHeight >= mRect.height) { Invalidate(); } else { @@ -4149,9 +4151,12 @@ nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, PRInt32 aPosition PRInt32 delta = aPosition - mHorzPosition; mHorzPosition = aPosition; - // See if we have a background image. If we do, then we cannot blit. + // See if we have a transparent background or a background image. + // If we do, then we cannot blit. const nsStyleBackground* background = GetStyleBackground(); - if (background->mBackgroundImage || background->IsTransparent() || + if (background->BottomLayer().mImage.mRequest || + background->mImageCount > 1 || + NS_GET_A(background->mBackgroundColor) < 255 || PR_ABS(delta) >= mRect.width) { Invalidate(); } else { diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h index 9dedac13805..6471a140375 100644 --- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -685,6 +685,20 @@ class nsTArray : public nsTArray_base { RemoveElementsAt(newLen, oldLen - newLen); } + // This method ensures that the array has length at least the given + // length. If the current length is shorter than the given length, + // then new elements will be constructed using elem_type's default + // constructor. + // @param minLen The desired minimum length of this array. + // @return True if the operation succeeded; false otherwise. + PRBool EnsureLengthAtLeast(size_type minLen) { + size_type oldLen = Length(); + if (minLen > oldLen) { + return InsertElementsAt(oldLen, minLen - oldLen) != nsnull; + } + return PR_TRUE; + } + // This method inserts elements into the array, constructing // them using elem_type's default constructor. // @param index the place to insert the new elements. This must be no