From 49c9b3d67e02717f3b333377dc8a73cd1dab0eec Mon Sep 17 00:00:00 2001 From: "Andrew Smith ext:(%2C%20Rob%20Arnold%20%3Ctellrob%40gmail.com%3E%2C%20L.%20David%20Baron%20%3Cdbaron%40dbaron.org%3E)" Date: Wed, 16 Jul 2008 23:30:25 -0700 Subject: [PATCH] Implement css3 border-image property. (Bug 378217) r=vlad,dbaron,robarnold --- .../html/content/src/nsGenericHTMLElement.cpp | 4 +- content/html/document/src/nsImageDocument.cpp | 2 +- dom/public/idl/css/nsIDOMCSS2Properties.idl | 6 +- gfx/thebes/public/gfxContext.h | 3 +- layout/base/nsCSSRendering.cpp | 428 +++++++++++++++++- layout/base/nsCSSRendering.h | 16 + layout/base/nsDisplayList.cpp | 8 +- layout/base/nsImageLoader.cpp | 14 +- layout/base/nsImageLoader.h | 4 +- layout/base/nsPresContext.cpp | 32 +- layout/base/nsPresContext.h | 27 +- layout/base/nsStyleConsts.h | 5 + layout/forms/nsButtonFrameRenderer.cpp | 4 +- layout/forms/nsFieldSetFrame.cpp | 2 +- layout/forms/nsHTMLButtonControlFrame.cpp | 2 +- layout/generic/nsAbsoluteContainingBlock.cpp | 2 +- layout/generic/nsBlockFrame.cpp | 6 +- layout/generic/nsContainerFrame.cpp | 4 +- layout/generic/nsFrame.cpp | 8 +- layout/generic/nsHTMLReflowState.cpp | 16 +- layout/generic/nsInlineFrame.cpp | 4 +- layout/generic/nsPageContentFrame.cpp | 2 +- .../reftests/border-image/10x5multicolor.png | Bin 0 -> 213 bytes .../reftests/border-image/3x3green-1DD813.png | Bin 0 -> 89 bytes .../reftests/border-image/3x3multicolor.png | Bin 0 -> 109 bytes .../reftests/border-image/3x3transparent.png | Bin 0 -> 69 bytes .../reftests/border-image/4x4multicolor.png | Bin 0 -> 118 bytes .../border-image/multicolor-image-1-ref.html | 37 ++ .../border-image/multicolor-image-1.html | 22 + .../border-image/multicolor-image-2-ref.html | 165 +++++++ .../border-image/multicolor-image-2.html | 96 ++++ .../border-image/multicolor-image-3-ref.html | 55 +++ .../border-image/multicolor-image-3.html | 56 +++ layout/reftests/border-image/reftest.list | 7 + .../border-image/repeat-image-1-ref.html | 57 +++ .../reftests/border-image/repeat-image-1.html | 31 ++ .../border-image/solid-image-1-ref.html | 11 + .../reftests/border-image/solid-image-1.html | 18 + .../border-image/solid-image-2-ref.html | 38 ++ .../reftests/border-image/solid-image-2.html | 40 ++ .../border-image/transparent-image-1-ref.html | 11 + .../border-image/transparent-image-1.html | 18 + .../pixel-rounding/border-image-width-0.html | 59 +++ .../pixel-rounding/border-image-width-10.html | 59 +++ .../pixel-rounding/border-image-width-4.html | 59 +++ .../pixel-rounding/border-image-width-9.html | 59 +++ .../reftests/pixel-rounding/random-10x10.png | Bin 0 -> 391 bytes layout/reftests/pixel-rounding/reftest.list | 4 + layout/reftests/reftest.list | 3 + layout/style/Makefile.in | 1 + layout/style/nsCSSDataBlock.cpp | 18 +- layout/style/nsCSSDeclaration.cpp | 8 + layout/style/nsCSSParser.cpp | 91 ++++ layout/style/nsCSSPropList.h | 1 + layout/style/nsCSSProps.cpp | 7 + layout/style/nsCSSProps.h | 1 + layout/style/nsCSSStruct.h | 1 + layout/style/nsComputedDOMStyle.cpp | 91 +++- layout/style/nsComputedDOMStyle.h | 1 + layout/style/nsRuleNode.cpp | 80 +++- layout/style/nsStyleContext.cpp | 8 +- layout/style/nsStyleStruct.cpp | 51 ++- layout/style/nsStyleStruct.h | 97 ++-- layout/style/nsStyleStructInlines.h | 70 +++ layout/style/test/property_database.js | 17 + layout/style/test/test_value_storage.html | 4 +- layout/tables/nsTableCellFrame.cpp | 2 +- layout/tables/nsTableFrame.cpp | 4 +- layout/xul/base/src/nsBox.cpp | 2 +- layout/xul/base/src/nsBoxObject.cpp | 8 +- layout/xul/base/src/nsGroupBoxFrame.cpp | 2 +- layout/xul/base/src/nsListBoxBodyFrame.cpp | 2 +- .../xul/base/src/tree/src/nsTreeBodyFrame.cpp | 2 +- 73 files changed, 1959 insertions(+), 114 deletions(-) create mode 100644 layout/reftests/border-image/10x5multicolor.png create mode 100644 layout/reftests/border-image/3x3green-1DD813.png create mode 100644 layout/reftests/border-image/3x3multicolor.png create mode 100644 layout/reftests/border-image/3x3transparent.png create mode 100644 layout/reftests/border-image/4x4multicolor.png create mode 100644 layout/reftests/border-image/multicolor-image-1-ref.html create mode 100644 layout/reftests/border-image/multicolor-image-1.html create mode 100644 layout/reftests/border-image/multicolor-image-2-ref.html create mode 100644 layout/reftests/border-image/multicolor-image-2.html create mode 100644 layout/reftests/border-image/multicolor-image-3-ref.html create mode 100644 layout/reftests/border-image/multicolor-image-3.html create mode 100644 layout/reftests/border-image/reftest.list create mode 100644 layout/reftests/border-image/repeat-image-1-ref.html create mode 100644 layout/reftests/border-image/repeat-image-1.html create mode 100644 layout/reftests/border-image/solid-image-1-ref.html create mode 100644 layout/reftests/border-image/solid-image-1.html create mode 100644 layout/reftests/border-image/solid-image-2-ref.html create mode 100644 layout/reftests/border-image/solid-image-2.html create mode 100644 layout/reftests/border-image/transparent-image-1-ref.html create mode 100644 layout/reftests/border-image/transparent-image-1.html create mode 100644 layout/reftests/pixel-rounding/border-image-width-0.html create mode 100644 layout/reftests/pixel-rounding/border-image-width-10.html create mode 100644 layout/reftests/pixel-rounding/border-image-width-4.html create mode 100644 layout/reftests/pixel-rounding/border-image-width-9.html create mode 100644 layout/reftests/pixel-rounding/random-10x10.png create mode 100644 layout/style/nsStyleStructInlines.h diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 3ebcc01f955..15730970884 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -583,8 +583,8 @@ nsGenericHTMLElement::GetOffsetRect(nsRect& aRect, nsIContent** aOffsetParent) if (parent && parent->GetStylePosition()->mBoxSizing != NS_STYLE_BOX_SIZING_BORDER) { const nsStyleBorder* border = parent->GetStyleBorder(); - origin.x -= border->GetBorderWidth(NS_SIDE_LEFT); - origin.y -= border->GetBorderWidth(NS_SIDE_TOP); + origin.x -= border->GetActualBorderWidth(NS_SIDE_LEFT); + origin.y -= border->GetActualBorderWidth(NS_SIDE_TOP); } // XXX We should really consider subtracting out padding for diff --git a/content/html/document/src/nsImageDocument.cpp b/content/html/document/src/nsImageDocument.cpp index d9560a4b9c0..a302f74585e 100644 --- a/content/html/document/src/nsImageDocument.cpp +++ b/content/html/document/src/nsImageDocument.cpp @@ -666,7 +666,7 @@ nsImageDocument::CheckOverflowing(PRBool changeState) nsMargin m; if (styleContext->GetStyleMargin()->GetMargin(m)) visibleArea.Deflate(m); - m = styleContext->GetStyleBorder()->GetBorder(); + m = styleContext->GetStyleBorder()->GetActualBorder(); visibleArea.Deflate(m); if (styleContext->GetStylePadding()->GetPadding(m)) visibleArea.Deflate(m); diff --git a/dom/public/idl/css/nsIDOMCSS2Properties.idl b/dom/public/idl/css/nsIDOMCSS2Properties.idl index 82723cb8563..f919c75fd18 100644 --- a/dom/public/idl/css/nsIDOMCSS2Properties.idl +++ b/dom/public/idl/css/nsIDOMCSS2Properties.idl @@ -406,7 +406,7 @@ interface nsIDOMCSS2Properties : nsISupports // raises(DOMException) on setting }; -[scriptable, uuid(816581b0-3d89-11dd-ae16-0800200c9a66)] +[scriptable, uuid(f1781ae4-00e6-4751-8698-2925f925fd76)] interface nsIDOMNSCSS2Properties : nsIDOMCSS2Properties { /* Non-DOM 2 extensions */ @@ -596,4 +596,8 @@ interface nsIDOMNSCSS2Properties : nsIDOMCSS2Properties attribute DOMString MozBoxShadow; // raises(DOMException) on setting + + attribute DOMString MozBorderImage; + // raises(DOMException) on setting + }; diff --git a/gfx/thebes/public/gfxContext.h b/gfx/thebes/public/gfxContext.h index 15387aaa0b7..c91e9fb3b2f 100644 --- a/gfx/thebes/public/gfxContext.h +++ b/gfx/thebes/public/gfxContext.h @@ -337,7 +337,8 @@ public: * Uses a surface for drawing. This is a shorthand for creating a * pattern and setting it. * - * @param offset ? + * @param offset from the source surface, to use only part of it. + * May need to make it negative. */ void SetSource(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0)); diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index ef82038fbdf..0242d0427ec 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -26,6 +26,7 @@ * Masayuki Nakano * L. David Baron , Mozilla Corporation * Michael Ventnor + * Rob Arnold * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -72,8 +73,11 @@ #include "nsLayoutUtils.h" #include "nsINameSpaceManager.h" #include "nsBlockFrame.h" - #include "gfxContext.h" +#include "nsIInterfaceRequestorUtils.h" +#include "gfxPlatform.h" +#include "gfxImageSurface.h" +#include "nsStyleStructInlines.h" #define BORDER_FULL 0 //entire side #define BORDER_INSIDE 1 //inside half @@ -2695,6 +2699,12 @@ nsCSSRendering::PaintBorder(nsPresContext* aPresContext, return; // Let the theme handle it. } + if (aBorderStyle.IsBorderImageLoaded()) { + DrawBorderImage(aPresContext, aRenderingContext, aForFrame, + aBorderArea, aBorderStyle, aHardBorderSize); + return; + } + // Get our style context's color struct. const nsStyleColor* ourColor = aStyleContext->GetStyleColor(); @@ -2706,7 +2716,7 @@ nsCSSRendering::PaintBorder(nsPresContext* aPresContext, if (aHardBorderSize > 0) { border.SizeTo(aHardBorderSize, aHardBorderSize, aHardBorderSize, aHardBorderSize); } else { - border = aBorderStyle.GetBorder(); + border = aBorderStyle.GetComputedBorder(); } if ((0 == border.left) && (0 == border.right) && @@ -3339,7 +3349,7 @@ nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext, nsRect frameRect; const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder(); - borderValues = styleBorder->GetBorder(); + borderValues = styleBorder->GetActualBorder(); sidesToSkip = aForFrame->GetSkipSides(); frameRect = nsRect(aForFramePt, aForFrame->GetSize()); @@ -3726,20 +3736,13 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, PRBool needBackgroundColor = !(aColor.mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT); PRIntn repeat = aColor.mBackgroundRepeat; - nscoord xDistance, yDistance; switch (repeat) { case NS_STYLE_BG_REPEAT_X: - xDistance = dirtyRect.width; - yDistance = tileHeight; break; case NS_STYLE_BG_REPEAT_Y: - xDistance = tileWidth; - yDistance = dirtyRect.height; break; case NS_STYLE_BG_REPEAT_XY: - xDistance = dirtyRect.width; - yDistance = dirtyRect.height; if (needBackgroundColor) { // If the image is completely opaque, we do not need to paint the // background color @@ -3764,8 +3767,6 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, case NS_STYLE_BG_REPEAT_OFF: default: NS_ASSERTION(repeat == NS_STYLE_BG_REPEAT_OFF, "unknown background-repeat value"); - xDistance = tileWidth; - yDistance = tileHeight; break; } @@ -3846,7 +3847,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // Take the border out of the frame's rect const nsStyleBorder* borderStyle = firstRootElementFrame->GetStyleBorder(); - firstRootElementFrameArea.Deflate(borderStyle->GetBorder()); + firstRootElementFrameArea.Deflate(borderStyle->GetActualBorder()); // Get the anchor point ComputeBackgroundAnchorPoint(aColor, firstRootElementFrameArea + @@ -3886,7 +3887,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, if (haveRadius) { gfxFloat radii[4]; - ComputePixelRadii(borderRadii, bgClipArea, aBorder.GetBorder(), + ComputePixelRadii(borderRadii, bgClipArea, aBorder.GetActualBorder(), aForFrame ? aForFrame->GetSkipSides() : 0, appUnitsPerPixel, radii); @@ -4061,6 +4062,401 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, } +void +nsCSSRendering::DrawBorderImage(nsPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aBorderArea, + const nsStyleBorder& aBorderStyle, + nscoord aHardBorderSize) +{ + float percent; + nsStyleCoord borderImageSplit[4]; + PRInt32 borderImageSplitInt[4]; + nsMargin border; + gfxFloat borderTop, borderRight, borderBottom, borderLeft; + gfxFloat borderImageSplitGfx[4]; + + if (aHardBorderSize > 0) { + border.SizeTo(aHardBorderSize, aHardBorderSize, aHardBorderSize, aHardBorderSize); + } else { + border = aBorderStyle.GetActualBorder(); + } + + if ((0 == border.left) && (0 == border.right) && + (0 == border.top) && (0 == border.bottom)) { + // Empty border area + return; + } + + borderImageSplit[NS_SIDE_TOP] = aBorderStyle.mBorderImageSplit.GetTop(); + borderImageSplit[NS_SIDE_RIGHT] = aBorderStyle.mBorderImageSplit.GetRight(); + borderImageSplit[NS_SIDE_BOTTOM] = aBorderStyle.mBorderImageSplit.GetBottom(); + borderImageSplit[NS_SIDE_LEFT] = aBorderStyle.mBorderImageSplit.GetLeft(); + + imgIRequest *req = aPresContext->LoadBorderImage(aBorderStyle.GetBorderImage(), aForFrame); + + nsCOMPtr image; + req->GetImage(getter_AddRefs(image)); + + nsSize imageSize; + image->GetWidth(&imageSize.width); + image->GetHeight(&imageSize.height); + imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width); + imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height); + + // convert percentage values + NS_FOR_CSS_SIDES(side) { + borderImageSplitInt[side] = 0; + switch (borderImageSplit[side].GetUnit()) { + case eStyleUnit_Percent: + percent = borderImageSplit[side].GetPercentValue(); + if (side == NS_SIDE_TOP || side == NS_SIDE_BOTTOM) + borderImageSplitInt[side] = (nscoord)(percent * imageSize.height); + else + borderImageSplitInt[side] = (nscoord)(percent * imageSize.width); + break; + case eStyleUnit_Integer: + borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side]. + GetIntValue()); + break; + case eStyleUnit_Factor: + borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].GetFactorValue()); + break; + default: + break; + } + } + + gfxContext *thebesCtx = aRenderingContext.ThebesContext(); + nsCOMPtr dc; + aRenderingContext.GetDeviceContext(*getter_AddRefs(dc)); + + NS_FOR_CSS_SIDES(side) { + borderImageSplitGfx[side] = nsPresContext::AppUnitsToFloatCSSPixels(borderImageSplitInt[side]); + } + + borderTop = dc->AppUnitsToGfxUnits(border.top); + borderRight = dc->AppUnitsToGfxUnits(border.right); + borderBottom = dc->AppUnitsToGfxUnits(border.bottom); + borderLeft = dc->AppUnitsToGfxUnits(border.left); + + gfxSize gfxImageSize; + gfxImageSize.width = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.width); + gfxImageSize.height = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.height); + + nsRect outerRect(aBorderArea); + gfxRect rectToDraw, + rectToDrawSource; + + gfxRect clipRect; + clipRect.pos.x = dc->AppUnitsToGfxUnits(outerRect.x); + clipRect.pos.y = dc->AppUnitsToGfxUnits(outerRect.y); + clipRect.size.width = dc->AppUnitsToGfxUnits(outerRect.width); + clipRect.size.height = dc->AppUnitsToGfxUnits(outerRect.height); + thebesCtx->UserToDevicePixelSnapped(clipRect); + + thebesCtx->Save(); + thebesCtx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); + + gfxSize middleSize(clipRect.size.width - (borderLeft + borderRight), + clipRect.size.height - (borderTop + borderBottom)); + + // middle size in source space + gfxIntSize middleSizeSource(gfxImageSize.width - (borderImageSplitGfx[NS_SIDE_RIGHT] + borderImageSplitGfx[NS_SIDE_LEFT]), + gfxImageSize.height - (borderImageSplitGfx[NS_SIDE_TOP] + borderImageSplitGfx[NS_SIDE_BOTTOM])); + + gfxSize interSizeTop, interSizeBottom, interSizeLeft, interSizeRight, + interSizeMiddle; + gfxFloat topScale = borderTop/borderImageSplitGfx[NS_SIDE_TOP]; + gfxFloat bottomScale = borderBottom/borderImageSplitGfx[NS_SIDE_BOTTOM]; + gfxFloat leftScale = borderLeft/borderImageSplitGfx[NS_SIDE_LEFT]; + gfxFloat rightScale = borderRight/borderImageSplitGfx[NS_SIDE_RIGHT]; + gfxFloat middleScaleH, + middleScaleV; + // TODO: check for nan and properly check for inf + if (topScale != 0.0 && borderImageSplitGfx[NS_SIDE_TOP] != 0.0) { + middleScaleH = topScale; + } else if (bottomScale != 0.0 && borderImageSplitGfx[NS_SIDE_BOTTOM] != 0.0) { + middleScaleH = bottomScale; + } else { + middleScaleH = 1.0; + } + + if (leftScale != 0.0 && borderImageSplitGfx[NS_SIDE_LEFT] != 0.0) { + middleScaleV = leftScale; + } else if (rightScale != 0.0 && borderImageSplitGfx[NS_SIDE_RIGHT] != 0.0) { + middleScaleV = rightScale; + } else { + middleScaleV = 1.0; + } + + interSizeTop.height = borderTop; + interSizeTop.width = middleSizeSource.width*topScale; + + interSizeBottom.height = borderBottom; + interSizeBottom.width = middleSizeSource.width*bottomScale; + + interSizeLeft.width = borderLeft; + interSizeLeft.height = middleSizeSource.height*leftScale; + + interSizeRight.width = borderRight; + interSizeRight.height = middleSizeSource.height*rightScale; + + interSizeMiddle.width = middleSizeSource.width*middleScaleH; + interSizeMiddle.height = middleSizeSource.height*middleScaleV; + + // draw top left corner + rectToDraw = clipRect; + rectToDraw.size.width = borderLeft; + rectToDraw.size.height = borderTop; + rectToDrawSource.pos.x = 0; + rectToDrawSource.pos.y = 0; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, rectToDraw.size, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw top + rectToDraw = clipRect; + rectToDraw.pos.x += borderLeft; + rectToDraw.size.width = middleSize.width; + rectToDraw.size.height = borderTop; + rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.pos.y = 0; + rectToDrawSource.size.width = middleSizeSource.width; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, interSizeTop, rectToDrawSource, + aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw top right corner + rectToDraw = clipRect; + rectToDraw.pos.x += clipRect.size.width - borderRight; + rectToDraw.size.width = borderRight; + rectToDraw.size.height = borderTop; + rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.pos.y = 0; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, rectToDraw.size, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw right + rectToDraw = clipRect; + rectToDraw.pos.x += clipRect.size.width - borderRight; + rectToDraw.pos.y += borderTop; + rectToDraw.size.width = borderRight; + rectToDraw.size.height = middleSize.height; + rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP]; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.size.height = middleSizeSource.height; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, interSizeRight, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill); + + // draw bottom right corner + rectToDraw = clipRect; + rectToDraw.pos.x += clipRect.size.width - borderRight; + rectToDraw.pos.y += clipRect.size.height - borderBottom; + rectToDraw.size.width = borderRight; + rectToDraw.size.height = borderBottom; + rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM]; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT]; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, rectToDraw.size, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw bottom + rectToDraw = clipRect; + rectToDraw.pos.x += borderLeft; + rectToDraw.pos.y += clipRect.size.height - borderBottom; + rectToDraw.size.width = middleSize.width; + rectToDraw.size.height = borderBottom; + rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM]; + rectToDrawSource.size.width = middleSizeSource.width; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, interSizeBottom, rectToDrawSource, + aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw bottom left corner + rectToDraw = clipRect; + rectToDraw.pos.y += clipRect.size.height - borderBottom; + rectToDraw.size.width = borderLeft; + rectToDraw.size.height = borderBottom; + rectToDrawSource.pos.x = 0; + rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM]; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM]; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, rectToDraw.size, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH); + + // draw left + rectToDraw = clipRect; + rectToDraw.pos.y += borderTop; + rectToDraw.size.width = borderLeft; + rectToDraw.size.height = middleSize.height; + rectToDrawSource.pos.x = 0; + rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP]; + rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.size.height = middleSizeSource.height; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, interSizeLeft, rectToDrawSource, + NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill); + + // Draw middle + rectToDraw = clipRect; + rectToDraw.pos.x += borderLeft; + rectToDraw.pos.y += borderTop; + rectToDraw.size.width = middleSize.width; + rectToDraw.size.height = middleSize.height; + rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT]; + rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP]; + rectToDrawSource.size = middleSizeSource; + DrawBorderImageSide(thebesCtx, dc, image, + rectToDraw, interSizeMiddle, rectToDrawSource, + aBorderStyle.mBorderImageHFill, aBorderStyle.mBorderImageVFill); + + thebesCtx->PopGroupToSource(); + thebesCtx->SetOperator(gfxContext::OPERATOR_OVER); + thebesCtx->Paint(); + thebesCtx->Restore(); +} + +void +nsCSSRendering::DrawBorderImageSide(gfxContext *aThebesContext, + nsIDeviceContext* aDeviceContext, + imgIContainer* aImage, + gfxRect& aDestRect, + gfxSize& aInterSize, + gfxRect& aSourceRect, + PRUint8 aHFillType, + PRUint8 aVFillType) +{ + if (aDestRect.size.width < 1.0 || aDestRect.size.height < 1.0 || + aSourceRect.size.width < 1.0 || aSourceRect.size.height < 1.0) { + return; + } + + gfxIntSize gfxSourceSize((PRInt32)aSourceRect.size.width, + (PRInt32)aSourceRect.size.height); + + // where the actual border ends up being rendered + aThebesContext->UserToDevicePixelSnapped(aDestRect); + aThebesContext->UserToDevicePixelSnapped(aSourceRect); + + if (aDestRect.size.height < 1.0 || + aDestRect.size.width < 1.0) + return; + + if (aInterSize.width < 1.0 || + aInterSize.height < 1.0) + return; + + // Surface will hold just the part of the source image specified by the aSourceRect + // but at a different size + nsRefPtr interSurface = + gfxPlatform::GetPlatform()->CreateOffscreenSurface( + gfxSourceSize, gfxASurface::ImageFormatARGB32); + + gfxMatrix srcMatrix; + // Adjust the matrix scale for Step 1 of the spec + srcMatrix.Scale(aSourceRect.size.width/aInterSize.width, + aSourceRect.size.height/aInterSize.height); + { + nsCOMPtr frame; + nsresult rv = aImage->GetCurrentFrame(getter_AddRefs(frame)); + if(NS_FAILED(rv)) + return; + nsCOMPtr image; + image = do_GetInterface(frame); + if(!image) + return; + + // surface for the whole image + nsRefPtr imagePattern; + rv = image->GetPattern(getter_AddRefs(imagePattern)); + if(NS_FAILED(rv) || !imagePattern) + return; + + gfxMatrix mat; + mat.Translate(aSourceRect.pos); + imagePattern->SetMatrix(mat); + + // Straightforward blit - no resizing + nsRefPtr srcCtx = new gfxContext(interSurface); + srcCtx->SetPattern(imagePattern); + srcCtx->SetOperator(gfxContext::OPERATOR_SOURCE); + srcCtx->Paint(); + srcCtx = nsnull; + + } + + // offset to make the middle tile centered in the middle of the border + gfxPoint renderOffset(0, 0); + gfxSize rectSize(aDestRect.size); + + aThebesContext->Save(); + aThebesContext->Clip(aDestRect); + + gfxFloat hScale(1.0), vScale(1.0); + + nsRefPtr pattern = new gfxPattern(interSurface); + pattern->SetExtend(gfxPattern::EXTEND_PAD); + switch (aHFillType) { + case NS_STYLE_BORDER_IMAGE_REPEAT: + renderOffset.x = (rectSize.width - aInterSize.width*NS_ceil(rectSize.width/aInterSize.width))*-0.5; + aDestRect.pos.x -= renderOffset.x; + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + break; + case NS_STYLE_BORDER_IMAGE_ROUND: + hScale = aInterSize.width*(NS_ceil(aDestRect.size.width/aInterSize.width)/aDestRect.size.width); + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + break; + case NS_STYLE_BORDER_IMAGE_STRETCH: + default: + hScale = aInterSize.width/aDestRect.size.width; + break; + } + + switch (aVFillType) { + case NS_STYLE_BORDER_IMAGE_REPEAT: + renderOffset.y = (rectSize.height - aInterSize.height*NS_ceil(rectSize.height/aInterSize.height))*-0.5; + aDestRect.pos.y -= renderOffset.y; + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + break; + case NS_STYLE_BORDER_IMAGE_ROUND: + vScale = aInterSize.height*(NS_ceil(aDestRect.size.height/aInterSize.height)/aDestRect.size.height); + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + break; + case NS_STYLE_BORDER_IMAGE_STRETCH: + default: + vScale = aInterSize.height/aDestRect.size.height; + break; + } + + // Adjust the matrix scale for Step 2 of the spec + srcMatrix.Scale(hScale,vScale); + pattern->SetMatrix(srcMatrix); + + // render + aThebesContext->Translate(aDestRect.pos); + aThebesContext->SetPattern(pattern); + aThebesContext->NewPath(); + aThebesContext->Rectangle(gfxRect(renderOffset, rectSize)); + aThebesContext->SetOperator(gfxContext::OPERATOR_ADD); + aThebesContext->Fill(); + aThebesContext->Restore(); +} + void nsCSSRendering::PaintBackgroundColor(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -4140,7 +4536,7 @@ nsCSSRendering::PaintRoundedBackground(nsPresContext* aPresContext, // Get the radius to the outer edge of the padding. // -moz-border-radius is the radius to the outer edge of the border. NS_FOR_CSS_SIDES(side) { - aTheRadius[side] -= aBorder.GetBorderWidth(side); + aTheRadius[side] -= aBorder.GetActualBorderWidth(side); aTheRadius[side] = PR_MAX(aTheRadius[side], 0); } } @@ -4154,7 +4550,7 @@ nsCSSRendering::PaintRoundedBackground(nsPresContext* aPresContext, // convert the radii gfxFloat radii[4]; - nsMargin border = aBorder.GetBorder(); + nsMargin border = aBorder.GetActualBorder(); ComputePixelRadii(aTheRadius, aBgClipArea, border, aForFrame ? aForFrame->GetSkipSides() : 0, diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index cad7450ecc2..9ff92577e44 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -273,6 +273,22 @@ public: protected: + static void DrawBorderImage(nsPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + nsIFrame* aForFrame, + const nsRect& aBorderArea, + const nsStyleBorder& aBorderStyle, + nscoord aHardBorderSize); + + static void DrawBorderImageSide(gfxContext *aThebesContext, + nsIDeviceContext* aDeviceContext, + imgIContainer* aImage, + gfxRect& aDestRect, + gfxSize& aInterSize, + gfxRect& aSourceRect, + PRUint8 aHFillType, + PRUint8 aVFillType); + static void PaintBackgroundColor(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIFrame* aForFrame, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index cebd60da3a8..0671e8544dd 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -50,6 +50,7 @@ #include "nsRegion.h" #include "nsFrameManager.h" #include "gfxContext.h" +#include "nsStyleStructInlines.h" nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, PRBool aIsForEvents, PRBool aBuildCaret) @@ -608,11 +609,16 @@ nsDisplayBorder::OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() + aBuilder->ToReferenceFrame(mFrame); + const nsStyleBorder *styleBorder; if (paddingRect.Contains(aVisibleRegion->GetBounds()) && - !nsLayoutUtils::HasNonZeroSide(mFrame->GetStyleBorder()->mBorderRadius)) { + !(styleBorder = mFrame->GetStyleBorder())->IsBorderImageLoaded() && + !nsLayoutUtils::HasNonZeroSide(styleBorder->mBorderRadius)) { // the visible region is entirely inside the content rect, and no part // of the border is rendered inside the content rect, so we are not // visible + // Skip this if there's a border-image (which draws a background + // too) or if there is a border-radius (which makes the border draw + // further in). return PR_FALSE; } diff --git a/layout/base/nsImageLoader.cpp b/layout/base/nsImageLoader.cpp index 36b542d2f56..4b33d6ae654 100644 --- a/layout/base/nsImageLoader.cpp +++ b/layout/base/nsImageLoader.cpp @@ -80,10 +80,12 @@ nsImageLoader::~nsImageLoader() void -nsImageLoader::Init(nsIFrame *aFrame, nsPresContext *aPresContext) +nsImageLoader::Init(nsIFrame *aFrame, nsPresContext *aPresContext, + PRBool aReflowOnLoad) { mFrame = aFrame; mPresContext = aPresContext; + mReflowOnLoad = aReflowOnLoad; } void @@ -208,6 +210,16 @@ NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer, void nsImageLoader::RedrawDirtyFrame(const nsRect* aDamageRect) { + if (mReflowOnLoad) { + nsIPresShell *shell = mPresContext->GetPresShell(); + nsresult rv = shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not reflow after loading border-image"); + // Note that we're assuming that the resulting reflow will + // invalidate the entire frame. Given that we only set + // mReflowOnLoad if the actual border width will change when we do + // this reflow, this invalidate should happen. + return; + } // NOTE: It is not sufficient to invalidate only the size of the image: // the image may be tiled! // The best option is to call into the frame, however lacking this diff --git a/layout/base/nsImageLoader.h b/layout/base/nsImageLoader.h index 56c5b278dc7..578ea40a56f 100644 --- a/layout/base/nsImageLoader.h +++ b/layout/base/nsImageLoader.h @@ -69,7 +69,8 @@ public: NS_IMETHOD FrameChanged(imgIContainer *aContainer, gfxIImageFrame *newframe, nsRect * dirtyRect); - void Init(nsIFrame *aFrame, nsPresContext *aPresContext); + void Init(nsIFrame *aFrame, nsPresContext *aPresContext, + PRBool aReflowOnLoad); nsresult Load(imgIRequest *aImage); void Destroy(); @@ -84,4 +85,5 @@ private: nsIFrame *mFrame; nsPresContext *mPresContext; nsCOMPtr mRequest; + PRBool mReflowOnLoad; }; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 31962737ae4..5b46b59915d 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -801,6 +801,9 @@ nsPresContext::Init(nsIDeviceContext* aDeviceContext) if (!mImageLoaders.Init()) return NS_ERROR_OUT_OF_MEMORY; + if (!mBorderImageLoaders.Init()) + return NS_ERROR_OUT_OF_MEMORY; + // Get the look and feel service here; default colors will be initialized // from calling GetUserPreferences() when we get a presshell. nsresult rv = CallGetService(kLookAndFeelCID, &mLookAndFeel); @@ -1168,18 +1171,21 @@ nsPresContext::SetFullZoom(float aZoom) } imgIRequest* -nsPresContext::LoadImage(imgIRequest* aImage, nsIFrame* aTargetFrame) +nsPresContext::DoLoadImage(nsPresContext::ImageLoaderTable& aTable, + imgIRequest* aImage, + nsIFrame* aTargetFrame, + PRBool aReflowOnLoad) { // look and see if we have a loader for the target frame. nsCOMPtr loader; - mImageLoaders.Get(aTargetFrame, getter_AddRefs(loader)); + aTable.Get(aTargetFrame, getter_AddRefs(loader)); if (!loader) { loader = new nsImageLoader(); if (!loader) return nsnull; - loader->Init(aTargetFrame, this); + loader->Init(aTargetFrame, this, aReflowOnLoad); mImageLoaders.Put(aTargetFrame, loader); } @@ -1190,6 +1196,18 @@ nsPresContext::LoadImage(imgIRequest* aImage, nsIFrame* aTargetFrame) return request; } +imgIRequest* +nsPresContext::LoadImage(imgIRequest* aImage, nsIFrame* aTargetFrame) +{ + return DoLoadImage(mImageLoaders, aImage, aTargetFrame, PR_FALSE); +} + +imgIRequest* +nsPresContext::LoadBorderImage(imgIRequest* aImage, nsIFrame* aTargetFrame) +{ + return DoLoadImage(mBorderImageLoaders, aImage, aTargetFrame, + aTargetFrame->GetStyleBorder()->ImageBorderDiffers()); +} void nsPresContext::StopImagesFor(nsIFrame* aTargetFrame) @@ -1202,6 +1220,14 @@ nsPresContext::StopImagesFor(nsIFrame* aTargetFrame) mImageLoaders.Remove(aTargetFrame); } + + mBorderImageLoaders.Get(aTargetFrame, getter_AddRefs(loader)); + + if (loader) { + loader->Destroy(); + + mBorderImageLoaders.Remove(aTargetFrame); + } } diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index ea1ca3d811d..e9da85007b4 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -364,14 +364,28 @@ public: /** - * Load an image for the target frame. This call can be made - * repeated with only a single image ever being loaded. When the - * image's data is ready for rendering the target frame's Paint() - * method will be invoked (via the ViewManager) so that the - * appropriate damage repair is done. + * Set up observers so that aTargetFrame will be invalidated when + * aImage loads, where aImage is its background image. Only a single + * image will be tracked per frame. */ NS_HIDDEN_(imgIRequest*) LoadImage(imgIRequest* aImage, nsIFrame* aTargetFrame); + /** + * Set up observers so that aTargetFrame will be invalidated or + * reflowed (as appropriate) when aImage loads, where aImage is its + * *border* image. Only a single image will be tracked per frame. + */ + NS_HIDDEN_(imgIRequest*) LoadBorderImage(imgIRequest* aImage, + nsIFrame* aTargetFrame); + +private: + typedef nsInterfaceHashtable ImageLoaderTable; + + NS_HIDDEN_(imgIRequest*) DoLoadImage(ImageLoaderTable& aTable, + imgIRequest* aImage, + nsIFrame* aTargetFrame, + PRBool aReflowOnLoad); +public: /** * This method is called when a frame is being destroyed to @@ -800,7 +814,8 @@ protected: nsILinkHandler* mLinkHandler; // [WEAK] nsIAtom* mLangGroup; // [STRONG] - nsInterfaceHashtable mImageLoaders; + ImageLoaderTable mImageLoaders; + ImageLoaderTable mBorderImageLoaders; nsWeakPtr mContainer; float mTextZoom; // Text zoom, defaults to 1.0 diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h index 10f42e0c8a0..2d23f10fab7 100644 --- a/layout/base/nsStyleConsts.h +++ b/layout/base/nsStyleConsts.h @@ -271,6 +271,11 @@ // derived from a table with its rules attribute set #define NS_STYLE_BORDER_STYLE_RULES_MARKER 0x10 +// See nsStyleBorder mBorderImage +#define NS_STYLE_BORDER_IMAGE_STRETCH 0 +#define NS_STYLE_BORDER_IMAGE_REPEAT 1 +#define NS_STYLE_BORDER_IMAGE_ROUND 2 + // See nsStyleDisplay #define NS_STYLE_CLEAR_NONE 0 #define NS_STYLE_CLEAR_LEFT 1 diff --git a/layout/forms/nsButtonFrameRenderer.cpp b/layout/forms/nsButtonFrameRenderer.cpp index 62014229838..ca2f6d61bc9 100644 --- a/layout/forms/nsButtonFrameRenderer.cpp +++ b/layout/forms/nsButtonFrameRenderer.cpp @@ -263,7 +263,7 @@ nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding() if (!mOuterFocusStyle->GetStylePadding()->GetPadding(result)) { NS_NOTYETIMPLEMENTED("percentage padding"); } - result += mOuterFocusStyle->GetStyleBorder()->GetBorder(); + result += mOuterFocusStyle->GetStyleBorder()->GetActualBorder(); } return result; @@ -302,7 +302,7 @@ nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding() if (!mInnerFocusStyle->GetStylePadding()->GetPadding(result)) { NS_NOTYETIMPLEMENTED("percentage padding"); } - result += mInnerFocusStyle->GetStyleBorder()->GetBorder(); + result += mInnerFocusStyle->GetStyleBorder()->GetActualBorder(); } return result; diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 55a966efde5..277a66de71c 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -262,7 +262,7 @@ nsFieldSetFrame::PaintBorderBackground(nsIRenderingContext& aRenderingContext, const nsStyleBorder* borderStyle = GetStyleBorder(); const nsStylePadding* paddingStyle = GetStylePadding(); - nscoord topBorder = borderStyle->GetBorderWidth(NS_SIDE_TOP); + nscoord topBorder = borderStyle->GetActualBorderWidth(NS_SIDE_TOP); nscoord yoff = 0; nsPresContext* presContext = PresContext(); diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index 585a44268f3..1c2cd7f03cb 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -226,7 +226,7 @@ nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // but the real problem is the FirstChild (the AreaFrame) // isn't being constrained properly // Bug #17474 - nsMargin border = GetStyleBorder()->GetBorder(); + nsMargin border = GetStyleBorder()->GetActualBorder(); nsRect rect(aBuilder->ToReferenceFrame(this), GetSize()); rect.Deflate(border); diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 03128314d9c..12dc5f7c640 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -395,7 +395,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat nsresult rv; // Get the border values - const nsMargin& border = aReflowState.mStyleBorder->GetBorder(); + const nsMargin& border = aReflowState.mStyleBorder->GetActualBorder(); nscoord availWidth = aContainingBlockWidth; if (availWidth == -1) { diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index c32d1a8b39c..b679c48bf4e 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -796,7 +796,7 @@ CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState, nsSize cbSize(aFrameSize); // Containing block is relative to the padding edge - const nsMargin& border = aReflowState.mStyleBorder->GetBorder(); + const nsMargin& border = aReflowState.mStyleBorder->GetActualBorder(); cbSize.width -= border.left + border.right; cbSize.height -= border.top + border.bottom; @@ -2650,8 +2650,8 @@ nsBlockFrame::IsSelfEmpty() const nsStyleBorder* border = GetStyleBorder(); const nsStylePadding* padding = GetStylePadding(); - if (border->GetBorderWidth(NS_SIDE_TOP) != 0 || - border->GetBorderWidth(NS_SIDE_BOTTOM) != 0 || + if (border->GetActualBorderWidth(NS_SIDE_TOP) != 0 || + border->GetActualBorderWidth(NS_SIDE_BOTTOM) != 0 || !IsPaddingZero(padding->mPadding.GetTopUnit(), padding->mPadding.GetTop()) || !IsPaddingZero(padding->mPadding.GetBottomUnit(), diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index edc4d9af363..6a4b3f17093 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -666,7 +666,7 @@ nsContainerFrame::DoInlineIntrinsicWidth(nsIRenderingContext *aRenderingContext, if (!GetPrevContinuation()) { aData->currentLine += GetCoord(stylePadding->mPadding.Get(startSide), 0) + - styleBorder->GetBorderWidth(startSide) + + styleBorder->GetActualBorderWidth(startSide) + GetCoord(styleMargin->mMargin.Get(startSide), 0); } @@ -703,7 +703,7 @@ nsContainerFrame::DoInlineIntrinsicWidth(nsIRenderingContext *aRenderingContext, if (!lastInFlow->GetNextContinuation()) { aData->currentLine += GetCoord(stylePadding->mPadding.Get(endSide), 0) + - styleBorder->GetBorderWidth(endSide) + + styleBorder->GetActualBorderWidth(endSide) + GetCoord(styleMargin->mMargin.Get(endSide), 0); } } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 5c8201629fa..10941d08781 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -586,7 +586,7 @@ nsIFrame::GetUsedBorder() const return result; } - return GetStyleBorder()->GetBorder(); + return GetStyleBorder()->GetActualBorder(); } /* virtual */ nsMargin @@ -2989,8 +2989,8 @@ nsFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext) &result.hPadding, &result.hPctPadding); const nsStyleBorder *styleBorder = GetStyleBorder(); - result.hBorder += styleBorder->GetBorderWidth(NS_SIDE_LEFT); - result.hBorder += styleBorder->GetBorderWidth(NS_SIDE_RIGHT); + result.hBorder += styleBorder->GetActualBorderWidth(NS_SIDE_LEFT); + result.hBorder += styleBorder->GetActualBorderWidth(NS_SIDE_RIGHT); const nsStyleDisplay *disp = GetStyleDisplay(); if (IsThemed(disp)) { @@ -3781,7 +3781,7 @@ nsFrame::CheckInvalidateSizeChange(nsPresContext* aPresContext, // may be moving. const nsStyleBorder* border = GetStyleBorder(); NS_FOR_CSS_SIDES(side) { - if (border->GetBorderWidth(side) != 0) { + if (border->GetActualBorderWidth(side) != 0) { Invalidate(nsRect(0, 0, mRect.width, mRect.height)); return; } diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index fdff0ea178f..3e1c6282c86 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -57,6 +57,7 @@ #include "nsIPercentHeightObserver.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" +#include "nsStyleStructInlines.h" #ifdef IBMBIDI #include "nsBidiUtils.h" #endif @@ -308,6 +309,17 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, InitResizeFlags(aPresContext); + // We have to start loading the border image now, because the + // border-image's width overrides only apply once the image is loaded. + // Starting the load of the image means we'll get a reflow when the + // image loads. (If we didn't do it now, and the image loaded between + // reflow and paint, we'd never get the notification, and our size + // would be wrong.) + imgIRequest *borderImage = mStyleBorder->GetBorderImage(); + if (borderImage) { + aPresContext->LoadBorderImage(borderImage, frame); + } + NS_ASSERTION((mFrameType == NS_CSS_FRAME_TYPE_INLINE && !frame->IsFrameOfType(nsIFrame::eReplaced)) || frame->GetType() == nsGkAtoms::textFrame || @@ -799,7 +811,7 @@ nsHTMLReflowState::CalculateHorizBorderPaddingMargin( nscoord* aInsideBoxSizing, nscoord* aOutsideBoxSizing) { - const nsMargin& border = mStyleBorder->GetBorder(); + const nsMargin& border = mStyleBorder->GetActualBorder(); nsMargin padding, margin; // See if the style system can provide us the padding directly @@ -1891,7 +1903,7 @@ nsCSSOffsetState::InitOffsets(nscoord aContainingBlockWidth, mComputedBorderPadding = *aBorder; } else { - mComputedBorderPadding = frame->GetStyleBorder()->GetBorder(); + mComputedBorderPadding = frame->GetStyleBorder()->GetActualBorder(); } mComputedBorderPadding += mComputedPadding; diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index ad266e62ff7..cde57affc3d 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -133,8 +133,8 @@ nsInlineFrame::IsSelfEmpty() // XXX Top and bottom removed, since they shouldn't affect things, but this // doesn't really match with nsLineLayout.cpp's setting of // ZeroEffectiveSpanBox, anymore, so what should this really be? - if (border->GetBorderWidth(NS_SIDE_RIGHT) != 0 || - border->GetBorderWidth(NS_SIDE_LEFT) != 0 || + if (border->GetActualBorderWidth(NS_SIDE_RIGHT) != 0 || + border->GetActualBorderWidth(NS_SIDE_LEFT) != 0 || !IsPaddingZero(padding->mPadding.GetRightUnit(), padding->mPadding.GetRight()) || !IsPaddingZero(padding->mPadding.GetLeftUnit(), diff --git a/layout/generic/nsPageContentFrame.cpp b/layout/generic/nsPageContentFrame.cpp index 39f637fb598..0a1a6ce49e3 100644 --- a/layout/generic/nsPageContentFrame.cpp +++ b/layout/generic/nsPageContentFrame.cpp @@ -206,7 +206,7 @@ nsPageContentFrame::Reflow(nsPresContext* aPresContext, if (aDesiredSize.mOverflowArea.XMost() > aDesiredSize.width) { mPD->mPageContentXMost = aDesiredSize.mOverflowArea.XMost() + - kidReflowState.mStyleBorder->GetBorderWidth(NS_SIDE_RIGHT) + + kidReflowState.mStyleBorder->GetActualBorderWidth(NS_SIDE_RIGHT) + padding.right; } } diff --git a/layout/reftests/border-image/10x5multicolor.png b/layout/reftests/border-image/10x5multicolor.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d888a32188f39fa19befdbd660fc366097f9a2 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^AT}!p6OjDO_R1beF%}28J29*~C-V}>;VkfoEM{Qf z76xHPhFNnYfP(BLp1!W^H&}(mjQNjrJ!t?6NtU=qlsM<-=BDPAFgO>bCYGe8D3oWG zWGJ|M`UZqI@`(c#IeEG`hHzX@p0me%S|bQJW;!x43+?!*)(YfwGU_M1tgewebjc>U xLuccbj!urdq0@gVtTB+SOzE^0RF{yDU?^O literal 0 HcmV?d00001 diff --git a/layout/reftests/border-image/3x3green-1DD813.png b/layout/reftests/border-image/3x3green-1DD813.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7a3cf21f5a2298a16cb7ce0e132bb4fa4ebf4e GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~3Z5>GAsp9}r|ebyGoO)bh6E7E iW$#H%NdN+uX^af5@oYR+=B!>owG5uFelF{r5}E)hoD_)w literal 0 HcmV?d00001 diff --git a/layout/reftests/border-image/3x3multicolor.png b/layout/reftests/border-image/3x3multicolor.png new file mode 100644 index 0000000000000000000000000000000000000000..9be62f07a0f10aae43601e5d239a753fec9f5316 GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1SD^IDZKzvjKx9jP7LeL$-D$|)ID7sLpZJ{ z=ltLBbi>oNzf^pWadx-pT5js=zMu5)?yhwgZ2p}JlxH|2$1C<{t$8U>3xlVtpUXO@ GgeCx4RwOY1 literal 0 HcmV?d00001 diff --git a/layout/reftests/border-image/3x3transparent.png b/layout/reftests/border-image/3x3transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..6528a9f036834a59300848246ca8442de86e1a21 GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~Jf1F&Asp9}6BIzo)EOA1Q~16D OSqz@8elF{r5}E*Z`3!mh literal 0 HcmV?d00001 diff --git a/layout/reftests/border-image/4x4multicolor.png b/layout/reftests/border-image/4x4multicolor.png new file mode 100644 index 0000000000000000000000000000000000000000..c2518c3e10c601aa5d0063014ad6aef6e0c230a3 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1SHiab7}%9&H|6fVg?4j!ywFfJby(BP>``W z$lZxy-8q?;K#sJhi(`mHc=C_)2M#bCQ(!v56cixl!lTu{hKFI%eEyFe`ip)8l`(j_ L`njxgN@xNAnPDC! literal 0 HcmV?d00001 diff --git a/layout/reftests/border-image/multicolor-image-1-ref.html b/layout/reftests/border-image/multicolor-image-1-ref.html new file mode 100644 index 00000000000..6f204e7d784 --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-1-ref.html @@ -0,0 +1,37 @@ + + + + test of -moz-border-image + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/layout/reftests/border-image/multicolor-image-1.html b/layout/reftests/border-image/multicolor-image-1.html new file mode 100644 index 00000000000..b48015776db --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-1.html @@ -0,0 +1,22 @@ + + + + test of -moz-border-image + + + + + +
+ + diff --git a/layout/reftests/border-image/multicolor-image-2-ref.html b/layout/reftests/border-image/multicolor-image-2-ref.html new file mode 100644 index 00000000000..e5ff9c66901 --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-2-ref.html @@ -0,0 +1,165 @@ + + + + test of -moz-border-image + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/layout/reftests/border-image/multicolor-image-2.html b/layout/reftests/border-image/multicolor-image-2.html new file mode 100644 index 00000000000..8212ae3daf3 --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-2.html @@ -0,0 +1,96 @@ + + + + test of -moz-border-image + + + + + +
+
+
+
+
+
+
+ + diff --git a/layout/reftests/border-image/multicolor-image-3-ref.html b/layout/reftests/border-image/multicolor-image-3-ref.html new file mode 100644 index 00000000000..a20c3bc83a1 --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-3-ref.html @@ -0,0 +1,55 @@ + + + + test of -moz-border-image: number repetition + + + + + +
+
+
+
+ + diff --git a/layout/reftests/border-image/multicolor-image-3.html b/layout/reftests/border-image/multicolor-image-3.html new file mode 100644 index 00000000000..43b52b69727 --- /dev/null +++ b/layout/reftests/border-image/multicolor-image-3.html @@ -0,0 +1,56 @@ + + + + test of -moz-border-image: number repetition + + + + + +
+
+
+
+ + diff --git a/layout/reftests/border-image/reftest.list b/layout/reftests/border-image/reftest.list new file mode 100644 index 00000000000..5fe7b3df0d0 --- /dev/null +++ b/layout/reftests/border-image/reftest.list @@ -0,0 +1,7 @@ +== solid-image-1.html solid-image-1-ref.html +== transparent-image-1.html transparent-image-1-ref.html +== solid-image-2.html solid-image-2-ref.html +== multicolor-image-1.html multicolor-image-1-ref.html +== multicolor-image-2.html multicolor-image-2-ref.html +== multicolor-image-3.html multicolor-image-3-ref.html +!= repeat-image-1.html repeat-image-1-ref.html diff --git a/layout/reftests/border-image/repeat-image-1-ref.html b/layout/reftests/border-image/repeat-image-1-ref.html new file mode 100644 index 00000000000..a03ed1690e9 --- /dev/null +++ b/layout/reftests/border-image/repeat-image-1-ref.html @@ -0,0 +1,57 @@ + + + + test of -moz-border-image + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/layout/reftests/border-image/repeat-image-1.html b/layout/reftests/border-image/repeat-image-1.html new file mode 100644 index 00000000000..805e4742084 --- /dev/null +++ b/layout/reftests/border-image/repeat-image-1.html @@ -0,0 +1,31 @@ + + + + test of -moz-border-image + + + + + +
+ + + diff --git a/layout/reftests/border-image/solid-image-1-ref.html b/layout/reftests/border-image/solid-image-1-ref.html new file mode 100644 index 00000000000..c84e54faa6b --- /dev/null +++ b/layout/reftests/border-image/solid-image-1-ref.html @@ -0,0 +1,11 @@ + + + + test of -moz-border-image + + + + +
border.png
second longer longer longer longer longer longer line
third longer longer longer longer longer longer line
+ + diff --git a/layout/reftests/border-image/solid-image-1.html b/layout/reftests/border-image/solid-image-1.html new file mode 100644 index 00000000000..0e6b7d58af4 --- /dev/null +++ b/layout/reftests/border-image/solid-image-1.html @@ -0,0 +1,18 @@ + + + + test of -moz-border-image + + + + + +
border.png
second longer longer longer longer longer longer line
third longer longer longer longer longer longer line
+ + diff --git a/layout/reftests/border-image/solid-image-2-ref.html b/layout/reftests/border-image/solid-image-2-ref.html new file mode 100644 index 00000000000..e62c1812ea3 --- /dev/null +++ b/layout/reftests/border-image/solid-image-2-ref.html @@ -0,0 +1,38 @@ + + + + test of -moz-border-image + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/border-image/solid-image-2.html b/layout/reftests/border-image/solid-image-2.html new file mode 100644 index 00000000000..6496f4c4081 --- /dev/null +++ b/layout/reftests/border-image/solid-image-2.html @@ -0,0 +1,40 @@ + + + + test of -moz-border-image + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/border-image/transparent-image-1-ref.html b/layout/reftests/border-image/transparent-image-1-ref.html new file mode 100644 index 00000000000..93aaa75ba6c --- /dev/null +++ b/layout/reftests/border-image/transparent-image-1-ref.html @@ -0,0 +1,11 @@ + + + + test of -moz-border-image + + + + +
border.png
second longer longer longer longer longer longer line
third longer longer longer longer longer longer line
+ + diff --git a/layout/reftests/border-image/transparent-image-1.html b/layout/reftests/border-image/transparent-image-1.html new file mode 100644 index 00000000000..03e5ec39e0c --- /dev/null +++ b/layout/reftests/border-image/transparent-image-1.html @@ -0,0 +1,18 @@ + + + + test of -moz-border-image + + + + + +
border.png
second longer longer longer longer longer longer line
third longer longer longer longer longer longer line
+ + diff --git a/layout/reftests/pixel-rounding/border-image-width-0.html b/layout/reftests/pixel-rounding/border-image-width-0.html new file mode 100644 index 00000000000..207978eca8d --- /dev/null +++ b/layout/reftests/pixel-rounding/border-image-width-0.html @@ -0,0 +1,59 @@ + + + +Pixel rounding testcase + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/pixel-rounding/border-image-width-10.html b/layout/reftests/pixel-rounding/border-image-width-10.html new file mode 100644 index 00000000000..a17a067b1f9 --- /dev/null +++ b/layout/reftests/pixel-rounding/border-image-width-10.html @@ -0,0 +1,59 @@ + + + +Pixel rounding testcase + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/pixel-rounding/border-image-width-4.html b/layout/reftests/pixel-rounding/border-image-width-4.html new file mode 100644 index 00000000000..a15076b80f3 --- /dev/null +++ b/layout/reftests/pixel-rounding/border-image-width-4.html @@ -0,0 +1,59 @@ + + + +Pixel rounding testcase + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/pixel-rounding/border-image-width-9.html b/layout/reftests/pixel-rounding/border-image-width-9.html new file mode 100644 index 00000000000..497924fb736 --- /dev/null +++ b/layout/reftests/pixel-rounding/border-image-width-9.html @@ -0,0 +1,59 @@ + + + +Pixel rounding testcase + + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/pixel-rounding/random-10x10.png b/layout/reftests/pixel-rounding/random-10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..a91d31a838e20b490f68a7444147a9494304300f GIT binary patch literal 391 zcmV;20eJq2P)Px$K}keGR2b6%HUY{01Kd(f=O6j}lw^5gLkJ+i*A^HkRN3FD<2iM@-sh788bNPmn9d_u~e#%EjObu zCX>F>I|GMD=fhrV1?5EvQ`RhW1XKW56qYABm>+Edw+Oi~gQLS3ZM!Q2m>0Oi`oaf=+$d8b3jkhh lzy6yeq`B$#)GQuOPK6^Dl)F%It5yI2002ovPDHLkV1kNtr=$P? literal 0 HcmV?d00001 diff --git a/layout/reftests/pixel-rounding/reftest.list b/layout/reftests/pixel-rounding/reftest.list index bdc75de8e1f..c7d50c4a621 100644 --- a/layout/reftests/pixel-rounding/reftest.list +++ b/layout/reftests/pixel-rounding/reftest.list @@ -173,3 +173,7 @@ random-if(MOZ_WIDGET_TOOLKIT=="cocoa") == image-left-6.html image-left-10-ref.ht == rounded-background-color-width-left-6.html rounded-background-color-width-6.html fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == background-image-tiling.html background-image-tiling-ref.html # probably bug 379317 + +!= border-image-width-0.html border-image-width-10.html +== border-image-width-4.html border-image-width-0.html +== border-image-width-9.html border-image-width-0.html diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 9a0a74519a3..0559773bb9b 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -14,6 +14,9 @@ include ../../modules/libpr0n/test/reftest/reftest.list # bidi/ include bidi/reftest.list +# border-image +include border-image/reftest.list + # box-properties/ include box-properties/reftest.list diff --git a/layout/style/Makefile.in b/layout/style/Makefile.in index 745c989ff47..128062fa372 100644 --- a/layout/style/Makefile.in +++ b/layout/style/Makefile.in @@ -117,6 +117,7 @@ EXPORTS = \ nsStyleSet.h \ nsStyleStruct.h \ nsStyleStructFwd.h \ + nsStyleStructInlines.h \ nsStyleStructList.h \ nsStyleUtil.h \ $(NULL) diff --git a/layout/style/nsCSSDataBlock.cpp b/layout/style/nsCSSDataBlock.cpp index ddf8b3bd8e9..79f3a1e6a12 100644 --- a/layout/style/nsCSSDataBlock.cpp +++ b/layout/style/nsCSSDataBlock.cpp @@ -206,10 +206,20 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const if (target->GetUnit() == eCSSUnit_Null) { const nsCSSValue *val = ValueAtCursor(cursor); NS_ASSERTION(val->GetUnit() != eCSSUnit_Null, "oops"); - if ((iProp == eCSSProperty_background_image || - iProp == eCSSProperty_list_style_image) && - val->GetUnit() == eCSSUnit_URL) { - val->StartImageLoad(aRuleData->mPresContext->Document()); + if (iProp == eCSSProperty_background_image || + iProp == eCSSProperty_list_style_image) { + if (val->GetUnit() == eCSSUnit_URL) { + val->StartImageLoad( + aRuleData->mPresContext->Document()); + } + } else if (iProp == eCSSProperty_border_image) { + if (val->GetUnit() == eCSSUnit_Array) { + nsCSSValue::Array *array = val->GetArrayValue(); + if (array->Item(0).GetUnit() == eCSSUnit_URL) { + array->Item(0).StartImageLoad( + aRuleData->mPresContext->Document()); + } + } } *target = *val; if (iProp == eCSSProperty_font_family) { diff --git a/layout/style/nsCSSDeclaration.cpp b/layout/style/nsCSSDeclaration.cpp index a893c59d7e0..fd109b1a6b4 100644 --- a/layout/style/nsCSSDeclaration.cpp +++ b/layout/style/nsCSSDeclaration.cpp @@ -329,6 +329,14 @@ nsCSSDeclaration::AppendCSSValueToString(nsCSSProperty aProperty, nsCSSValue::Array *array = aValue.GetArrayValue(); PRBool mark = PR_FALSE; for (PRUint16 i = 0, i_end = array->Count(); i < i_end; ++i) { + if (aProperty == eCSSProperty_border_image && i >= 5) { + if (array->Item(i).GetUnit() == eCSSUnit_Null) { + continue; + } + if (i == 5) { + aResult.AppendLiteral(" /"); + } + } if (mark && array->Item(i).GetUnit() != eCSSUnit_Null) { if (unit == eCSSUnit_Array) aResult.AppendLiteral(" "); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 64b371e81b9..0967cc29a4a 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -324,6 +324,7 @@ protected: PRBool ParseBorderColors(nsresult& aErrorCode, nsCSSValueList** aResult, nsCSSProperty aProperty); + PRBool ParseBorderImage(nsresult& aErrorCode); PRBool ParseBorderSpacing(nsresult& aErrorCode); PRBool ParseBorderSide(nsresult& aErrorCode, const nsCSSProperty aPropIDs[], @@ -4439,6 +4440,8 @@ PRBool CSSParserImpl::ParseProperty(nsresult& aErrorCode, return ParseBorderColors(aErrorCode, &mTempData.mMargin.mBorderColors.mTop, aPropID); + case eCSSProperty_border_image: + return ParseBorderImage(aErrorCode); case eCSSProperty_border_width: return ParseBorderWidth(aErrorCode); case eCSSProperty_border_end_color: @@ -4652,6 +4655,7 @@ PRBool CSSParserImpl::ParseSingleValueProperty(nsresult& aErrorCode, case eCSSProperty_border: case eCSSProperty_border_color: case eCSSProperty_border_bottom_colors: + case eCSSProperty_border_image: case eCSSProperty_border_left_colors: case eCSSProperty_border_right_colors: case eCSSProperty_border_end_color: @@ -5489,6 +5493,93 @@ PRBool CSSParserImpl::ParseBorderColor(nsresult& aErrorCode) kBorderColorIDs); } +PRBool CSSParserImpl::ParseBorderImage(nsresult& aErrorCode) +{ + if (ParseVariant(aErrorCode, mTempData.mMargin.mBorderImage, + VARIANT_INHERIT | VARIANT_NONE, nsnull)) { + mTempData.SetPropertyBit(eCSSProperty_border_image); + return PR_TRUE; + } + + // [ | ]{1,4} [ / {1,4} ]? [stretch | repeat | round]{0,2} + nsRefPtr arr = nsCSSValue::Array::Create(11); + if (!arr) { + aErrorCode = NS_ERROR_OUT_OF_MEMORY; + return PR_FALSE; + } + + nsCSSValue& url = arr->Item(0); + nsCSSValue& splitTop = arr->Item(1); + nsCSSValue& splitRight = arr->Item(2); + nsCSSValue& splitBottom = arr->Item(3); + nsCSSValue& splitLeft = arr->Item(4); + nsCSSValue& borderWidthTop = arr->Item(5); + nsCSSValue& borderWidthRight = arr->Item(6); + nsCSSValue& borderWidthBottom = arr->Item(7); + nsCSSValue& borderWidthLeft = arr->Item(8); + nsCSSValue& horizontalKeyword = arr->Item(9); + nsCSSValue& verticalKeyword = arr->Item(10); + + // + if (!ParseVariant(aErrorCode, url, VARIANT_URL, nsnull)) { + return PR_FALSE; + } + + // [ | ]{1,4} + if (!ParsePositiveVariant(aErrorCode, splitTop, + VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { + return PR_FALSE; + } + if (!ParsePositiveVariant(aErrorCode, splitRight, + VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { + splitRight = splitTop; + } + if (!ParsePositiveVariant(aErrorCode, splitBottom, + VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { + splitBottom = splitTop; + } + if (!ParsePositiveVariant(aErrorCode, splitLeft, + VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { + splitLeft = splitRight; + } + + // [ / {1,4} ]? + if (ExpectSymbol(aErrorCode, '/', PR_TRUE)) { + // if have '/', at least one value is required + if (!ParsePositiveVariant(aErrorCode, borderWidthTop, + VARIANT_LENGTH, nsnull)) { + return PR_FALSE; + } + if (!ParsePositiveVariant(aErrorCode, borderWidthRight, + VARIANT_LENGTH, nsnull)) { + borderWidthRight = borderWidthTop; + } + if (!ParsePositiveVariant(aErrorCode, borderWidthBottom, + VARIANT_LENGTH, nsnull)) { + borderWidthBottom = borderWidthTop; + } + if (!ParsePositiveVariant(aErrorCode, borderWidthLeft, + VARIANT_LENGTH, nsnull)) { + borderWidthLeft = borderWidthRight; + } + } + + // [stretch | repeat | round]{0,2} + // missing keywords are handled in nsRuleNode::ComputeBorderData() + if (ParseEnum(aErrorCode, horizontalKeyword, nsCSSProps::kBorderImageKTable)) { + ParseEnum(aErrorCode, verticalKeyword, nsCSSProps::kBorderImageKTable); + } + + if (!ExpectEndProperty(aErrorCode)) { + return PR_FALSE; + } + + mTempData.mMargin.mBorderImage.SetArrayValue(arr, eCSSUnit_Array); + mTempData.SetPropertyBit(eCSSProperty_border_image); + + return PR_TRUE; +} + PRBool CSSParserImpl::ParseBorderSpacing(nsresult& aErrorCode) { nsCSSValue xValue; diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 50c5d22e06f..26c22382faf 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -304,6 +304,7 @@ CSS_PROP_SHORTHAND(-moz-border-end-width, border_end_width, MozBorderEndWidth) #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL CSS_PROP_BORDER(border-end-width-value, border_end_width_value, X, Margin, mBorderEndWidth, eCSSType_Value, kBorderWidthKTable) #endif +CSS_PROP_BORDER(-moz-border-image, border_image, MozBorderImage, Margin, mBorderImage, eCSSType_Value, kBorderImageKTable) CSS_PROP_SHORTHAND(border-left, border_left, BorderLeft) CSS_PROP_SHORTHAND(border-left-color, border_left_color, BorderLeftColor) #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 28573a12ad9..051801f2bd6 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -352,6 +352,13 @@ const PRInt32 nsCSSProps::kBorderColorKTable[] = { eCSSKeyword_UNKNOWN,-1 }; +const PRInt32 nsCSSProps::kBorderImageKTable[] = { + eCSSKeyword_stretch, NS_STYLE_BORDER_IMAGE_STRETCH, + eCSSKeyword_repeat, NS_STYLE_BORDER_IMAGE_REPEAT, + eCSSKeyword_round, NS_STYLE_BORDER_IMAGE_ROUND, + eCSSKeyword_UNKNOWN,-1 +}; + const PRInt32 nsCSSProps::kBorderStyleKTable[] = { eCSSKeyword_hidden, NS_STYLE_BORDER_STYLE_HIDDEN, eCSSKeyword_dotted, NS_STYLE_BORDER_STYLE_DOTTED, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 3f2da407bed..00de0cb82a8 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -122,6 +122,7 @@ public: static const PRInt32 kBackgroundRepeatKTable[]; static const PRInt32 kBorderCollapseKTable[]; static const PRInt32 kBorderColorKTable[]; + static const PRInt32 kBorderImageKTable[]; static const PRInt32 kBorderStyleKTable[]; static const PRInt32 kBorderWidthKTable[]; static const PRInt32 kBoxAlignKTable[]; diff --git a/layout/style/nsCSSStruct.h b/layout/style/nsCSSStruct.h index 6f672040aa3..dba0f3da15d 100644 --- a/layout/style/nsCSSStruct.h +++ b/layout/style/nsCSSStruct.h @@ -354,6 +354,7 @@ struct nsCSSMargin : public nsCSSStruct { nsCSSValue mOutlineOffset; nsCSSRect mOutlineRadius; // (extension) nsCSSValue mFloatEdge; // NEW + nsCSSValue mBorderImage; nsCSSValueList* mBoxShadow; private: nsCSSMargin(const nsCSSMargin& aOther); // NOT IMPLEMENTED diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 6061a875809..b312f815395 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -61,6 +61,7 @@ #include "nsHTMLReflowState.h" #include "nsThemeConstants.h" #include "nsStyleUtil.h" +#include "nsStyleStructInlines.h" #include "nsPresContext.h" #include "nsIDocument.h" @@ -2173,6 +2174,93 @@ nsComputedDOMStyle::GetBoxSizing(nsIDOMCSSValue** aValue) return CallQueryInterface(val, aValue); } +nsresult +nsComputedDOMStyle::GetBorderImage(nsIDOMCSSValue** aValue) +{ + const nsStyleBorder* border = GetStyleBorder(); + + // none + if (!border->GetBorderImage()) { + nsROCSSPrimitiveValue *valNone = GetROCSSPrimitiveValue(); + NS_ENSURE_TRUE(valNone, NS_ERROR_OUT_OF_MEMORY); + valNone->SetIdent(nsGkAtoms::none); + return CallQueryInterface(valNone, aValue); + } + + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_FALSE); + NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); + + // uri + nsROCSSPrimitiveValue *valURI = GetROCSSPrimitiveValue(); + if (!valURI || !valueList->AppendCSSValue(valURI)) { + delete valURI; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + nsCOMPtr uri; + border->GetBorderImage()->GetURI(getter_AddRefs(uri)); + valURI->SetURI(uri); + + // four split numbers + NS_FOR_CSS_SIDES(side) { + nsROCSSPrimitiveValue *valSplit = GetROCSSPrimitiveValue(); + if (!valSplit || !valueList->AppendCSSValue(valSplit)) { + delete valSplit; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + SetValueToCoord(valSplit, border->mBorderImageSplit.Get(side), nsnull, + nsnull); + } + + // copy of border-width + if (border->mHaveBorderImageWidth) { + nsROCSSPrimitiveValue *slash = GetROCSSPrimitiveValue(); + if (!slash || !valueList->AppendCSSValue(slash)) { + delete slash; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + slash->SetString(NS_LITERAL_STRING("/")); + NS_FOR_CSS_SIDES(side) { + nsROCSSPrimitiveValue *borderWidth = GetROCSSPrimitiveValue(); + if (!borderWidth || !valueList->AppendCSSValue(borderWidth)) { + delete borderWidth; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + nscoord width = GetStyleBorder()->mBorderImageWidth.side(side); + borderWidth->SetAppUnits(width); + } + } + + // first keyword + nsROCSSPrimitiveValue *keyword = GetROCSSPrimitiveValue(); + if (!keyword || !valueList->AppendCSSValue(keyword)) { + delete keyword; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + const nsAFlatCString& borderImageIdent = + nsCSSProps::ValueToKeyword(GetStyleBorder()->mBorderImageHFill, + nsCSSProps::kBorderImageKTable); + keyword->SetIdent(borderImageIdent); + + // second keyword + nsROCSSPrimitiveValue *keyword2 = GetROCSSPrimitiveValue(); + if (!keyword2 || !valueList->AppendCSSValue(keyword2)) { + delete keyword2; + delete valueList; + return NS_ERROR_OUT_OF_MEMORY; + } + const nsAFlatCString& borderImageIdent2 = + nsCSSProps::ValueToKeyword(GetStyleBorder()->mBorderImageVFill, + nsCSSProps::kBorderImageKTable); + keyword2->SetIdent(borderImageIdent2); + + return CallQueryInterface(valueList, aValue); +} + nsresult nsComputedDOMStyle::GetFloatEdge(nsIDOMCSSValue** aValue) { @@ -2966,7 +3054,7 @@ nsComputedDOMStyle::GetBorderWidthFor(PRUint8 aSide, nsIDOMCSSValue** aValue) FlushPendingReflows(); width = mInnerFrame->GetUsedBorder().side(aSide); } else { - width = GetStyleBorder()->GetBorderWidth(aSide); + width = GetStyleBorder()->GetActualBorderWidth(aSide); } val->SetAppUnits(width); @@ -3854,6 +3942,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength) COMPUTED_STYLE_MAP_ENTRY(_moz_background_origin, BackgroundOrigin), COMPUTED_STYLE_MAP_ENTRY(binding, Binding), COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors, BorderBottomColors), + COMPUTED_STYLE_MAP_ENTRY(border_image, BorderImage), COMPUTED_STYLE_MAP_ENTRY(border_left_colors, BorderLeftColors), COMPUTED_STYLE_MAP_ENTRY(border_right_colors, BorderRightColors), COMPUTED_STYLE_MAP_ENTRY(border_top_colors, BorderTopColors), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 0187ee6b058..0f54684e9ba 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -198,6 +198,7 @@ private: nsresult GetBorderRadiusTopLeft(nsIDOMCSSValue** aValue); nsresult GetBorderRadiusTopRight(nsIDOMCSSValue** aValue); nsresult GetFloatEdge(nsIDOMCSSValue** aValue); + nsresult GetBorderImage(nsIDOMCSSValue** aValue); /* Box Shadow */ nsresult GetBoxShadow(nsIDOMCSSValue** aValue); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 3ed40bacf5c..6a33dc31cca 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -68,6 +68,7 @@ #include "nsILanguageAtomService.h" #include "nsIStyleRule.h" #include "nsBidiUtils.h" +#include "nsStyleStructInlines.h" /* * For storage of an |nsRuleNode|'s children in a PLDHashTable. @@ -3771,12 +3772,17 @@ nsRuleNode::ComputeBorderData(void* aStartStruct, } else if (eCSSUnit_Inherit == value.GetUnit()) { inherited = PR_TRUE; - border->SetBorderWidth(side, parentBorder->GetBorderWidth(side)); + border->SetBorderWidth(side, + parentBorder->GetComputedBorder().side(side)); } else if (eCSSUnit_Initial == value.GetUnit()) { border->SetBorderWidth(side, (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); } + else { + NS_ASSERTION(eCSSUnit_Null == value.GetUnit(), + "missing case handling border width"); + } } } @@ -3913,6 +3919,78 @@ nsRuleNode::ComputeBorderData(void* aStartStruct, else if (eCSSUnit_Initial == marginData.mFloatEdge.GetUnit()) { border->mFloatEdge = NS_STYLE_FLOAT_EDGE_CONTENT; } + + // border-image + if (eCSSUnit_Array == marginData.mBorderImage.GetUnit()) { + nsCSSValue::Array *arr = marginData.mBorderImage.GetArrayValue(); + + // the image + if (eCSSUnit_Image == arr->Item(0).GetUnit()) { + border->SetBorderImage(arr->Item(0).GetImageValue()); + } + + // the numbers saying where to split the image + NS_FOR_CSS_SIDES(side) { + // an uninitialized parentCoord is ok because I'm not passing SETCOORD_INHERIT + if (SetCoord(arr->Item(1 + side), coord, nsStyleCoord(), + SETCOORD_FACTOR | SETCOORD_PERCENT, aContext, + mPresContext, inherited)) { + border->mBorderImageSplit.Set(side, coord); + } + } + + // possible replacement for border-width + // if have one - have all four (see CSSParserImpl::ParseBorderImage()) + if (eCSSUnit_Null != arr->Item(5).GetUnit()) { + NS_FOR_CSS_SIDES(side) { + // an uninitialized parentCoord is ok because I'm not passing SETCOORD_INHERIT + if (!SetCoord(arr->Item(5 + side), coord, nsStyleCoord(), + SETCOORD_LENGTH, aContext, mPresContext, inherited)) { + NS_NOTREACHED("SetCoord for border-width replacement from border-image failed"); + } + if (coord.GetUnit() == eStyleUnit_Coord) { + border->SetBorderImageWidthOverride(side, coord.GetCoordValue()); + } else { + NS_WARNING("a border-width replacement from border-image " + "has a unit that's not eStyleUnit_Coord"); + border->SetBorderImageWidthOverride(side, 0); + } + } + border->mHaveBorderImageWidth = PR_TRUE; + } else { + border->mHaveBorderImageWidth = PR_FALSE; + } + + // stretch/round/repeat keywords + if (eCSSUnit_Null == arr->Item(9).GetUnit()) { + // default, both horizontal and vertical are stretch + border->mBorderImageHFill = NS_STYLE_BORDER_IMAGE_STRETCH; + border->mBorderImageVFill = NS_STYLE_BORDER_IMAGE_STRETCH; + } else { + // have horizontal value + border->mBorderImageHFill = arr->Item(9).GetIntValue(); + if (eCSSUnit_Null == arr->Item(10).GetUnit()) { + // vertical same as horizontal + border->mBorderImageVFill = border->mBorderImageHFill; + } else { + // have vertical value + border->mBorderImageVFill = arr->Item(10).GetIntValue(); + } + } + } else if (eCSSUnit_None == marginData.mBorderImage.GetUnit() || + eCSSUnit_Initial == marginData.mBorderImage.GetUnit()) { + border->mHaveBorderImageWidth = PR_FALSE; + border->SetBorderImage(nsnull); + } else if (eCSSUnit_Inherit == marginData.mBorderImage.GetUnit()) { + NS_FOR_CSS_SIDES(side) { + border->SetBorderImageWidthOverride(side, parentBorder->mBorderImageWidth.side(side)); + } + border->mBorderImageSplit = parentBorder->mBorderImageSplit; + border->mBorderImageHFill = parentBorder->mBorderImageHFill; + border->mBorderImageVFill = parentBorder->mBorderImageVFill; + border->mHaveBorderImageWidth = parentBorder->mHaveBorderImageWidth; + border->SetBorderImage(parentBorder->GetBorderImage()); + } COMPUTE_END_RESET(Border, border) } diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index f06095fd1fb..b51b7c6e3ba 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -644,10 +644,10 @@ void nsStyleContext::DumpRegressionData(nsPresContext* aPresContext, FILE* out, const char format [] = "top: %dtw right: %dtw bottom: %dtw left: %dtw"; #endif nsPrintfCString output(format, - border->GetBorderWidth(NS_SIDE_TOP), - border->GetBorderWidth(NS_SIDE_RIGHT), - border->GetBorderWidth(NS_SIDE_BOTTOM), - border->GetBorderWidth(NS_SIDE_LEFT)); + border->GetActualBorderWidth(NS_SIDE_TOP), + border->GetActualBorderWidth(NS_SIDE_RIGHT), + border->GetActualBorderWidth(NS_SIDE_BOTTOM), + border->GetActualBorderWidth(NS_SIDE_LEFT)); fprintf(out, "%s ", output.get()); border->mBorderRadius.ToString(str); fprintf(out, "%s ", NS_ConvertUTF16toUTF8(str).get()); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index e6d48efc240..644dc8775a8 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -44,6 +44,7 @@ */ #include "nsStyleStruct.h" +#include "nsStyleStructInlines.h" #include "nsStyleConsts.h" #include "nsThemeConstants.h" #include "nsString.h" @@ -353,7 +354,9 @@ nsChangeHint nsStylePadding::MaxDifference() #endif nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) - : mActualBorder(0, 0, 0, 0) + : mHaveBorderImageWidth(PR_FALSE), + mComputedBorder(0, 0, 0, 0), + mBorderImage(nsnull) { nscoord medium = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; @@ -373,12 +376,18 @@ nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) - : mActualBorder(aSrc.mActualBorder), - mTwipsPerPixel(aSrc.mTwipsPerPixel), - mBorder(aSrc.mBorder), - mBorderRadius(aSrc.mBorderRadius), + : mBorderRadius(aSrc.mBorderRadius), + mBorderImageSplit(aSrc.mBorderImageSplit), mFloatEdge(aSrc.mFloatEdge), - mBoxShadow(aSrc.mBoxShadow) + mBorderImageHFill(aSrc.mBorderImageHFill), + mBorderImageVFill(aSrc.mBorderImageVFill), + mBoxShadow(aSrc.mBoxShadow), + mHaveBorderImageWidth(aSrc.mHaveBorderImageWidth), + mBorderImageWidth(aSrc.mBorderImageWidth), + mComputedBorder(aSrc.mComputedBorder), + mBorder(aSrc.mBorder), + mBorderImage(aSrc.mBorderImage), + mTwipsPerPixel(aSrc.mTwipsPerPixel) { mBorderColors = nsnull; if (aSrc.mBorderColors) { @@ -396,6 +405,15 @@ nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) } } +nsStyleBorder::~nsStyleBorder() +{ + if (mBorderColors) { + for (PRInt32 i = 0; i < 4; i++) + delete mBorderColors[i]; + delete [] mBorderColors; + } +} + void* nsStyleBorder::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); @@ -416,7 +434,7 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const // Note that differences in mBorder don't affect rendering (which should only // use mComputedBorder), so don't need to be tested for here. if (mTwipsPerPixel == aOther.mTwipsPerPixel && - mActualBorder == aOther.mActualBorder && + mComputedBorder == aOther.mComputedBorder && mFloatEdge == aOther.mFloatEdge) { // Note that mBorderStyle stores not only the border style but also // color-related flags. Given that we've already done an mComputedBorder @@ -462,6 +480,25 @@ nsChangeHint nsStyleBorder::MaxDifference() } #endif +PRBool +nsStyleBorder::ImageBorderDiffers() const +{ + return mComputedBorder != + (mHaveBorderImageWidth ? mBorderImageWidth : mBorder); +} + +const nsMargin& +nsStyleBorder::GetActualBorder() const +{ + if (IsBorderImageLoaded()) + if (mHaveBorderImageWidth) + return mBorderImageWidth; + else + return mBorder; + else + return mComputedBorder; +} + nsStyleOutline::nsStyleOutline(nsPresContext* aPresContext) { // spacing values not inherited diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 7bda69e8a84..8f01bd539b6 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -22,6 +22,7 @@ * Contributor(s): * Mats Palmgren * Masayuki Nakano + * Rob Arnold * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -192,6 +193,7 @@ struct nsStyleBackground { // We have to take slower codepaths for fixed background attachment, // but we don't want to do that when there's no image. // Not inline because it uses an nsCOMPtr + // FIXME: Should be in nsStyleStructInlines.h. PRBool HasFixedBackground() const; }; @@ -399,13 +401,7 @@ class nsCSSShadowArray { struct nsStyleBorder { nsStyleBorder(nsPresContext* aContext); nsStyleBorder(const nsStyleBorder& aBorder); - ~nsStyleBorder(void) { - if (mBorderColors) { - for (PRInt32 i = 0; i < 4; i++) - delete mBorderColors[i]; - delete []mBorderColors; - } - } + ~nsStyleBorder(); void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW; void Destroy(nsPresContext* aContext); @@ -414,12 +410,18 @@ struct nsStyleBorder { #ifdef DEBUG static nsChangeHint MaxDifference(); #endif + PRBool ImageBorderDiffers() const; nsStyleSides mBorderRadius; // [reset] length, percent + nsStyleSides mBorderImageSplit; // [reset] integer, percent PRUint8 mFloatEdge; // [reset] see nsStyleConsts.h + PRUint8 mBorderImageHFill; // [reset] + PRUint8 mBorderImageVFill; // [reset] nsBorderColors** mBorderColors; // [reset] multiple levels of color for a border. nsRefPtr mBoxShadow; // [reset] NULL for 'none' - + PRBool mHaveBorderImageWidth; // [reset] + nsMargin mBorderImageWidth; // [reset] + void EnsureBorderColors() { if (!mBorderColors) { mBorderColors = new nsBorderColors*[4]; @@ -438,34 +440,54 @@ struct nsStyleBorder { // Return whether aStyle is a visible style. Invisible styles cause // the relevant computed border width to be 0. - static PRBool IsVisibleStyle(PRUint8 aStyle) { - return aStyle != NS_STYLE_BORDER_STYLE_NONE && - aStyle != NS_STYLE_BORDER_STYLE_HIDDEN; + // Note that this does *not* consider the effects of 'border-image': + // if border-style is none, but there is a loaded border image, + // HasVisibleStyle will be false even though there *is* a border. + PRBool HasVisibleStyle(PRUint8 aSide) + { + PRUint8 style = GetBorderStyle(aSide); + return (style != NS_STYLE_BORDER_STYLE_NONE && + style != NS_STYLE_BORDER_STYLE_HIDDEN); } // aBorderWidth is in twips void SetBorderWidth(PRUint8 aSide, nscoord aBorderWidth) { - mBorder.side(aSide) = aBorderWidth; - if (IsVisibleStyle(GetBorderStyle(aSide))) { - mActualBorder.side(aSide) = - NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, mTwipsPerPixel); - } + nscoord roundedWidth = + NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, mTwipsPerPixel); + mBorder.side(aSide) = roundedWidth; + if (HasVisibleStyle(aSide)) + mComputedBorder.side(aSide) = roundedWidth; } - // Get the actual border, in twips. - const nsMargin& GetBorder() const + void SetBorderImageWidthOverride(PRUint8 aSide, nscoord aBorderWidth) { - return mActualBorder; + mBorderImageWidth.side(aSide) = + NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, mTwipsPerPixel); + } + + // Get the actual border, in twips. (If there is no border-image + // loaded, this is the same as GetComputedBorder. If there is a + // border-image loaded, it uses the border-image width overrides if + // present, and otherwise mBorder, which is GetComputedBorder without + // considering border-style: none.) + const nsMargin& GetActualBorder() const; + + // Get the computed border (plus rounding). This does consider the + // effects of 'border-style: none', but does not consider + // 'border-image'. + const nsMargin& GetComputedBorder() const + { + return mComputedBorder; } // Get the actual border width for a particular side, in twips. Note that // this is zero if and only if there is no border to be painted for this // side. That is, this value takes into account the border style and the // value is rounded to the nearest device pixel by NS_ROUND_BORDER_TO_PIXELS. - nscoord GetBorderWidth(PRUint8 aSide) const + nscoord GetActualBorderWidth(PRUint8 aSide) const { - return mActualBorder.side(aSide); + return GetActualBorder().side(aSide); } PRUint8 GetBorderStyle(PRUint8 aSide) const @@ -479,14 +501,13 @@ struct nsStyleBorder { NS_ASSERTION(aSide <= NS_SIDE_LEFT, "bad side"); mBorderStyle[aSide] &= ~BORDER_STYLE_MASK; mBorderStyle[aSide] |= (aStyle & BORDER_STYLE_MASK); - if (IsVisibleStyle(aStyle)) { - mActualBorder.side(aSide) = - NS_ROUND_BORDER_TO_PIXELS(mBorder.side(aSide), mTwipsPerPixel); - } else { - mActualBorder.side(aSide) = 0; - } + mComputedBorder.side(aSide) = + (HasVisibleStyle(aSide) ? mBorder.side(aSide) : 0); } + // Defined in nsStyleStructInlines.h + inline PRBool IsBorderImageLoaded() const; + void GetBorderColor(PRUint8 aSide, nscolor& aColor, PRBool& aTransparent, PRBool& aForeground) const { @@ -507,6 +528,10 @@ struct nsStyleBorder { mBorderStyle[aSide] &= ~BORDER_COLOR_SPECIAL; } + // These are defined in nsStyleStructInlines.h + inline void SetBorderImage(imgIRequest* aImage); + inline imgIRequest* GetBorderImage() const; + void GetCompositeColors(PRInt32 aIndex, nsBorderColors** aColors) const { if (!mBorderColors) @@ -545,26 +570,34 @@ struct nsStyleBorder { } protected: - // mActualBorder holds the CSS2.1 actual border-width values. In + // mComputedBorder holds the CSS2.1 computed border-width values. In // particular, these widths take into account the border-style for the - // relevant side and the values are rounded to the nearest device pixel. - nsMargin mActualBorder; + // relevant side and the values are rounded to the nearest device + // pixel. They are also rounded (which is not part of the definition + // of computed values). However, they do *not* take into account the + // presence of border-image. See GetActualBorder above for how to + // really get the actual border. + nsMargin mComputedBorder; // mBorder holds the nscoord values for the border widths as they would be if // all the border-style values were visible (not hidden or none). This - // member exists solely so that when we create structs using the copy + // member exists so that when we create structs using the copy // constructor during style resolution the new structs will know what the // specified values of the border were in case they have more specific rules // setting the border style. Note that this isn't quite the CSS specified // value, since this has had the enumerated border widths converted to // lengths, and all lengths converted to twips. But it's not quite the - // computed value either. + // computed value either. The values are rounded to the nearest device pixel + // We also use these values when we have a loaded border-image that + // does not have width overrides. nsMargin mBorder; PRUint8 mBorderStyle[4]; // [reset] See nsStyleConsts.h nscolor mBorderColor[4]; // [reset] the colors to use for a simple border. not used // if -moz-border-colors is specified + nsCOMPtr mBorderImage; // [reset] + nscoord mTwipsPerPixel; }; diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h new file mode 100644 index 00000000000..f6bc46285b1 --- /dev/null +++ b/layout/style/nsStyleStructInlines.h @@ -0,0 +1,70 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is nsStyleStructInlines.h. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * L. David Baron , Mozilla Corporation (original author) + * Rob Arnold + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Inline methods that belong in nsStyleStruct.h, except that they + * require more headers. + */ + +#ifndef nsStyleStructInlines_h_ +#define nsStyleStructInlines_h_ + +#include "nsStyleStruct.h" +#include "imgIRequest.h" + +inline void +nsStyleBorder::SetBorderImage(imgIRequest* aImage) +{ + mBorderImage = aImage; +} + +inline imgIRequest* +nsStyleBorder::GetBorderImage() const +{ + return mBorderImage; +} + +inline PRBool nsStyleBorder::IsBorderImageLoaded() const +{ + PRUint32 status; + return mBorderImage && + NS_SUCCEEDED(mBorderImage->GetImageStatus(&status)) && + (status & imgIRequest::STATUS_FRAME_COMPLETE); +} + +#endif /* !defined(nsStyleStructInlines_h_) */ diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 124e047508a..5700c731b8d 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -149,6 +149,23 @@ var gCSSProperties = { other_values: [ "thin", "thick", "1px", "2em" ], invalid_values: [ "5%" ] }, + "-moz-border-image": { + domProp: "MozBorderImage", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url('border.png') 27 27 27 27", + "url('border.png') 27", + "url('border.png') 27 27 27 27 repeat", + "url('border.png') 27 27 27 27 / 1em", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em repeat", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em stretch round" ], + invalid_values: [ "url('border.png')", + "url('border.png') 27 27 27 27 27", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em 1em", + "url('border.png') / repeat", + "url('border.png') 27 27 27 27 /" ] + }, "-moz-border-left-colors": { domProp: "MozBorderLeftColors", inherited: false, diff --git a/layout/style/test/test_value_storage.html b/layout/style/test/test_value_storage.html index f8bdde1efb4..a5ee652be11 100644 --- a/layout/style/test/test_value_storage.html +++ b/layout/style/test/test_value_storage.html @@ -199,14 +199,14 @@ function test_property(property) step1comps.push(gComputedStyle.getPropertyValue(info.subproperties[idx])); func = xfail_accepted(property, value) ? todo_isnot : isnot; - func(step1val, "", "setting '" + value + "' on '" + property); + func(step1val, "", "setting '" + value + "' on '" + property + "'"); if ("subproperties" in info) for (idx in info.subproperties) { var subprop = info.subproperties[idx]; func = xfail_accepted_split(property, subprop, value) ? todo_isnot : isnot; func(gDeclaration.getPropertyValue(subprop), "", - "setting '" + value + "' on '" + property); + "setting '" + value + "' on '" + property + "'"); } // We don't care particularly about the whitespace or the placement of diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index e050f232542..4db7d8a372b 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -1040,7 +1040,7 @@ NS_NewTableCellFrame(nsIPresShell* aPresShell, nsMargin* nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const { - aBorder = GetStyleBorder()->GetBorder(); + aBorder = GetStyleBorder()->GetActualBorder(); return &aBorder; } diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index af3d5225745..f2fa94a51e3 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -2546,7 +2546,7 @@ void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState, // mComputedBorderPadding or we don't and then we get the padding // wrong! const nsStyleBorder* border = aStyleContext.GetStyleBorder(); - aBorderPadding = border->GetBorder(); + aBorderPadding = border->GetActualBorder(); if (aReflowState) { aBorderPadding += aReflowState->mComputedPadding; } @@ -4648,7 +4648,7 @@ GetColorAndStyle(const nsIFrame* aFrame, aSide = NS_SIDE_RIGHT; } } - width = styleData->GetBorderWidth(aSide); + width = styleData->GetActualBorderWidth(aSide); aWidth = nsPresContext::AppUnitsToIntCSSPixels(width); } diff --git a/layout/xul/base/src/nsBox.cpp b/layout/xul/base/src/nsBox.cpp index f0495fb906c..c3a9bc7a222 100644 --- a/layout/xul/base/src/nsBox.cpp +++ b/layout/xul/base/src/nsBox.cpp @@ -355,7 +355,7 @@ nsBox::GetBorder(nsMargin& aMargin) } } - aMargin = GetStyleBorder()->GetBorder(); + aMargin = GetStyleBorder()->GetActualBorder(); return NS_OK; } diff --git a/layout/xul/base/src/nsBoxObject.cpp b/layout/xul/base/src/nsBoxObject.cpp index 60cf43350ca..dce253a0bcf 100644 --- a/layout/xul/base/src/nsBoxObject.cpp +++ b/layout/xul/base/src/nsBoxObject.cpp @@ -197,13 +197,13 @@ nsBoxObject::GetOffsetRect(nsRect& aRect) // For the origin, add in the border for the frame const nsStyleBorder* border = frame->GetStyleBorder(); - origin.x += border->GetBorderWidth(NS_SIDE_LEFT); - origin.y += border->GetBorderWidth(NS_SIDE_TOP); + origin.x += border->GetActualBorderWidth(NS_SIDE_LEFT); + origin.y += border->GetActualBorderWidth(NS_SIDE_TOP); // And subtract out the border for the parent const nsStyleBorder* parentBorder = parent->GetStyleBorder(); - origin.x -= parentBorder->GetBorderWidth(NS_SIDE_LEFT); - origin.y -= parentBorder->GetBorderWidth(NS_SIDE_TOP); + origin.x -= parentBorder->GetActualBorderWidth(NS_SIDE_LEFT); + origin.y -= parentBorder->GetActualBorderWidth(NS_SIDE_TOP); aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x); aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y); diff --git a/layout/xul/base/src/nsGroupBoxFrame.cpp b/layout/xul/base/src/nsGroupBoxFrame.cpp index 7bcf2824bda..7361aa6543c 100644 --- a/layout/xul/base/src/nsGroupBoxFrame.cpp +++ b/layout/xul/base/src/nsGroupBoxFrame.cpp @@ -151,7 +151,7 @@ nsGroupBoxFrame::PaintBorderBackground(nsIRenderingContext& aRenderingContext, PRIntn skipSides = 0; const nsStyleBorder* borderStyleData = GetStyleBorder(); const nsStylePadding* paddingStyleData = GetStylePadding(); - const nsMargin& border = borderStyleData->GetBorder(); + const nsMargin& border = borderStyleData->GetActualBorder(); nscoord yoff = 0; nsPresContext* presContext = PresContext(); diff --git a/layout/xul/base/src/nsListBoxBodyFrame.cpp b/layout/xul/base/src/nsListBoxBodyFrame.cpp index 531f5237b85..34c23bc185f 100644 --- a/layout/xul/base/src/nsListBoxBodyFrame.cpp +++ b/layout/xul/base/src/nsListBoxBodyFrame.cpp @@ -776,7 +776,7 @@ nsListBoxBodyFrame::ComputeIntrinsicWidth(nsBoxLayoutState& aBoxLayoutState) if (styleContext->GetStylePadding()->GetPadding(margin)) width += margin.LeftRight(); - width += styleContext->GetStyleBorder()->GetBorder().LeftRight(); + width += styleContext->GetStyleBorder()->GetActualBorder().LeftRight(); if (styleContext->GetStyleMargin()->GetMargin(margin)) width += margin.LeftRight(); diff --git a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index 43b7b25bcc5..32c98019287 100644 --- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -195,7 +195,7 @@ GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin) if (!aContext->GetStylePadding()->GetPadding(aMargin)) { NS_NOTYETIMPLEMENTED("percentage padding"); } - aMargin += aContext->GetStyleBorder()->GetBorder(); + aMargin += aContext->GetStyleBorder()->GetActualBorder(); } static void