Use nsIFrame::GetBorderRadii so that we pick up when it is overridden. (Bug 459144, patch 7) r=roc a2.0=blocking:beta6+

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */// vim:cindent:ts=2:et:sw=2:/* ***** 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 mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mats Palmgren <mats.palmgren@bredband.net> * Takeshi Ichimaru <ayakawa.m@gmail.com> * Masayuki Nakano <masayuki@d-toybox.com> * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation * Michael Ventnor <m.ventnor@gmail.com> * Rob Arnold <robarnold@mozilla.com> * Jeff Walden <jwalden+code@mit.edu> * * 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"), * 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 ***** *//* utility functions for drawing borders and backgrounds */#include"nsStyleConsts.h"#include"nsPresContext.h"#include"nsIFrame.h"#include"nsPoint.h"#include"nsRect.h"#include"nsIViewManager.h"#include"nsIPresShell.h"#include"nsFrameManager.h"#include"nsStyleContext.h"#include"nsGkAtoms.h"#include"nsCSSAnonBoxes.h"#include"nsTransform2D.h"#include"nsIDeviceContext.h"#include"nsIContent.h"#include"nsIDocument.h"#include"nsIScrollableFrame.h"#include"imgIRequest.h"#include"imgIContainer.h"#include"nsCSSRendering.h"#include"nsCSSColorUtils.h"#include"nsITheme.h"#include"nsThemeConstants.h"#include"nsIServiceManager.h"#include"nsIHTMLDocument.h"#include"nsLayoutUtils.h"#include"nsINameSpaceManager.h"#include"nsBlockFrame.h"#include"gfxContext.h"#include"nsIInterfaceRequestorUtils.h"#include"gfxPlatform.h"#include"gfxImageSurface.h"#include"nsStyleStructInlines.h"#include"nsCSSFrameConstructor.h"#include"nsCSSProps.h"#include"nsContentUtils.h"#ifdef MOZ_SVG#include"nsSVGEffects.h"#include"nsSVGIntegrationUtils.h"#include"gfxDrawable.h"#endif#include"nsCSSRenderingBorders.h"/** * This is a small wrapper class to encapsulate image drawing that can draw an * nsStyleImage image, which may internally be a real image, a sub image, or a * CSS gradient. * * @note Always call the member functions in the order of PrepareImage(), * ComputeSize(), and Draw(). */classImageRenderer{public:enum{FLAG_SYNC_DECODE_IMAGES=0x01};ImageRenderer(nsIFrame*aForFrame,constnsStyleImage*aImage,PRUint32aFlags);~ImageRenderer();/** * Populates member variables to get ready for rendering. * @return PR_TRUE iff the image is ready, and there is at least a pixel to * draw. */PRBoolPrepareImage();/** * @return the image size in appunits. CSS gradient images don't have an * intrinsic size so we have to pass in a default that they will use. */nsSizeComputeSize(constnsSize&aDefault);/** * Draws the image to the target rendering context. * @see nsLayoutUtils::DrawImage() for other parameters */voidDraw(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty);private:nsIFrame*mForFrame;constnsStyleImage*mImage;nsStyleImageTypemType;nsCOMPtr<imgIContainer>mImageContainer;nsRefPtr<nsStyleGradient>mGradientData;#ifdef MOZ_SVGnsIFrame*mPaintServerFrame;nsLayoutUtils::SurfaceFromElementResultmImageElementSurface;#endifPRBoolmIsReady;nsSizemSize;PRUint32mFlags;};// To avoid storing this data on nsInlineFrame (bloat) and to avoid// recalculating this for each frame in a continuation (perf), hold// a cache of various coordinate information that we need in order// to paint inline backgrounds.structInlineBackgroundData{InlineBackgroundData():mFrame(nsnull),mBlockFrame(nsnull){}~InlineBackgroundData(){}voidReset(){mBoundingBox.SetRect(0,0,0,0);mContinuationPoint=mLineContinuationPoint=mUnbrokenWidth=0;mFrame=mBlockFrame=nsnull;}nsRectGetContinuousRect(nsIFrame*aFrame){SetFrame(aFrame);nscoordx;if(mBidiEnabled){x=mLineContinuationPoint;// Scan continuations on the same line as aFrame and accumulate the widths// of frames that are to the left (if this is an LTR block) or right// (if it's RTL) of the current one.PRBoolisRtlBlock=(mBlockFrame->GetStyleVisibility()->mDirection==NS_STYLE_DIRECTION_RTL);nscoordcurOffset=aFrame->GetOffsetTo(mBlockFrame).x;// No need to use our GetPrevContinuation/GetNextContinuation methods// here, since ib special siblings are certainly not on the same line.nsIFrame*inlineFrame=aFrame->GetPrevContinuation();// If the continuation is fluid we know inlineFrame is not on the same line.// If it's not fluid, we need to test further to be sure.while(inlineFrame&&!inlineFrame->GetNextInFlow()&&AreOnSameLine(aFrame,inlineFrame)){nscoordframeXOffset=inlineFrame->GetOffsetTo(mBlockFrame).x;if(isRtlBlock==(frameXOffset>=curOffset)){x+=inlineFrame->GetSize().width;}inlineFrame=inlineFrame->GetPrevContinuation();}inlineFrame=aFrame->GetNextContinuation();while(inlineFrame&&!inlineFrame->GetPrevInFlow()&&AreOnSameLine(aFrame,inlineFrame)){nscoordframeXOffset=inlineFrame->GetOffsetTo(mBlockFrame).x;if(isRtlBlock==(frameXOffset>=curOffset)){x+=inlineFrame->GetSize().width;}inlineFrame=inlineFrame->GetNextContinuation();}if(isRtlBlock){// aFrame itself is also to the right of its left edge, so add its width.x+=aFrame->GetSize().width;// x is now the distance from the left edge of aFrame to the right edge// of the unbroken content. Change it to indicate the distance from the// left edge of the unbroken content to the left edge of aFrame.x=mUnbrokenWidth-x;}}else{x=mContinuationPoint;}// Assume background-origin: border and return a rect with offsets// relative to (0,0). If we have a different background-origin,// then our rect should be deflated appropriately by our caller.returnnsRect(-x,0,mUnbrokenWidth,mFrame->GetSize().height);}nsRectGetBoundingRect(nsIFrame*aFrame){SetFrame(aFrame);// Move the offsets relative to (0,0) which puts the bounding box into// our coordinate system rather than our parent's. We do this by// moving it the back distance from us to the bounding box.// This also assumes background-origin: border, so our caller will// need to deflate us if needed.nsRectboundingBox(mBoundingBox);nsPointpoint=mFrame->GetPosition();boundingBox.MoveBy(-point.x,-point.y);returnboundingBox;}protected:nsIFrame*mFrame;nsBlockFrame*mBlockFrame;nsRectmBoundingBox;nscoordmContinuationPoint;nscoordmUnbrokenWidth;nscoordmLineContinuationPoint;PRBoolmBidiEnabled;voidSetFrame(nsIFrame*aFrame){NS_PRECONDITION(aFrame,"Need a frame");nsIFrame*prevContinuation=GetPrevContinuation(aFrame);if(!prevContinuation||mFrame!=prevContinuation){// Ok, we've got the wrong frame. We have to start from scratch.Reset();Init(aFrame);return;}// Get our last frame's size and add its width to our continuation// point before we cache the new frame.mContinuationPoint+=mFrame->GetSize().width;// If this a new line, update mLineContinuationPoint.if(mBidiEnabled&&(aFrame->GetPrevInFlow()||!AreOnSameLine(mFrame,aFrame))){mLineContinuationPoint=mContinuationPoint;}mFrame=aFrame;}nsIFrame*GetPrevContinuation(nsIFrame*aFrame){nsIFrame*prevCont=aFrame->GetPrevContinuation();if(!prevCont&&(aFrame->GetStateBits()&NS_FRAME_IS_SPECIAL)){nsIFrame*block=static_cast<nsIFrame*>(aFrame->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));if(block){// The {ib} properties are only stored on first continuationsNS_ASSERTION(!block->GetPrevContinuation(),"Incorrect value for IBSplitSpecialPrevSibling");prevCont=static_cast<nsIFrame*>(block->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));NS_ASSERTION(prevCont,"How did that happen?");}}returnprevCont;}nsIFrame*GetNextContinuation(nsIFrame*aFrame){nsIFrame*nextCont=aFrame->GetNextContinuation();if(!nextCont&&(aFrame->GetStateBits()&NS_FRAME_IS_SPECIAL)){// The {ib} properties are only stored on first continuationsaFrame=aFrame->GetFirstContinuation();nsIFrame*block=static_cast<nsIFrame*>(aFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling()));if(block){nextCont=static_cast<nsIFrame*>(block->Properties().Get(nsIFrame::IBSplitSpecialSibling()));NS_ASSERTION(nextCont,"How did that happen?");}}returnnextCont;}voidInit(nsIFrame*aFrame){// Start with the previous flow frame as our continuation point// is the total of the widths of the previous frames.nsIFrame*inlineFrame=GetPrevContinuation(aFrame);while(inlineFrame){nsRectrect=inlineFrame->GetRect();mContinuationPoint+=rect.width;mUnbrokenWidth+=rect.width;mBoundingBox.UnionRect(mBoundingBox,rect);inlineFrame=GetPrevContinuation(inlineFrame);}// Next add this frame and subsequent frames to the bounding box and// unbroken width.inlineFrame=aFrame;while(inlineFrame){nsRectrect=inlineFrame->GetRect();mUnbrokenWidth+=rect.width;mBoundingBox.UnionRect(mBoundingBox,rect);inlineFrame=GetNextContinuation(inlineFrame);}mFrame=aFrame;mBidiEnabled=aFrame->PresContext()->BidiEnabled();if(mBidiEnabled){// Find the containing block framensIFrame*frame=aFrame;do{frame=frame->GetParent();mBlockFrame=do_QueryFrame(frame);}while(frame&&frame->IsFrameOfType(nsIFrame::eLineParticipant));NS_ASSERTION(mBlockFrame,"Cannot find containing block.");mLineContinuationPoint=mContinuationPoint;}}PRBoolAreOnSameLine(nsIFrame*aFrame1,nsIFrame*aFrame2){// Assumes that aFrame1 and aFrame2 are both decsendants of mBlockFrame.PRBoolisValid1,isValid2;nsBlockInFlowLineIteratorit1(mBlockFrame,aFrame1,&isValid1);nsBlockInFlowLineIteratorit2(mBlockFrame,aFrame2,&isValid2);returnisValid1&&isValid2&&it1.GetLine()==it2.GetLine();}};/* Local functions */staticvoidPaintBackgroundLayer(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,PRUint32aFlags,constnsRect&aDirtyRect,constnsRect&aBorderArea,constnsRect&aBGClipRect,constnsStyleBackground&aBackground,constnsStyleBackground::Layer&aLayer);staticvoidDrawBorderImage(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aBorderArea,constnsStyleBorder&aStyleBorder,constnsRect&aDirtyRect);staticvoidDrawBorderImageComponent(nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,imgIContainer*aImage,constnsRect&aDirtyRect,constnsRect&aFill,constnsIntRect&aSrc,PRUint8aHFill,PRUint8aVFill,constnsSize&aUnitSize,constnsStyleBorder&aStyleBorder,PRUint8aIndex);staticnscolorMakeBevelColor(mozilla::css::SidewhichSide,PRUint8style,nscoloraBackgroundColor,nscoloraBorderColor);staticInlineBackgroundData*gInlineBGData=nsnull;// Initialize any static variables used by nsCSSRendering.nsresultnsCSSRendering::Init(){NS_ASSERTION(!gInlineBGData,"Init called twice");gInlineBGData=newInlineBackgroundData();if(!gInlineBGData)returnNS_ERROR_OUT_OF_MEMORY;returnNS_OK;}// Clean up any global variables used by nsCSSRendering.voidnsCSSRendering::Shutdown(){deletegInlineBGData;gInlineBGData=nsnull;}/** * Make a bevel color */staticnscolorMakeBevelColor(mozilla::css::SidewhichSide,PRUint8style,nscoloraBackgroundColor,nscoloraBorderColor){nscolorcolors[2];nscolortheColor;// Given a background color and a border color// calculate the color used for the shadingNS_GetSpecial3DColors(colors,aBackgroundColor,aBorderColor);if((style==NS_STYLE_BORDER_STYLE_OUTSET)||(style==NS_STYLE_BORDER_STYLE_RIDGE)){// Flip colors for these two border stylesswitch(whichSide){caseNS_SIDE_BOTTOM:whichSide=NS_SIDE_TOP;break;caseNS_SIDE_RIGHT:whichSide=NS_SIDE_LEFT;break;caseNS_SIDE_TOP:whichSide=NS_SIDE_BOTTOM;break;caseNS_SIDE_LEFT:whichSide=NS_SIDE_RIGHT;break;}}switch(whichSide){caseNS_SIDE_BOTTOM:theColor=colors[1];break;caseNS_SIDE_RIGHT:theColor=colors[1];break;caseNS_SIDE_TOP:theColor=colors[0];break;caseNS_SIDE_LEFT:default:theColor=colors[0];break;}returntheColor;}//----------------------------------------------------------------------// Thebes Border Rendering Code Start// helper function to convert a nsRect to a gfxRectstaticgfxRectRectToGfxRect(constnsRect&rect,nscoordtwipsPerPixel){returngfxRect(gfxFloat(rect.x)/twipsPerPixel,gfxFloat(rect.y)/twipsPerPixel,gfxFloat(rect.width)/twipsPerPixel,gfxFloat(rect.height)/twipsPerPixel);}/* * Compute the float-pixel radii that should be used for drawing * this border/outline, given the various input bits. */staticvoidComputePixelRadii(constnscoord*aTwipsRadii,nscoordtwipsPerPixel,gfxCornerSizes*oBorderRadii){gfxFloatradii[8];NS_FOR_CSS_HALF_CORNERS(corner)radii[corner]=gfxFloat(aTwipsRadii[corner])/twipsPerPixel;(*oBorderRadii)[C_TL]=gfxSize(radii[NS_CORNER_TOP_LEFT_X],radii[NS_CORNER_TOP_LEFT_Y]);(*oBorderRadii)[C_TR]=gfxSize(radii[NS_CORNER_TOP_RIGHT_X],radii[NS_CORNER_TOP_RIGHT_Y]);(*oBorderRadii)[C_BR]=gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],radii[NS_CORNER_BOTTOM_RIGHT_Y]);(*oBorderRadii)[C_BL]=gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],radii[NS_CORNER_BOTTOM_LEFT_Y]);}voidnsCSSRendering::PaintBorder(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aDirtyRect,constnsRect&aBorderArea,nsStyleContext*aStyleContext,PRIntnaSkipSides){nsStyleContext*styleIfVisited=aStyleContext->GetStyleIfVisited();constnsStyleBorder*styleBorder=aStyleContext->GetStyleBorder();// Don't check RelevantLinkVisited here, since we want to take the// same amount of time whether or not it's true.if(!styleIfVisited){PaintBorderWithStyleBorder(aPresContext,aRenderingContext,aForFrame,aDirtyRect,aBorderArea,*styleBorder,aStyleContext,aSkipSides);return;}nsStyleBordernewStyleBorder(*styleBorder);NS_FOR_CSS_SIDES(side){newStyleBorder.SetBorderColor(side,aStyleContext->GetVisitedDependentColor(nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]));}PaintBorderWithStyleBorder(aPresContext,aRenderingContext,aForFrame,aDirtyRect,aBorderArea,newStyleBorder,aStyleContext,aSkipSides);}voidnsCSSRendering::PaintBorderWithStyleBorder(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aDirtyRect,constnsRect&aBorderArea,constnsStyleBorder&aStyleBorder,nsStyleContext*aStyleContext,PRIntnaSkipSides){nsMarginborder;nscoordtwipsRadii[8];nsCompatibilitycompatMode=aPresContext->CompatibilityMode();SN("++ PaintBorder");// Check to see if we have an appearance defined. If so, we let the theme// renderer draw the border. DO not get the data from aForFrame, since the passed in style context// may be different! Always use |aStyleContext|!constnsStyleDisplay*displayData=aStyleContext->GetStyleDisplay();if(displayData->mAppearance){nsITheme*theme=aPresContext->GetTheme();if(theme&&theme->ThemeSupportsWidget(aPresContext,aForFrame,displayData->mAppearance))return;// Let the theme handle it.}if(aStyleBorder.IsBorderImageLoaded()){DrawBorderImage(aPresContext,aRenderingContext,aForFrame,aBorderArea,aStyleBorder,aDirtyRect);return;}// Get our style context's color struct.constnsStyleColor*ourColor=aStyleContext->GetStyleColor();// in NavQuirks mode we want to use the parent's context as a starting point// for determining the background colornsIFrame*bgFrame=nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame,compatMode==eCompatibility_NavQuirks?PR_TRUE:PR_FALSE);nsStyleContext*bgContext=bgFrame->GetStyleContext();nscolorbgColor=bgContext->GetVisitedDependentColor(eCSSProperty_background_color);border=aStyleBorder.GetComputedBorder();if((0==border.left)&&(0==border.right)&&(0==border.top)&&(0==border.bottom)){// Empty border areareturn;}nsSizeframeSize=aForFrame->GetSize();if(&aStyleBorder==aForFrame->GetStyleBorder()&&frameSize==aBorderArea.Size()){aForFrame->GetBorderRadii(twipsRadii);}else{nsIFrame::ComputeBorderRadii(aStyleBorder.mBorderRadius,frameSize,aBorderArea.Size(),aSkipSides,twipsRadii);}// Turn off rendering for all of the zero sized sidesif(aSkipSides&SIDE_BIT_TOP)border.top=0;if(aSkipSides&SIDE_BIT_RIGHT)border.right=0;if(aSkipSides&SIDE_BIT_BOTTOM)border.bottom=0;if(aSkipSides&SIDE_BIT_LEFT)border.left=0;// get the inside and outside parts of the bordernsRectouterRect(aBorderArea);SF(" outerRect: %d %d %d %d\n",outerRect.x,outerRect.y,outerRect.width,outerRect.height);// we can assume that we're already clipped to aDirtyRect -- I think? (!?)// Get our conversion valuesnscoordtwipsPerPixel=aPresContext->DevPixelsToAppUnits(1);// convert outer and inner rectsgfxRectoRect(RectToGfxRect(outerRect,twipsPerPixel));// convert the border widthsgfxFloatborderWidths[4]={gfxFloat(border.top/twipsPerPixel),gfxFloat(border.right/twipsPerPixel),gfxFloat(border.bottom/twipsPerPixel),gfxFloat(border.left/twipsPerPixel)};// convert the radiigfxCornerSizesborderRadii;ComputePixelRadii(twipsRadii,twipsPerPixel,&borderRadii);PRUint8borderStyles[4];nscolorborderColors[4];nsBorderColors*compositeColors[4];// pull out styles, colors, composite colorsNS_FOR_CSS_SIDES(i){PRBoolforeground;borderStyles[i]=aStyleBorder.GetBorderStyle(i);aStyleBorder.GetBorderColor(i,borderColors[i],foreground);aStyleBorder.GetCompositeColors(i,&compositeColors[i]);if(foreground)borderColors[i]=ourColor->mColor;}SF(" borderStyles: %d %d %d %d\n",borderStyles[0],borderStyles[1],borderStyles[2],borderStyles[3]);// start drawinggfxContext*ctx=aRenderingContext.ThebesContext();ctx->Save();#if 0 // this will draw a transparent red backround underneath the oRect area ctx->Save(); ctx->Rectangle(oRect); ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5)); ctx->Fill(); ctx->Restore();#endif//SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);nsCSSBorderRendererbr(twipsPerPixel,ctx,oRect,borderStyles,borderWidths,borderRadii,borderColors,compositeColors,aSkipSides,bgColor);br.DrawBorders();ctx->Restore();SN();}staticnsRectGetOutlineInnerRect(nsIFrame*aFrame){nsRect*savedOutlineInnerRect=static_cast<nsRect*>(aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty()));if(savedOutlineInnerRect)return*savedOutlineInnerRect;returnaFrame->GetOverflowRect();}voidnsCSSRendering::PaintOutline(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aDirtyRect,constnsRect&aBorderArea,nsStyleContext*aStyleContext){nscoordtwipsRadii[8];// Get our style context's color struct.constnsStyleOutline*ourOutline=aStyleContext->GetStyleOutline();nscoordwidth;ourOutline->GetOutlineWidth(width);if(width==0){// Empty outlinereturn;}nsIFrame*bgFrame=nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame,PR_FALSE);nsStyleContext*bgContext=bgFrame->GetStyleContext();nscolorbgColor=bgContext->GetVisitedDependentColor(eCSSProperty_background_color);// When the outline property is set on :-moz-anonymous-block or// :-moz-anonyomus-positioned-block pseudo-elements, it inherited that// outline from the inline that was broken because it contained a// block. In that case, we don't want a really wide outline if the// block inside the inline is narrow, so union the actual contents of// the anonymous blocks.nsIFrame*frameForArea=aForFrame;do{nsIAtom*pseudoType=frameForArea->GetStyleContext()->GetPseudo();if(pseudoType!=nsCSSAnonBoxes::mozAnonymousBlock&&pseudoType!=nsCSSAnonBoxes::mozAnonymousPositionedBlock)break;// If we're done, we really want it and all its later siblings.frameForArea=frameForArea->GetFirstChild(nsnull);NS_ASSERTION(frameForArea,"anonymous block with no children?");}while(frameForArea);nsRectinnerRect;// relative to aBorderArea.TopLeft()if(frameForArea==aForFrame){innerRect=GetOutlineInnerRect(aForFrame);}else{for(;frameForArea;frameForArea=frameForArea->GetNextSibling()){// The outline has already been included in aForFrame's overflow// area, but not in those of its descendants, so we have to// include it. Otherwise we'll end up drawing the outline inside// the border.nsRectr(GetOutlineInnerRect(frameForArea)+frameForArea->GetOffsetTo(aForFrame));innerRect.UnionRect(innerRect,r);}}innerRect+=aBorderArea.TopLeft();nscoordoffset=ourOutline->mOutlineOffset;innerRect.Inflate(offset,offset);// If the dirty rect is completely inside the border area (e.g., only the// content is being painted), then we can skip out now// XXX this isn't exactly true for rounded borders, where the inside curves may// encroach into the content area. A safer calculation would be to// shorten insideRect by the radius one each side before performing this test.if(innerRect.Contains(aDirtyRect))return;nsRectouterRect=innerRect;outerRect.Inflate(width,width);// get the radius for our outlinensIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius,aBorderArea.Size(),outerRect.Size(),0,twipsRadii);// Get our conversion valuesnscoordtwipsPerPixel=aPresContext->DevPixelsToAppUnits(1);// get the outer rectanglesgfxRectoRect(RectToGfxRect(outerRect,twipsPerPixel));// convert the radiinsMarginoutlineMargin(width,width,width,width);gfxCornerSizesoutlineRadii;ComputePixelRadii(twipsRadii,twipsPerPixel,&outlineRadii);PRUint8outlineStyle=ourOutline->GetOutlineStyle();PRUint8outlineStyles[4]={outlineStyle,outlineStyle,outlineStyle,outlineStyle};// This handles treating the initial color as 'currentColor'; if we// ever want 'invert' back we'll need to do a bit of work here too.nscoloroutlineColor=aStyleContext->GetVisitedDependentColor(eCSSProperty_outline_color);nscoloroutlineColors[4]={outlineColor,outlineColor,outlineColor,outlineColor};// convert the border widthsgfxFloatoutlineWidths[4]={gfxFloat(width/twipsPerPixel),gfxFloat(width/twipsPerPixel),gfxFloat(width/twipsPerPixel),gfxFloat(width/twipsPerPixel)};// start drawinggfxContext*ctx=aRenderingContext.ThebesContext();ctx->Save();nsCSSBorderRendererbr(twipsPerPixel,ctx,oRect,outlineStyles,outlineWidths,outlineRadii,outlineColors,nsnull,0,bgColor);br.DrawBorders();ctx->Restore();SN();}voidnsCSSRendering::PaintFocus(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,constnsRect&aFocusRect,nscoloraColor){nscoordoneCSSPixel=nsPresContext::CSSPixelsToAppUnits(1);nscoordoneDevPixel=aPresContext->DevPixelsToAppUnits(1);gfxRectfocusRect(RectToGfxRect(aFocusRect,oneDevPixel));gfxCornerSizesfocusRadii;{nscoordtwipsRadii[8]={0,0,0,0,0,0,0,0};ComputePixelRadii(twipsRadii,oneDevPixel,&focusRadii);}gfxFloatfocusWidths[4]={gfxFloat(oneCSSPixel/oneDevPixel),gfxFloat(oneCSSPixel/oneDevPixel),gfxFloat(oneCSSPixel/oneDevPixel),gfxFloat(oneCSSPixel/oneDevPixel)};PRUint8focusStyles[4]={NS_STYLE_BORDER_STYLE_DOTTED,NS_STYLE_BORDER_STYLE_DOTTED,NS_STYLE_BORDER_STYLE_DOTTED,NS_STYLE_BORDER_STYLE_DOTTED};nscolorfocusColors[4]={aColor,aColor,aColor,aColor};gfxContext*ctx=aRenderingContext.ThebesContext();ctx->Save();// Because this renders a dotted border, the background color// should not be used. Therefore, we provide a value that will// be blatantly wrong if it ever does get used. (If this becomes// something that CSS can style, this function will then have access// to a style context and can use the same logic that PaintBorder// and PaintOutline do.)nsCSSBorderRendererbr(oneDevPixel,ctx,focusRect,focusStyles,focusWidths,focusRadii,focusColors,nsnull,0,NS_RGB(255,0,0));br.DrawBorders();ctx->Restore();SN();}// Thebes Border Rendering Code End//----------------------------------------------------------------------//----------------------------------------------------------------------/** * Computes the placement of a background image. * * @param aOriginBounds is the box to which the tiling position should be * relative * This should correspond to 'background-origin' for the frame, * except when painting on the canvas, in which case the origin bounds * should be the bounds of the root element's frame. * @param aTopLeft the top-left corner where an image tile should be drawn * @param aAnchorPoint a point which should be pixel-aligned by * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS * specifies a percentage (including 'right' or 'bottom'), in which case * it's that percentage within of aOriginBounds. So 'right' would set * aAnchorPoint.x to aOriginBounds.XMost(). * * Points are returned relative to aOriginBounds. */staticvoidComputeBackgroundAnchorPoint(constnsStyleBackground::Layer&aLayer,constnsSize&aOriginBounds,constnsSize&aImageSize,nsPoint*aTopLeft,nsPoint*aAnchorPoint){if(!aLayer.mPosition.mXIsPercent){aTopLeft->x=aAnchorPoint->x=aLayer.mPosition.mXPosition.mCoord;}else{doublepercent=aLayer.mPosition.mXPosition.mFloat;aAnchorPoint->x=NSToCoordRound(percent*aOriginBounds.width);aTopLeft->x=NSToCoordRound(percent*(aOriginBounds.width-aImageSize.width));}if(!aLayer.mPosition.mYIsPercent){aTopLeft->y=aAnchorPoint->y=aLayer.mPosition.mYPosition.mCoord;}else{doublepercent=aLayer.mPosition.mYPosition.mFloat;aAnchorPoint->y=NSToCoordRound(percent*aOriginBounds.height);aTopLeft->y=NSToCoordRound(percent*(aOriginBounds.height-aImageSize.height));}}nsIFrame*nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame*aFrame,PRBoolaStartAtParent/*= PR_FALSE*/){NS_ASSERTION(aFrame,"Cannot find NonTransparentBackgroundFrame in a null frame");nsIFrame*frame=nsnull;if(aStartAtParent){frame=nsLayoutUtils::GetParentOrPlaceholderFor(aFrame->PresContext()->FrameManager(),aFrame);}if(!frame){frame=aFrame;}while(frame){// No need to call GetVisitedDependentColor because it always uses// this alpha component anyway.if(NS_GET_A(frame->GetStyleBackground()->mBackgroundColor)>0)break;if(frame->IsThemed())break;nsIFrame*parent=nsLayoutUtils::GetParentOrPlaceholderFor(frame->PresContext()->FrameManager(),frame);if(!parent)break;frame=parent;}returnframe;}// Returns true if aFrame is a canvas frame.// We need to treat the viewport as canvas because, even though// it does not actually paint a background, we need to get the right// background style so we correctly detect transparent documents.PRBoolnsCSSRendering::IsCanvasFrame(nsIFrame*aFrame){nsIAtom*frameType=aFrame->GetType();returnframeType==nsGkAtoms::canvasFrame||frameType==nsGkAtoms::rootFrame||frameType==nsGkAtoms::pageFrame||frameType==nsGkAtoms::pageContentFrame||frameType==nsGkAtoms::viewportFrame;}nsIFrame*nsCSSRendering::FindBackgroundStyleFrame(nsIFrame*aForFrame){constnsStyleBackground*result=aForFrame->GetStyleBackground();// Check if we need to do propagation from BODY rather than HTML.if(result->IsTransparent()){nsIContent*content=aForFrame->GetContent();// The root element content can't be null. We wouldn't know what// frame to create for aFrame.// Use |GetOwnerDoc| so it works during destruction.if(content){nsIDocument*document=content->GetOwnerDoc();nsCOMPtr<nsIHTMLDocument>htmlDoc=do_QueryInterface(document);if(htmlDoc){nsIContent*bodyContent=htmlDoc->GetBodyContentExternal();// We need to null check the body node (bug 118829) since// there are cases, thanks to the fix for bug 5569, where we// will reflow a document with no body. In particular, if a// SCRIPT element in the head blocks the parser and then has a// SCRIPT that does "document.location.href = 'foo'", then// nsParser::Terminate will call |DidBuildModel| methods// through to the content sink, which will call |StartLayout|// and thus |InitialReflow| on the pres shell. See bug 119351// for the ugly details.if(bodyContent){nsIFrame*bodyFrame=bodyContent->GetPrimaryFrame();if(bodyFrame){returnnsLayoutUtils::GetStyleFrame(bodyFrame);}}}}}returnaForFrame;}/** * |FindBackground| finds the correct style data to use to paint the * background. It is responsible for handling the following two * statements in section 14.2 of CSS2: * * The background of the box generated by the root element covers the * entire canvas. * * For HTML documents, however, we recommend that authors specify the * background for the BODY element rather than the HTML element. User * agents should observe the following precedence rules to fill in the * background: if the value of the 'background' property for the HTML * element is different from 'transparent' then use it, else use the * value of the 'background' property for the BODY element. If the * resulting value is 'transparent', the rendering is undefined. * * Thus, in our implementation, it is responsible for ensuring that: * + we paint the correct background on the |nsCanvasFrame|, * |nsRootBoxFrame|, or |nsPageFrame|, * + we don't paint the background on the root element, and * + we don't paint the background on the BODY element in *some* cases, * and for SGML-based HTML documents only. * * |FindBackground| returns true if a background should be painted, and * the resulting style context to use for the background information * will be filled in to |aBackground|. */nsStyleContext*nsCSSRendering::FindRootFrameBackground(nsIFrame*aForFrame){returnFindBackgroundStyleFrame(aForFrame)->GetStyleContext();}inlinePRBoolFindElementBackground(nsIFrame*aForFrame,nsIFrame*aRootElementFrame,nsStyleContext**aBackgroundSC){if(aForFrame==aRootElementFrame){// We must have propagated our background to the viewport or canvas. Abort.returnPR_FALSE;}*aBackgroundSC=aForFrame->GetStyleContext();// Return true unless the frame is for a BODY element whose background// was propagated to the viewport.nsIContent*content=aForFrame->GetContent();if(!content||content->Tag()!=nsGkAtoms::body)returnPR_TRUE;// not frame for a "body" element// It could be a non-HTML "body" element but that's OK, we'd fail the// bodyContent check belowif(aForFrame->GetStyleContext()->GetPseudo())returnPR_TRUE;// A pseudo-element frame.// We should only look at the <html> background if we're in an HTML documentnsIDocument*document=content->GetOwnerDoc();nsCOMPtr<nsIHTMLDocument>htmlDoc=do_QueryInterface(document);if(!htmlDoc)returnPR_TRUE;nsIContent*bodyContent=htmlDoc->GetBodyContentExternal();if(bodyContent!=content)returnPR_TRUE;// this wasn't the background that was propagated// This can be called even when there's no root element yet, during frame// construction, via nsLayoutUtils::FrameHasTransparency and// nsContainerFrame::SyncFrameViewProperties.if(!aRootElementFrame)returnPR_TRUE;constnsStyleBackground*htmlBG=aRootElementFrame->GetStyleBackground();return!htmlBG->IsTransparent();}PRBoolnsCSSRendering::FindBackground(nsPresContext*aPresContext,nsIFrame*aForFrame,nsStyleContext**aBackgroundSC){nsIFrame*rootElementFrame=aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();if(IsCanvasFrame(aForFrame)){*aBackgroundSC=FindCanvasBackground(aForFrame,rootElementFrame);returnPR_TRUE;}else{returnFindElementBackground(aForFrame,rootElementFrame,aBackgroundSC);}}voidnsCSSRendering::DidPaint(){gInlineBGData->Reset();}voidnsCSSRendering::PaintBoxShadowOuter(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aFrameArea,constnsRect&aDirtyRect){constnsStyleBorder*styleBorder=aForFrame->GetStyleBorder();nsCSSShadowArray*shadows=styleBorder->mBoxShadow;if(!shadows)return;nscoordtwipsPerPixel=aPresContext->DevPixelsToAppUnits(1);PRBoolhasBorderRadius;PRBoolnativeTheme;// mutually exclusive with hasBorderRadiusgfxCornerSizesborderRadii;// Get any border radius, since box-shadow must also have rounded corners if the frame doesconstnsStyleDisplay*styleDisplay=aForFrame->GetStyleDisplay();nsITheme::Transparencytransparency;if(aForFrame->IsThemed(styleDisplay,&transparency)){// We don't respect border-radius for native-themed widgetshasBorderRadius=PR_FALSE;// For opaque (rectangular) theme widgets we can take the generic// border-box path with border-radius disabled.nativeTheme=transparency!=nsITheme::eOpaque;}else{nativeTheme=PR_FALSE;nscoordtwipsRadii[8];NS_ASSERTION(aFrameArea.Size()==aForFrame->GetSize(),"unexpected size");hasBorderRadius=aForFrame->GetBorderRadii(twipsRadii);if(hasBorderRadius){ComputePixelRadii(twipsRadii,twipsPerPixel,&borderRadii);}}nsRectframeRect=nativeTheme?aForFrame->GetOverflowRectRelativeToSelf()+aFrameArea.TopLeft():aFrameArea;gfxRectframeGfxRect=RectToGfxRect(frameRect,twipsPerPixel);frameGfxRect.Round();// We don't show anything that intersects with the frame we're blurring on. So tell the// blurrer not to do unnecessary work there.gfxRectskipGfxRect=frameGfxRect;PRBooluseSkipGfxRect=PR_TRUE;if(nativeTheme){// Optimize non-leaf native-themed frames by skipping computing pixels// in the padding-box. We assume the padding-box is going to be painted// opaquely for non-leaf frames.// XXX this may not be a safe assumption; we should make this go away// by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.useSkipGfxRect=!aForFrame->IsLeaf();nsRectpaddingRect=aForFrame->GetPaddingRect()-aForFrame->GetPosition()+aFrameArea.TopLeft();skipGfxRect=RectToGfxRect(paddingRect,twipsPerPixel);}elseif(hasBorderRadius){skipGfxRect.Inset(PR_MAX(borderRadii[C_TL].height,borderRadii[C_TR].height),0,PR_MAX(borderRadii[C_BL].height,borderRadii[C_BR].height),0);}for(PRUint32i=shadows->Length();i>0;--i){nsCSSShadowItem*shadowItem=shadows->ShadowAt(i-1);if(shadowItem->mInset)continue;nsRectshadowRect=frameRect;shadowRect.MoveBy(shadowItem->mXOffset,shadowItem->mYOffset);nscoordpixelSpreadRadius;if(nativeTheme){pixelSpreadRadius=shadowItem->mSpread;}else{shadowRect.Inflate(shadowItem->mSpread,shadowItem->mSpread);pixelSpreadRadius=0;}// shadowRect won't include the blur, so make an extra rect here that includes the blur// for use in the even-odd rule below.nsRectshadowRectPlusBlur=shadowRect;nscoordblurRadius=shadowItem->mRadius;shadowRectPlusBlur.Inflate(blurRadius,blurRadius);gfxRectshadowGfxRect=RectToGfxRect(shadowRect,twipsPerPixel);gfxRectshadowGfxRectPlusBlur=RectToGfxRect(shadowRectPlusBlur,twipsPerPixel);shadowGfxRect.Round();shadowGfxRectPlusBlur.RoundOut();gfxContext*renderContext=aRenderingContext.ThebesContext();nsRefPtr<gfxContext>shadowContext;nsContextBoxBlurblurringArea;// When getting the widget shape from the native theme, we're going// to draw the widget into the shadow surface to create a mask.// We need to ensure that there actually *is* a shadow surface// and that we're not going to draw directly into renderContext.shadowContext=blurringArea.Init(shadowRect,pixelSpreadRadius,blurRadius,twipsPerPixel,renderContext,aDirtyRect,useSkipGfxRect?&skipGfxRect:nsnull,nativeTheme?nsContextBoxBlur::FORCE_MASK:0);if(!shadowContext)continue;// Set the shadow color; if not specified, use the foreground colornscolorshadowColor;if(shadowItem->mHasColor)shadowColor=shadowItem->mColor;elseshadowColor=aForFrame->GetStyleColor()->mColor;renderContext->Save();renderContext->SetColor(gfxRGBA(shadowColor));// Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur// doesn't make any temporary surfaces if blur is 0 and it just returns the original// surface? If we have no blur, we're painting this fill on the actual content surface// (renderContext == shadowContext) which is why we set up the color and clip// before doing this.if(nativeTheme){// We don't clip the border-box from the shadow, nor any other box.// We assume that the native theme is going to paint over the shadow.// Draw the widget shapegfxContextMatrixAutoSaveRestoresave(shadowContext);nsIDeviceContext*devCtx=aPresContext->DeviceContext();nsCOMPtr<nsIRenderingContext>wrapperCtx;devCtx->CreateRenderingContextInstance(*getter_AddRefs(wrapperCtx));wrapperCtx->Init(devCtx,shadowContext);wrapperCtx->Translate(shadowItem->mXOffset,shadowItem->mYOffset);aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx,aForFrame,styleDisplay->mAppearance,aFrameArea,frameRect);}else{// Clip out the area of the actual frame so the shadow is not shown within// the framerenderContext->NewPath();renderContext->Rectangle(shadowGfxRectPlusBlur);if(hasBorderRadius){renderContext->RoundedRectangle(frameGfxRect,borderRadii);}else{renderContext->Rectangle(frameGfxRect);}renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);renderContext->Clip();shadowContext->NewPath();if(hasBorderRadius){gfxCornerSizesclipRectRadii;gfxFloatspreadDistance=-shadowItem->mSpread/twipsPerPixel;gfxFloatborderSizes[4]={0,0,0,0};// We only give the spread radius to corners with a radius on them, otherwise we'll// give a rounded shadow corner to a frame corner with 0 border radius, should// the author use non-uniform border radii sizes (-moz-border-radius-topleft etc)// (bug 514670)if(borderRadii[C_TL].width>0||borderRadii[C_BL].width>0){borderSizes[NS_SIDE_LEFT]=spreadDistance;}if(borderRadii[C_TL].height>0||borderRadii[C_TR].height>0){borderSizes[NS_SIDE_TOP]=spreadDistance;}if(borderRadii[C_TR].width>0||borderRadii[C_BR].width>0){borderSizes[NS_SIDE_RIGHT]=spreadDistance;}if(borderRadii[C_BL].height>0||borderRadii[C_BR].height>0){borderSizes[NS_SIDE_BOTTOM]=spreadDistance;}nsCSSBorderRenderer::ComputeInnerRadii(borderRadii,borderSizes,&clipRectRadii);shadowContext->RoundedRectangle(shadowGfxRect,clipRectRadii);}else{shadowContext->Rectangle(shadowGfxRect);}shadowContext->Fill();}blurringArea.DoPaint();renderContext->Restore();}}voidnsCSSRendering::PaintBoxShadowInner(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aFrameArea,constnsRect&aDirtyRect){constnsStyleBorder*styleBorder=aForFrame->GetStyleBorder();nsCSSShadowArray*shadows=styleBorder->mBoxShadow;if(!shadows)return;if(aForFrame->IsThemed()&&aForFrame->GetContent()&&!nsContentUtils::IsChromeDoc(aForFrame->GetContent()->GetCurrentDoc())){// There's no way of getting hold of a shape corresponding to a// "padding-box" for native-themed widgets, so just don't draw// inner box-shadows for them. But we allow chrome to paint inner// box shadows since chrome can be aware of the platform theme.return;}// Get any border radius, since box-shadow must also have rounded corners if the frame doesnscoordtwipsRadii[8];NS_ASSERTION(aFrameArea.Size()==aForFrame->GetSize(),"unexpected size");PRBoolhasBorderRadius=aForFrame->GetBorderRadii(twipsRadii);nscoordtwipsPerPixel=aPresContext->DevPixelsToAppUnits(1);nsRectpaddingRect=aFrameArea;nsMarginborder=aForFrame->GetUsedBorder();aForFrame->ApplySkipSides(border);paddingRect.Deflate(border);gfxCornerSizesinnerRadii;if(hasBorderRadius){gfxCornerSizesborderRadii;ComputePixelRadii(twipsRadii,twipsPerPixel,&borderRadii);gfxFloatborderSizes[4]={gfxFloat(border.top/twipsPerPixel),gfxFloat(border.right/twipsPerPixel),gfxFloat(border.bottom/twipsPerPixel),gfxFloat(border.left/twipsPerPixel)};nsCSSBorderRenderer::ComputeInnerRadii(borderRadii,borderSizes,&innerRadii);}for(PRUint32i=shadows->Length();i>0;--i){nsCSSShadowItem*shadowItem=shadows->ShadowAt(i-1);if(!shadowItem->mInset)continue;/* * shadowRect: the frame's padding rect * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect * so that blurs still happen properly near the edges * shadowClipRect: the area on the temporary surface within shadowPaintRect * that we will NOT paint in */nscoordblurRadius=shadowItem->mRadius;nsRectshadowPaintRect=paddingRect;shadowPaintRect.Inflate(blurRadius,blurRadius);nsRectshadowClipRect=paddingRect;shadowClipRect.MoveBy(shadowItem->mXOffset,shadowItem->mYOffset);shadowClipRect.Deflate(shadowItem->mSpread,shadowItem->mSpread);gfxCornerSizesclipRectRadii;if(hasBorderRadius){// Calculate the radii the inner clipping rect will havegfxFloatspreadDistance=shadowItem->mSpread/twipsPerPixel;gfxFloatborderSizes[4]={0,0,0,0};// See PaintBoxShadowOuter and bug 514670if(innerRadii[C_TL].width>0||innerRadii[C_BL].width>0){borderSizes[NS_SIDE_LEFT]=spreadDistance;}if(innerRadii[C_TL].height>0||innerRadii[C_TR].height>0){borderSizes[NS_SIDE_TOP]=spreadDistance;}if(innerRadii[C_TR].width>0||innerRadii[C_BR].width>0){borderSizes[NS_SIDE_RIGHT]=spreadDistance;}if(innerRadii[C_BL].height>0||innerRadii[C_BR].height>0){borderSizes[NS_SIDE_BOTTOM]=spreadDistance;}nsCSSBorderRenderer::ComputeInnerRadii(innerRadii,borderSizes,&clipRectRadii);}// Set the "skip rect" to the area within the frame that we don't paint in,// including after blurring. We also use this for clipping later on.nsRectskipRect=shadowClipRect;skipRect.Deflate(blurRadius,blurRadius);gfxRectskipGfxRect=RectToGfxRect(skipRect,twipsPerPixel);if(hasBorderRadius){skipGfxRect.Inset(PR_MAX(clipRectRadii[C_TL].height,clipRectRadii[C_TR].height),0,PR_MAX(clipRectRadii[C_BL].height,clipRectRadii[C_BR].height),0);}gfxContext*renderContext=aRenderingContext.ThebesContext();nsRefPtr<gfxContext>shadowContext;nsContextBoxBlurblurringArea;shadowContext=blurringArea.Init(shadowPaintRect,0,blurRadius,twipsPerPixel,renderContext,aDirtyRect,&skipGfxRect);if(!shadowContext)continue;// Set the shadow color; if not specified, use the foreground colornscolorshadowColor;if(shadowItem->mHasColor)shadowColor=shadowItem->mColor;elseshadowColor=aForFrame->GetStyleColor()->mColor;renderContext->Save();renderContext->SetColor(gfxRGBA(shadowColor));// Clip the context to the area of the frame's padding rect, so no part of the// shadow is painted outside. Also cut out anything beyond where the inset shadow// will be.gfxRectshadowGfxRect=RectToGfxRect(paddingRect,twipsPerPixel);shadowGfxRect.Round();renderContext->NewPath();if(hasBorderRadius)renderContext->RoundedRectangle(shadowGfxRect,innerRadii,PR_FALSE);elserenderContext->Rectangle(shadowGfxRect);renderContext->Rectangle(skipGfxRect);renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);renderContext->Clip();// Fill the temporary surface minus the area within the frame that we should// not paint in, and blur and apply itgfxRectshadowPaintGfxRect=RectToGfxRect(shadowPaintRect,twipsPerPixel);shadowPaintGfxRect.RoundOut();gfxRectshadowClipGfxRect=RectToGfxRect(shadowClipRect,twipsPerPixel);shadowClipGfxRect.Round();shadowContext->NewPath();shadowContext->Rectangle(shadowPaintGfxRect);if(hasBorderRadius)shadowContext->RoundedRectangle(shadowClipGfxRect,clipRectRadii,PR_FALSE);elseshadowContext->Rectangle(shadowClipGfxRect);shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);shadowContext->Fill();blurringArea.DoPaint();renderContext->Restore();}}voidnsCSSRendering::PaintBackground(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aDirtyRect,constnsRect&aBorderArea,PRUint32aFlags,nsRect*aBGClipRect){NS_PRECONDITION(aForFrame,"Frame is expected to be provided to PaintBackground");nsStyleContext*sc;if(!FindBackground(aPresContext,aForFrame,&sc)){// We don't want to bail out if moz-appearance is set on a root// node. If it has a parent content node, bail because it's not// a root, other wise keep going in order to let the theme stuff// draw the background. The canvas really should be drawing the// bg, but there's no way to hook that up via css.if(!aForFrame->GetStyleDisplay()->mAppearance){return;}nsIContent*content=aForFrame->GetContent();if(!content||content->GetParent()){return;}sc=aForFrame->GetStyleContext();}PaintBackgroundWithSC(aPresContext,aRenderingContext,aForFrame,aDirtyRect,aBorderArea,sc,*aForFrame->GetStyleBorder(),aFlags,aBGClipRect);}staticPRBoolIsOpaqueBorderEdge(constnsStyleBorder&aBorder,mozilla::css::SideaSide){if(aBorder.GetActualBorder().side(aSide)==0)returnPR_TRUE;switch(aBorder.GetBorderStyle(aSide)){caseNS_STYLE_BORDER_STYLE_SOLID:caseNS_STYLE_BORDER_STYLE_GROOVE:caseNS_STYLE_BORDER_STYLE_RIDGE:caseNS_STYLE_BORDER_STYLE_INSET:caseNS_STYLE_BORDER_STYLE_OUTSET:break;default:returnPR_FALSE;}// If we're using a border image, assume it's not fully opaque,// because we may not even have the image loaded at this point, and// even if we did, checking whether the relevant tile is fully// opaque would be too much work.if(aBorder.GetBorderImage())returnPR_FALSE;nscolorcolor;PRBoolisForeground;aBorder.GetBorderColor(aSide,color,isForeground);// We don't know the foreground color here, so if it's being used// we must assume it might be transparent.if(isForeground)returnPR_FALSE;returnNS_GET_A(color)==255;}/** * Returns true if all border edges are either missing or opaque. */staticPRBoolIsOpaqueBorder(constnsStyleBorder&aBorder){if(aBorder.mBorderColors)returnPR_FALSE;NS_FOR_CSS_SIDES(i){if(!IsOpaqueBorderEdge(aBorder,i))returnPR_FALSE;}returnPR_TRUE;}staticinlinevoidSetupDirtyRects(constnsRect&aBGClipArea,constnsRect&aCallerDirtyRect,nscoordaAppUnitsPerPixel,/* OUT: */nsRect*aDirtyRect,gfxRect*aDirtyRectGfx){aDirtyRect->IntersectRect(aBGClipArea,aCallerDirtyRect);// Compute the Thebes equivalent of the dirtyRect.*aDirtyRectGfx=RectToGfxRect(*aDirtyRect,aAppUnitsPerPixel);NS_WARN_IF_FALSE(aDirtyRect->IsEmpty()||!aDirtyRectGfx->IsEmpty(),"converted dirty rect should not be empty");NS_ABORT_IF_FALSE(!aDirtyRect->IsEmpty()||aDirtyRectGfx->IsEmpty(),"second should be empty if first is");}staticvoidSetupBackgroundClip(gfxContext*aCtx,PRUint8aBackgroundClip,nsIFrame*aForFrame,constnsRect&aBorderArea,constnsRect&aCallerDirtyRect,PRBoolaHaveRoundedCorners,constgfxCornerSizes&aBGRadii,nscoordaAppUnitsPerPixel,gfxContextAutoSaveRestore*aAutoSR,/* OUT: */nsRect*aBGClipArea,nsRect*aDirtyRect,gfxRect*aDirtyRectGfx){*aBGClipArea=aBorderArea;PRBoolradiiAreOuter=PR_TRUE;gfxCornerSizesclippedRadii=aBGRadii;if(aBackgroundClip!=NS_STYLE_BG_CLIP_BORDER){nsMarginborder=aForFrame->GetUsedBorder();if(aBackgroundClip!=NS_STYLE_BG_CLIP_PADDING){NS_ASSERTION(aBackgroundClip==NS_STYLE_BG_CLIP_CONTENT,"unexpected background-clip");border+=aForFrame->GetUsedPadding();}aForFrame->ApplySkipSides(border);aBGClipArea->Deflate(border);if(aHaveRoundedCorners){gfxFloatborderSizes[4]={gfxFloat(border.top/aAppUnitsPerPixel),gfxFloat(border.right/aAppUnitsPerPixel),gfxFloat(border.bottom/aAppUnitsPerPixel),gfxFloat(border.left/aAppUnitsPerPixel)};nsCSSBorderRenderer::ComputeInnerRadii(aBGRadii,borderSizes,&clippedRadii);radiiAreOuter=PR_FALSE;}}SetupDirtyRects(*aBGClipArea,aCallerDirtyRect,aAppUnitsPerPixel,aDirtyRect,aDirtyRectGfx);if(aDirtyRectGfx->IsEmpty()){// Our caller won't draw anything under this condition, so no need// to set more up.return;}// If we have rounded corners, clip all subsequent drawing to the// rounded rectangle defined by bgArea and bgRadii (we don't know// whether the rounded corners intrude on the dirtyRect or not).// Do not do this if we have a caller-provided clip rect --// as above with bgArea, arguably a bug, but table painting seems// to depend on it.if(aHaveRoundedCorners){gfxRectbgAreaGfx(RectToGfxRect(*aBGClipArea,aAppUnitsPerPixel));bgAreaGfx.Round();bgAreaGfx.Condition();if(bgAreaGfx.IsEmpty()){// I think it's become possible to hit this since// http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.NS_WARNING("converted background area should not be empty");// Make our caller not do anything.aDirtyRectGfx->size.SizeTo(0.0,0.0);return;}aAutoSR->Reset(aCtx);aCtx->NewPath();aCtx->RoundedRectangle(bgAreaGfx,clippedRadii,radiiAreOuter);aCtx->Clip();}}staticnscolorDetermineBackgroundColorInternal(nsPresContext*aPresContext,nsStyleContext*aStyleContext,nsIFrame*aFrame,PRBool&aDrawBackgroundImage,PRBool&aDrawBackgroundColor){aDrawBackgroundImage=PR_TRUE;aDrawBackgroundColor=PR_TRUE;if(aFrame->HonorPrintBackgroundSettings()){aDrawBackgroundImage=aPresContext->GetBackgroundImageDraw();aDrawBackgroundColor=aPresContext->GetBackgroundColorDraw();}nscolorbgColor;if(aDrawBackgroundColor){bgColor=aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);if(NS_GET_A(bgColor)==0)aDrawBackgroundColor=PR_FALSE;}else{// If GetBackgroundColorDraw() is false, we are still expected to// draw color in the background of any frame that's not completely// transparent, but we are expected to use white instead of whatever// color was specified.bgColor=NS_RGB(255,255,255);if(aDrawBackgroundImage||!aStyleContext->GetStyleBackground()->IsTransparent())aDrawBackgroundColor=PR_TRUE;elsebgColor=NS_RGBA(0,0,0,0);}returnbgColor;}nscolornsCSSRendering::DetermineBackgroundColor(nsPresContext*aPresContext,nsStyleContext*aStyleContext,nsIFrame*aFrame){PRBooldrawBackgroundImage;PRBooldrawBackgroundColor;returnDetermineBackgroundColorInternal(aPresContext,aStyleContext,aFrame,drawBackgroundImage,drawBackgroundColor);}staticgfxFloatConvertGradientValueToPixels(constnsStyleCoord&aCoord,gfxFloataFillLength,PRInt32aAppUnitsPerPixel){switch(aCoord.GetUnit()){caseeStyleUnit_Percent:returnaCoord.GetPercentValue()*aFillLength;caseeStyleUnit_Coord:returnNSAppUnitsToFloatPixels(aCoord.GetCoordValue(),aAppUnitsPerPixel);default:NS_WARNING("Unexpected coord unit");return0;}}// Given a box with size aBoxSize and origin (0,0), and an angle aAngle,// and a starting point for the gradient line aStart, find the endpoint of// the gradient line --- the intersection of the gradient line with a line// perpendicular to aAngle that passes through the farthest corner in the// direction aAngle.staticgfxPointComputeGradientLineEndFromAngle(constgfxPoint&aStart,doubleaAngle,constgfxSize&aBoxSize){doubledx=cos(-aAngle);doubledy=sin(-aAngle);gfxPointfarthestCorner(dx>0?aBoxSize.width:0,dy>0?aBoxSize.height:0);gfxPointdelta=farthestCorner-aStart;doubleu=delta.x*dy-delta.y*dx;returnfarthestCorner+gfxPoint(-u*dy,u*dx);}// Compute the start and end points of the gradient line for a linear gradient.staticvoidComputeLinearGradientLine(nsPresContext*aPresContext,nsStyleGradient*aGradient,constgfxSize&aBoxSize,gfxPoint*aLineStart,gfxPoint*aLineEnd){if(aGradient->mBgPosX.GetUnit()==eStyleUnit_None){doubleangle;if(aGradient->mAngle.IsAngleValue()){angle=aGradient->mAngle.GetAngleValueInRadians();}else{angle=-M_PI_2;// defaults to vertical gradient starting from top}gfxPointcenter(aBoxSize.width/2,aBoxSize.height/2);*aLineEnd=ComputeGradientLineEndFromAngle(center,angle,aBoxSize);*aLineStart=gfxPoint(aBoxSize.width,aBoxSize.height)-*aLineEnd;}else{PRInt32appUnitsPerPixel=aPresContext->AppUnitsPerDevPixel();*aLineStart=gfxPoint(ConvertGradientValueToPixels(aGradient->mBgPosX,aBoxSize.width,appUnitsPerPixel),ConvertGradientValueToPixels(aGradient->mBgPosY,aBoxSize.height,appUnitsPerPixel));if(aGradient->mAngle.IsAngleValue()){doubleangle=aGradient->mAngle.GetAngleValueInRadians();*aLineEnd=ComputeGradientLineEndFromAngle(*aLineStart,angle,aBoxSize);}else{// No angle, the line end is just the reflection of the start point// through the center of the box*aLineEnd=gfxPoint(aBoxSize.width,aBoxSize.height)-*aLineStart;}}}// Compute the start and end points of the gradient line for a radial gradient.// Also returns the horizontal and vertical radii defining the circle or// ellipse to use.staticvoidComputeRadialGradientLine(nsPresContext*aPresContext,nsStyleGradient*aGradient,constgfxSize&aBoxSize,gfxPoint*aLineStart,gfxPoint*aLineEnd,double*aRadiusX,double*aRadiusY){if(aGradient->mBgPosX.GetUnit()==eStyleUnit_None){// Default line start point is the center of the box*aLineStart=gfxPoint(aBoxSize.width/2,aBoxSize.height/2);}else{PRInt32appUnitsPerPixel=aPresContext->AppUnitsPerDevPixel();*aLineStart=gfxPoint(ConvertGradientValueToPixels(aGradient->mBgPosX,aBoxSize.width,appUnitsPerPixel),ConvertGradientValueToPixels(aGradient->mBgPosY,aBoxSize.height,appUnitsPerPixel));}// Compute gradient shape: the x and y radii of an ellipse.doubleradiusX,radiusY;doubleleftDistance=PR_ABS(aLineStart->x);doublerightDistance=PR_ABS(aBoxSize.width-aLineStart->x);doubletopDistance=PR_ABS(aLineStart->y);doublebottomDistance=PR_ABS(aBoxSize.height-aLineStart->y);switch(aGradient->mSize){caseNS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE:radiusX=NS_MIN(leftDistance,rightDistance);radiusY=NS_MIN(topDistance,bottomDistance);if(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_CIRCULAR){radiusX=radiusY=NS_MIN(radiusX,radiusY);}break;caseNS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER:{// Compute x and y distances to nearest cornerdoubleoffsetX=NS_MIN(leftDistance,rightDistance);doubleoffsetY=NS_MIN(topDistance,bottomDistance);if(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_CIRCULAR){radiusX=radiusY=NS_hypot(offsetX,offsetY);}else{// maintain aspect ratioradiusX=offsetX*M_SQRT2;radiusY=offsetY*M_SQRT2;}break;}caseNS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE:radiusX=NS_MAX(leftDistance,rightDistance);radiusY=NS_MAX(topDistance,bottomDistance);if(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_CIRCULAR){radiusX=radiusY=NS_MAX(radiusX,radiusY);}break;caseNS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER:{// Compute x and y distances to nearest cornerdoubleoffsetX=NS_MAX(leftDistance,rightDistance);doubleoffsetY=NS_MAX(topDistance,bottomDistance);if(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_CIRCULAR){radiusX=radiusY=NS_hypot(offsetX,offsetY);}else{// maintain aspect ratioradiusX=offsetX*M_SQRT2;radiusY=offsetY*M_SQRT2;}break;}default:NS_ABORT_IF_FALSE(PR_FALSE,"unknown radial gradient sizing method");}*aRadiusX=radiusX;*aRadiusY=radiusY;doubleangle;if(aGradient->mAngle.IsAngleValue()){angle=aGradient->mAngle.GetAngleValueInRadians();}else{// Default angle is 0degangle=0.0;}// The gradient line end point is where the gradient line intersects// the ellipse.*aLineEnd=*aLineStart+gfxPoint(radiusX*cos(-angle),radiusY*sin(-angle));}// A resolved color stop --- with a specific position along the gradient line,// and a Thebes colorstructColorStop{ColorStop(doubleaPosition,nscoloraColor):mPosition(aPosition),mColor(aColor){}doublemPosition;// along the gradient line; 0=start, 1=endgfxRGBAmColor;};// Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done// in unpremultiplied space, which is what SVG gradients and cairo// gradients expect.staticgfxRGBAInterpolateColor(constgfxRGBA&aC1,constgfxRGBA&aC2,doubleaFrac){doubleother=1-aFrac;returngfxRGBA(aC2.r*aFrac+aC1.r*other,aC2.g*aFrac+aC1.g*other,aC2.b*aFrac+aC1.b*other,aC2.a*aFrac+aC1.a*other);}staticnscoordFindTileStart(nscoordaDirtyCoord,nscoordaTilePos,nscoordaTileDim){NS_ASSERTION(aTileDim>0,"Non-positive tile dimension");doublemultiples=NS_floor(double(aDirtyCoord-aTilePos)/aTileDim);returnNSToCoordRound(multiples*aTileDim+aTilePos);}voidnsCSSRendering::PaintGradient(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsStyleGradient*aGradient,constnsRect&aDirtyRect,constnsRect&aOneCellArea,constnsRect&aFillArea){if(aOneCellArea.IsEmpty())return;gfxContext*ctx=aRenderingContext.ThebesContext();nscoordappUnitsPerPixel=aPresContext->AppUnitsPerDevPixel();gfxRectoneCellArea=RectToGfxRect(aOneCellArea,appUnitsPerPixel);// Compute "gradient line" start and end relative to oneCellAreagfxPointlineStart,lineEnd;doubleradiusX=0,radiusY=0;// for radial gradients onlyif(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_LINEAR){ComputeLinearGradientLine(aPresContext,aGradient,oneCellArea.size,&lineStart,&lineEnd);}else{ComputeRadialGradientLine(aPresContext,aGradient,oneCellArea.size,&lineStart,&lineEnd,&radiusX,&radiusY);}gfxFloatlineLength=NS_hypot(lineEnd.x-lineStart.x,lineEnd.y-lineStart.y);NS_ABORT_IF_FALSE(aGradient->mStops.Length()>=2,"The parser should reject gradients with less than two stops");// Build color stop array and compute stop positionsnsTArray<ColorStop>stops;// If there is a run of stops before stop i that did not have specified// positions, then this is the index of the first stop in that run, otherwise// it's -1.PRInt32firstUnsetPosition=-1;for(PRUint32i=0;i<aGradient->mStops.Length();++i){constnsStyleGradientStop&stop=aGradient->mStops[i];doubleposition;switch(stop.mLocation.GetUnit()){caseeStyleUnit_None:if(i==0){// First stop defaults to position 0.0position=0.0;}elseif(i==aGradient->mStops.Length()-1){// Last stop defaults to position 1.0position=1.0;}else{// Other stops with no specified position get their position assigned// later by interpolation, see below.// Remeber where the run of stops with no specified position starts,// if it starts here.if(firstUnsetPosition<0){firstUnsetPosition=i;}stops.AppendElement(ColorStop(0,stop.mColor));continue;}break;caseeStyleUnit_Percent:position=stop.mLocation.GetPercentValue();break;caseeStyleUnit_Coord:position=lineLength<1e-6?0.0:stop.mLocation.GetCoordValue()/appUnitsPerPixel/lineLength;break;default:NS_ABORT_IF_FALSE(PR_FALSE,"Unknown stop position type");}if(i>0){// Prevent decreasing stop positions by advancing this position// to the previous stop position, if necessaryposition=NS_MAX(position,stops[i-1].mPosition);}stops.AppendElement(ColorStop(position,stop.mColor));if(firstUnsetPosition>0){// Interpolate positions for all stops that didn't have a specified positiondoublep=stops[firstUnsetPosition-1].mPosition;doubled=(stops[i].mPosition-p)/(i-firstUnsetPosition+1);for(PRUint32j=firstUnsetPosition;j<i;++j){p+=d;stops[j].mPosition=p;}firstUnsetPosition=-1;}}// Eliminate negative-position stops if the gradient is radial.doublefirstStop=stops[0].mPosition;if(aGradient->mShape!=NS_STYLE_GRADIENT_SHAPE_LINEAR&&firstStop<0.0){if(aGradient->mRepeating){// Choose an instance of the repeated pattern that gives us all positive// stop-offsets.doublelastStop=stops[stops.Length()-1].mPosition;doublestopDelta=lastStop-firstStop;// If all the stops are in approximately the same place then logic below// will kick in that makes us draw just the last stop color, so don't// try to do anything in that case. We certainly need to avoid// dividing by zero.if(stopDelta>=1e-6){doubleinstanceCount=NS_ceil(-firstStop/stopDelta);// Advance stops by instanceCount multiples of the period of the// repeating gradient.doubleoffset=instanceCount*stopDelta;for(PRUint32i=0;i<stops.Length();i++){stops[i].mPosition+=offset;}}}else{// Move negative-position stops to position 0.0. We may also need// to set the color of the stop to the color the gradient should have// at the center of the ellipse.for(PRUint32i=0;i<stops.Length();i++){doublepos=stops[i].mPosition;if(pos<0.0){stops[i].mPosition=0.0;// If this is the last stop, we don't need to adjust the color,// it will fill the entire area.if(i<stops.Length()-1){doublenextPos=stops[i+1].mPosition;// If nextPos is approximately equal to pos, then we don't// need to adjust the color of this stop because it's// not going to be displayed.// If nextPos is negative, we don't need to adjust the color of// this stop since it's not going to be displayed because// nextPos will also be moved to 0.0.if(nextPos>=0.0&&nextPos-pos>=1e-6){// Compute how far the new position 0.0 is along the interval// between pos and nextPos.// XXX Color interpolation (in cairo, too) should use the// CSS 'color-interpolation' property!doublefrac=(0.0-pos)/(nextPos-pos);stops[i].mColor=InterpolateColor(stops[i].mColor,stops[i+1].mColor,frac);}}}}}firstStop=stops[0].mPosition;NS_ABORT_IF_FALSE(firstStop>=0.0,"Failed to fix stop offsets");}doublelastStop=stops[stops.Length()-1].mPosition;// Cairo gradients must have stop positions in the range [0, 1]. So,// stop positions will be normalized below by subtracting firstStop and then// multiplying by stopScale.doublestopScale;doublestopDelta=lastStop-firstStop;if(stopDelta<1e-6||lineLength<1e-6||(aGradient->mShape!=NS_STYLE_GRADIENT_SHAPE_LINEAR&&(radiusX<1e-6||radiusY<1e-6))){// Stops are all at the same place. Map all stops to 0.0.// For radial gradients we need to fill with the last stop color,// so just set both radii to 0.stopScale=0.0;radiusX=radiusY=0.0;lastStop=firstStop;}else{stopScale=1.0/stopDelta;}// Create the gradient pattern.nsRefPtr<gfxPattern>gradientPattern;if(aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_LINEAR){// Compute the actual gradient line ends we need to pass to cairo after// stops have been normalized.gfxPointgradientStart=lineStart+(lineEnd-lineStart)*firstStop;gfxPointgradientEnd=lineStart+(lineEnd-lineStart)*lastStop;if(stopScale==0.0){// Stops are all at the same place. For repeating gradients, this will// just paint the last stop color. We don't need to do anything.// For non-repeating gradients, this should render as two colors, one// on each "side" of the gradient line segment, which is a point. All// our stops will be at 0.0; we just need to set the direction vector// correctly.gradientEnd=gradientStart+(lineEnd-lineStart);}gradientPattern=newgfxPattern(gradientStart.x,gradientStart.y,gradientEnd.x,gradientEnd.y);}else{NS_ASSERTION(firstStop>=0.0,"Negative stops not allowed for radial gradients");// To form an ellipse, we'll stretch a circle vertically, if necessary.// So our radii are based on radiusX.doubleinnerRadius=radiusX*firstStop;doubleouterRadius=radiusX*lastStop;gradientPattern=newgfxPattern(lineStart.x,lineStart.y,innerRadius,lineStart.x,lineStart.y,outerRadius);if(gradientPattern&&radiusX!=radiusY){// Stretch the circles into ellipses vertically by setting a transform// in the pattern.// Recall that this is the transform from user space to pattern space.// So to stretch the ellipse by factor of P vertically, we scale// user coordinates by 1/P.gfxMatrixmatrix;matrix.Translate(lineStart);matrix.Scale(1.0,radiusX/radiusY);matrix.Translate(-lineStart);gradientPattern->SetMatrix(matrix);}}if(!gradientPattern||gradientPattern->CairoStatus())return;// Now set normalized color stops in pattern.if(stopScale==0.0){// Non-repeating linear gradient with all stops in same place -> just add// first stop and last stop, both at position 0.// Repeating or radial gradient with all stops in the same place -> just// paint the last stop color.if(!aGradient->mRepeating&&aGradient->mShape==NS_STYLE_GRADIENT_SHAPE_LINEAR){gradientPattern->AddColorStop(0.0,stops[0].mColor);}gradientPattern->AddColorStop(0.0,stops[stops.Length()-1].mColor);}else{// Use all stopsfor(PRUint32i=0;i<stops.Length();i++){doublepos=stopScale*(stops[i].mPosition-firstStop);gradientPattern->AddColorStop(pos,stops[i].mColor);}}// Set repeat mode. Default cairo extend mode is PAD.if(aGradient->mRepeating){gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);}// Paint gradient tiles. This isn't terribly efficient, but doing it this// way is simple and sure to get pixel-snapping right. We could speed things// up by drawing tiles into temporary surfaces and copying those to the// destination, but after pixel-snapping tiles may not all be the same size.nsRectdirty;if(!dirty.IntersectRect(aDirtyRect,aFillArea))return;gfxRectareaToFill=RectToGfxRect(aFillArea,appUnitsPerPixel);gfxMatrixctm=ctx->CurrentMatrix();// xStart/yStart are the top-left corner of the top-left tile.nscoordxStart=FindTileStart(dirty.x,aOneCellArea.x,aOneCellArea.width);nscoordyStart=FindTileStart(dirty.y,aOneCellArea.y,aOneCellArea.height);nscoordxEnd=dirty.XMost();nscoordyEnd=dirty.YMost();// x and y are the top-left corner of the tile to drawfor(nscoordy=yStart;y<yEnd;y+=aOneCellArea.height){for(nscoordx=xStart;x<xEnd;x+=aOneCellArea.width){// The coordinates of the tilegfxRecttileRect=RectToGfxRect(nsRect(x,y,aOneCellArea.width,aOneCellArea.height),appUnitsPerPixel);// The actual area to fill with this tile is the intersection of this// tile with the overall area we're supposed to be fillinggfxRectfillRect=tileRect.Intersect(areaToFill);ctx->NewPath();ctx->Translate(tileRect.pos);ctx->SetPattern(gradientPattern);ctx->Rectangle(fillRect-tileRect.pos,PR_TRUE);ctx->Fill();ctx->SetMatrix(ctm);}}}voidnsCSSRendering::PaintBackgroundWithSC(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aDirtyRect,constnsRect&aBorderArea,nsStyleContext*aBackgroundSC,constnsStyleBorder&aBorder,PRUint32aFlags,nsRect*aBGClipRect){NS_PRECONDITION(aForFrame,"Frame is expected to be provided to PaintBackground");// Check to see if we have an appearance defined. If so, we let the theme// renderer draw the background and bail out.// XXXzw this ignores aBGClipRect.constnsStyleDisplay*displayData=aForFrame->GetStyleDisplay();if(displayData->mAppearance){nsITheme*theme=aPresContext->GetTheme();if(theme&&theme->ThemeSupportsWidget(aPresContext,aForFrame,displayData->mAppearance)){nsRectdrawing(aBorderArea);theme->GetWidgetOverflow(aPresContext->DeviceContext(),aForFrame,displayData->mAppearance,&drawing);drawing.IntersectRect(drawing,aDirtyRect);theme->DrawWidgetBackground(&aRenderingContext,aForFrame,displayData->mAppearance,aBorderArea,drawing);return;}}// For canvas frames (in the CSS sense) we draw the background color using// a solid color item that gets added in nsLayoutUtils::PaintFrame,// or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid// color may be moved into nsDisplayCanvasBackground by// nsPresShell::AddCanvasBackgroundColorItem, and painted by// nsDisplayCanvasBackground directly.) Either way we don't need to// paint the background color here.PRBoolisCanvasFrame=IsCanvasFrame(aForFrame);// Determine whether we are drawing background images and/or// background colors.PRBooldrawBackgroundImage;PRBooldrawBackgroundColor;nscolorbgColor=DetermineBackgroundColorInternal(aPresContext,aBackgroundSC,aForFrame,drawBackgroundImage,drawBackgroundColor);// At this point, drawBackgroundImage and drawBackgroundColor are// true if and only if we are actually supposed to paint an image or// color into aDirtyRect, respectively.if(!drawBackgroundImage&&!drawBackgroundColor)return;// Compute the outermost boundary of the area that might be painted.gfxContext*ctx=aRenderingContext.ThebesContext();nscoordappUnitsPerPixel=aPresContext->AppUnitsPerDevPixel();// Same coordinate space as aBorderArea & aBGClipRectgfxCornerSizesbgRadii;PRBoolhaveRoundedCorners;{nscoordradii[8];nsSizeframeSize=aForFrame->GetSize();if(&aBorder==aForFrame->GetStyleBorder()&&frameSize==aBorderArea.Size()){haveRoundedCorners=aForFrame->GetBorderRadii(radii);}else{haveRoundedCorners=nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius,frameSize,aBorderArea.Size(),aForFrame->GetSkipSides(),radii);}if(haveRoundedCorners)ComputePixelRadii(radii,appUnitsPerPixel,&bgRadii);}// The 'bgClipArea' (used only by the image tiling logic, far below)// is the caller-provided aBGClipRect if any, or else the area// determined by the value of 'background-clip' in// SetupCurrentBackgroundClip. (Arguably it should be the// intersection, but that breaks the table painter -- in particular,// taking the intersection breaks reftests/bugs/403249-1[ab].)constnsStyleBackground*bg=aBackgroundSC->GetStyleBackground();nsRectbgClipArea,dirtyRect;gfxRectdirtyRectGfx;PRUint8currentBackgroundClip;PRBoolisSolidBorder;gfxContextAutoSaveRestoreautoSR;if(aBGClipRect){bgClipArea=*aBGClipRect;SetupDirtyRects(bgClipArea,aDirtyRect,appUnitsPerPixel,&dirtyRect,&dirtyRectGfx);}else{// The background is rendered over the 'background-clip' area,// which is normally equal to the border area but may be reduced// to the padding area by CSS. Also, if the border is solid, we// don't need to draw outside the padding area. In either case,// if the borders are rounded, make sure we use the same inner// radii as the border code will.// The background-color is drawn based on the bottom// background-clip.currentBackgroundClip=bg->BottomLayer().mClip;isSolidBorder=(aFlags&PAINTBG_WILL_PAINT_BORDER)&&IsOpaqueBorder(aBorder);if(isSolidBorder)currentBackgroundClip=NS_STYLE_BG_CLIP_PADDING;SetupBackgroundClip(ctx,currentBackgroundClip,aForFrame,aBorderArea,aDirtyRect,haveRoundedCorners,bgRadii,appUnitsPerPixel,&autoSR,&bgClipArea,&dirtyRect,&dirtyRectGfx);}// If we might be using a background color, go ahead and set it now.if(drawBackgroundColor&&!isCanvasFrame)ctx->SetColor(gfxRGBA(bgColor));// If there is no background image, draw a color. (If there is// neither a background image nor a color, we wouldn't have gotten// this far.)if(!drawBackgroundImage){if(!dirtyRectGfx.IsEmpty()&&!isCanvasFrame){ctx->NewPath();ctx->Rectangle(dirtyRectGfx,PR_TRUE);ctx->Fill();}return;}// Ensure we get invalidated for loads of the image. We need to do// this here because this might be the only code that knows about the// association of the style data with the frame.aPresContext->SetupBackgroundImageLoaders(aForFrame,bg);// We can skip painting the background color if a background image is opaque.if(drawBackgroundColor&&bg->BottomLayer().mRepeat==NS_STYLE_BG_REPEAT_XY&&bg->BottomLayer().mImage.IsOpaque())drawBackgroundColor=PR_FALSE;// The background color is rendered over the entire dirty area,// even if the image isn't.if(drawBackgroundColor&&!isCanvasFrame){if(!dirtyRectGfx.IsEmpty()){ctx->NewPath();ctx->Rectangle(dirtyRectGfx,PR_TRUE);ctx->Fill();}}if(drawBackgroundImage){NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i,bg){constnsStyleBackground::Layer&layer=bg->mLayers[i];if(!aBGClipRect){PRUint8newBackgroundClip=layer.mClip;if(isSolidBorder&&newBackgroundClip==NS_STYLE_BG_CLIP_BORDER)newBackgroundClip=NS_STYLE_BG_CLIP_PADDING;if(currentBackgroundClip!=newBackgroundClip){currentBackgroundClip=newBackgroundClip;SetupBackgroundClip(ctx,currentBackgroundClip,aForFrame,aBorderArea,aDirtyRect,haveRoundedCorners,bgRadii,appUnitsPerPixel,&autoSR,&bgClipArea,&dirtyRect,&dirtyRectGfx);}}if(!dirtyRectGfx.IsEmpty()){PaintBackgroundLayer(aPresContext,aRenderingContext,aForFrame,aFlags,dirtyRect,aBorderArea,bgClipArea,*bg,layer);}}}}staticinlinefloatScaleDimension(nsStyleBackground::Size::DimensionaDimension,PRUint8aType,nscoordaLength,nscoordaAvailLength){switch(aType){casensStyleBackground::Size::ePercentage:returndouble(aDimension.mFloat)*(double(aAvailLength)/double(aLength));casensStyleBackground::Size::eLength:returndouble(aDimension.mCoord)/double(aLength);default:NS_ABORT_IF_FALSE(PR_FALSE,"bad aDimension.mType");return1.0f;casensStyleBackground::Size::eAuto:NS_ABORT_IF_FALSE(PR_FALSE,"aDimension.mType == eAuto isn't handled");return1.0f;}}staticvoidPaintBackgroundLayer(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,PRUint32aFlags,constnsRect&aDirtyRect,// intersected with aBGClipRectconstnsRect&aBorderArea,constnsRect&aBGClipRect,constnsStyleBackground&aBackground,constnsStyleBackground::Layer&aLayer){/* * The background properties we need to keep in mind when drawing background * layers are: * * background-image * background-repeat * background-attachment * background-position * background-clip * background-origin * background-size * background-break (-moz-background-inline-policy) * * (background-color applies to the entire element and not to individual * layers, so it is irrelevant to this method.) * * These properties have the following dependencies upon each other when * determining rendering: * * background-image * no dependencies * background-repeat * no dependencies * background-attachment * no dependencies * background-position * depends upon background-size (for the image's scaled size) and * background-break (for the background positioning area) * background-clip * no dependencies * background-origin * depends upon background-attachment (only in the case where that value * is 'fixed') * background-size * depends upon background-break (for the background positioning area for * resolving percentages), background-image (for the image's intrinsic * size), background-repeat (if that value is 'round'), and * background-origin (for the background painting area, when * background-repeat is 'round') * background-break * depends upon background-origin (specifying how the boxes making up the * background positioning area are determined) * * As a result of only-if dependencies we don't strictly do a topological * sort of the above properties when processing, but it's pretty close to one: * * background-clip (by caller) * background-image * background-break, background-origin * background-attachment (postfix for background-{origin,break} if 'fixed') * background-size * background-position * background-repeat */PRUint32irFlags=0;if(aFlags&nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES)irFlags|=ImageRenderer::FLAG_SYNC_DECODE_IMAGES;ImageRendererimageRenderer(aForFrame,&aLayer.mImage,irFlags);if(!imageRenderer.PrepareImage()){// There's no image or it's not ready to be painted.return;}// Compute background origin area relative to aBorderArea now as we may need// it to compute the effective image size for a CSS gradient.nsRectbgPositioningArea(0,0,0,0);nsIAtom*frameType=aForFrame->GetType();nsIFrame*geometryFrame=aForFrame;if(frameType==nsGkAtoms::inlineFrame||frameType==nsGkAtoms::positionedInlineFrame){// XXXjwalden Strictly speaking this is not quite faithful to how// background-break is supposed to interact with background-origin values,// but it's a non-trivial amount of work to make it fully conformant, and// until the specification is more finalized (and assuming background-break// even makes the cut) it doesn't make sense to hammer out exact behavior.switch(aBackground.mBackgroundInlinePolicy){caseNS_STYLE_BG_INLINE_POLICY_EACH_BOX:bgPositioningArea=nsRect(nsPoint(0,0),aBorderArea.Size());break;caseNS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:bgPositioningArea=gInlineBGData->GetBoundingRect(aForFrame);break;default:NS_ERROR("Unknown background-inline-policy value! ""Please, teach me what to do.");caseNS_STYLE_BG_INLINE_POLICY_CONTINUOUS:bgPositioningArea=gInlineBGData->GetContinuousRect(aForFrame);break;}}elseif(frameType==nsGkAtoms::canvasFrame){geometryFrame=aForFrame->GetFirstChild(nsnull);// geometryFrame might be null if this canvas is a page created// as an overflow container (e.g. the in-flow content has already// finished and this page only displays the continuations of// absolutely positioned content).if(geometryFrame){bgPositioningArea=geometryFrame->GetRect();}}else{bgPositioningArea=nsRect(nsPoint(0,0),aBorderArea.Size());}// Background images are tiled over the 'background-clip' area// but the origin of the tiling is based on the 'background-origin' areaif(aLayer.mOrigin!=NS_STYLE_BG_ORIGIN_BORDER&&geometryFrame){nsMarginborder=geometryFrame->GetUsedBorder();if(aLayer.mOrigin!=NS_STYLE_BG_ORIGIN_PADDING){border+=geometryFrame->GetUsedPadding();NS_ASSERTION(aLayer.mOrigin==NS_STYLE_BG_ORIGIN_CONTENT,"unknown background-origin value");}geometryFrame->ApplySkipSides(border);bgPositioningArea.Deflate(border);}// For background-attachment:fixed backgrounds, we'll limit the area// where the background can be drawn to the viewport.nsRectbgClipRect=aBGClipRect;// Compute the anchor point.//// relative to aBorderArea.TopLeft() (which is where the top-left// of aForFrame's border-box will be rendered)nsPointimageTopLeft,anchor;if(NS_STYLE_BG_ATTACHMENT_FIXED==aLayer.mAttachment){aPresContext->SetHasFixedBackgroundFrame();// If it's a fixed background attachment, then the image is placed// relative to the viewport, which is the area of the root frame// in a screen context or the page content frame in a print context.nsIFrame*topFrame=aPresContext->PresShell()->FrameManager()->GetRootFrame();NS_ASSERTION(topFrame,"no root frame");nsIFrame*pageContentFrame=nsnull;if(aPresContext->IsPaginated()){pageContentFrame=nsLayoutUtils::GetClosestFrameOfType(aForFrame,nsGkAtoms::pageContentFrame);if(pageContentFrame){topFrame=pageContentFrame;}// else this is an embedded shell and its root frame is what we want}// Set the background positioning area to the viewport's area// (relative to aForFrame)bgPositioningArea=nsRect(-aForFrame->GetOffsetTo(topFrame),topFrame->GetSize());if(!pageContentFrame){// Subtract the size of scrollbars.nsIScrollableFrame*scrollableFrame=aPresContext->PresShell()->GetRootScrollFrameAsScrollable();if(scrollableFrame){nsMarginscrollbars=scrollableFrame->GetActualScrollbarSizes();bgPositioningArea.Deflate(scrollbars);}}if(aRenderingContext.ThebesContext()->GetFlags()&gfxContext::FLAG_DESTINED_FOR_SCREEN){// Clip background-attachment:fixed backgrounds to the viewport, if we're// painting to the screen. This avoids triggering tiling in common cases,// without affecting output since drawing is always clipped to the viewport// when we draw to the screen. (But it's not a pure optimization since it// can affect the values of pixels at the edge of the viewport ---// whether they're sampled from a putative "next tile" or not.)bgClipRect.IntersectRect(bgClipRect,bgPositioningArea+aBorderArea.TopLeft());}}nsSizeimageSize=imageRenderer.ComputeSize(bgPositioningArea.Size());if(imageSize.width<=0||imageSize.height<=0)return;// Scale the image as specified for background-size and as required for// proper background positioning when background-position is defined with// percentages.floatscaleX,scaleY;switch(aLayer.mSize.mWidthType){casensStyleBackground::Size::eContain:casensStyleBackground::Size::eCover:{floatscaleFitX=double(bgPositioningArea.width)/imageSize.width;floatscaleFitY=double(bgPositioningArea.height)/imageSize.height;if(aLayer.mSize.mWidthType==nsStyleBackground::Size::eCover){scaleX=scaleY=NS_MAX(scaleFitX,scaleFitY);}else{scaleX=scaleY=NS_MIN(scaleFitX,scaleFitY);}break;}default:{if(aLayer.mSize.mWidthType==nsStyleBackground::Size::eAuto){if(aLayer.mSize.mHeightType==nsStyleBackground::Size::eAuto){scaleX=scaleY=1.0f;}else{scaleX=scaleY=ScaleDimension(aLayer.mSize.mHeight,aLayer.mSize.mHeightType,imageSize.height,bgPositioningArea.height);}}else{if(aLayer.mSize.mHeightType==nsStyleBackground::Size::eAuto){scaleX=scaleY=ScaleDimension(aLayer.mSize.mWidth,aLayer.mSize.mWidthType,imageSize.width,bgPositioningArea.width);}else{scaleX=ScaleDimension(aLayer.mSize.mWidth,aLayer.mSize.mWidthType,imageSize.width,bgPositioningArea.width);scaleY=ScaleDimension(aLayer.mSize.mHeight,aLayer.mSize.mHeightType,imageSize.height,bgPositioningArea.height);}}break;}}imageSize.width=NSCoordSaturatingNonnegativeMultiply(imageSize.width,scaleX);imageSize.height=NSCoordSaturatingNonnegativeMultiply(imageSize.height,scaleY);// Compute the position of the background now that the background's size is// determined.ComputeBackgroundAnchorPoint(aLayer,bgPositioningArea.Size(),imageSize,&imageTopLeft,&anchor);imageTopLeft+=bgPositioningArea.TopLeft();anchor+=bgPositioningArea.TopLeft();nsRectdestArea(imageTopLeft+aBorderArea.TopLeft(),imageSize);nsRectfillArea=destArea;PRIntnrepeat=aLayer.mRepeat;PR_STATIC_ASSERT(NS_STYLE_BG_REPEAT_XY==(NS_STYLE_BG_REPEAT_X|NS_STYLE_BG_REPEAT_Y));if(repeat&NS_STYLE_BG_REPEAT_X){fillArea.x=bgClipRect.x;fillArea.width=bgClipRect.width;}if(repeat&NS_STYLE_BG_REPEAT_Y){fillArea.y=bgClipRect.y;fillArea.height=bgClipRect.height;}fillArea.IntersectRect(fillArea,bgClipRect);imageRenderer.Draw(aPresContext,aRenderingContext,destArea,fillArea,anchor+aBorderArea.TopLeft(),aDirtyRect);}staticvoidDrawBorderImage(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,constnsRect&aBorderArea,constnsStyleBorder&aStyleBorder,constnsRect&aDirtyRect){if(aDirtyRect.IsEmpty())return;// Ensure we get invalidated for loads and animations of the image.// We need to do this here because this might be the only code that// knows about the association of the style data with the frame.// XXX We shouldn't really... since if anybody is passing in a// different style, they'll potentially have the wrong size for the// border too.aPresContext->SetupBorderImageLoaders(aForFrame,&aStyleBorder);imgIRequest*req=aStyleBorder.GetBorderImage();#ifdef DEBUG{PRUint32status=imgIRequest::STATUS_ERROR;if(req)req->GetImageStatus(&status);NS_ASSERTION(req&&(status&imgIRequest::STATUS_LOAD_COMPLETE),"no image to draw");}#endif// Get the actual image, and determine where the split points are.// Note that mBorderImageSplit is in image pixels, not necessarily// CSS pixels.nsCOMPtr<imgIContainer>imgContainer;req->GetImage(getter_AddRefs(imgContainer));nsIntSizeimageSize;imgContainer->GetWidth(&imageSize.width);imgContainer->GetHeight(&imageSize.height);// Convert percentages and clamp values to the image size.nsIntMarginsplit;NS_FOR_CSS_SIDES(s){nsStyleCoordcoord=aStyleBorder.mBorderImageSplit.Get(s);PRInt32imgDimension=((s==NS_SIDE_TOP||s==NS_SIDE_BOTTOM)?imageSize.height:imageSize.width);doublevalue;switch(coord.GetUnit()){caseeStyleUnit_Percent:value=coord.GetPercentValue()*imgDimension;break;caseeStyleUnit_Factor:value=coord.GetFactorValue();break;default:NS_ASSERTION(coord.GetUnit()==eStyleUnit_Null,"unexpected CSS unit for image split");value=0;break;}if(value<0)value=0;if(value>imgDimension)value=imgDimension;split.side(s)=NS_lround(value);}nsMarginborder(aStyleBorder.GetActualBorder());// These helper tables recharacterize the 'split' and 'border' margins// in a more convenient form: they are the x/y/width/height coords// required for various bands of the border, and they have been transformed// to be relative to the image (for 'split') or the page (for 'border').enum{LEFT,MIDDLE,RIGHT,TOP=LEFT,BOTTOM=RIGHT};constnscoordborderX[3]={aBorderArea.x+0,aBorderArea.x+border.left,aBorderArea.x+aBorderArea.width-border.right,};constnscoordborderY[3]={aBorderArea.y+0,aBorderArea.y+border.top,aBorderArea.y+aBorderArea.height-border.bottom,};constnscoordborderWidth[3]={border.left,aBorderArea.width-border.left-border.right,border.right,};constnscoordborderHeight[3]={border.top,aBorderArea.height-border.top-border.bottom,border.bottom,};constPRInt32splitX[3]={0,split.left,imageSize.width-split.right,};constPRInt32splitY[3]={0,split.top,imageSize.height-split.bottom,};constPRInt32splitWidth[3]={split.left,imageSize.width-split.left-split.right,split.right,};constPRInt32splitHeight[3]={split.top,imageSize.height-split.top-split.bottom,split.bottom,};// In all the 'factor' calculations below, 'border' measurements are// in app units but 'split' measurements are in image/CSS pixels, so// the factor corresponding to no additional scaling is// CSSPixelsToAppUnits(1), not simply 1.for(inti=LEFT;i<=RIGHT;i++){for(intj=TOP;j<=BOTTOM;j++){nsRectdestArea(borderX[i],borderY[j],borderWidth[i],borderHeight[j]);nsIntRectsubArea(splitX[i],splitY[j],splitWidth[i],splitHeight[j]);PRUint8fillStyleH,fillStyleV;nsSizeunitSize;if(i==MIDDLE&&j==MIDDLE){// css-background:// The middle image's width is scaled by the same factor as the// top image unless that factor is zero or infinity, in which// case the scaling factor of the bottom is substituted, and// failing that, the width is not scaled. The height of the// middle image is scaled by the same factor as the left image// unless that factor is zero or infinity, in which case the// scaling factor of the right image is substituted, and failing// that, the height is not scaled.gfxFloathFactor,vFactor;if(0<border.left&&0<split.left)vFactor=gfxFloat(border.left)/split.left;elseif(0<border.right&&0<split.right)vFactor=gfxFloat(border.right)/split.right;elsevFactor=nsPresContext::CSSPixelsToAppUnits(1);if(0<border.top&&0<split.top)hFactor=gfxFloat(border.top)/split.top;elseif(0<border.bottom&&0<split.bottom)hFactor=gfxFloat(border.bottom)/split.bottom;elsehFactor=nsPresContext::CSSPixelsToAppUnits(1);unitSize.width=splitWidth[i]*hFactor;unitSize.height=splitHeight[j]*vFactor;fillStyleH=aStyleBorder.mBorderImageHFill;fillStyleV=aStyleBorder.mBorderImageVFill;}elseif(i==MIDDLE){// top, bottom// Sides are always stretched to the thickness of their border,// and stretched proportionately on the other axis.gfxFloatfactor;if(0<borderHeight[j]&&0<splitHeight[j])factor=gfxFloat(borderHeight[j])/splitHeight[j];elsefactor=nsPresContext::CSSPixelsToAppUnits(1);unitSize.width=splitWidth[i]*factor;unitSize.height=borderHeight[j];fillStyleH=aStyleBorder.mBorderImageHFill;fillStyleV=NS_STYLE_BORDER_IMAGE_STRETCH;}elseif(j==MIDDLE){// left, rightgfxFloatfactor;if(0<borderWidth[i]&&0<splitWidth[i])factor=gfxFloat(borderWidth[i])/splitWidth[i];elsefactor=nsPresContext::CSSPixelsToAppUnits(1);unitSize.width=borderWidth[i];unitSize.height=splitHeight[j]*factor;fillStyleH=NS_STYLE_BORDER_IMAGE_STRETCH;fillStyleV=aStyleBorder.mBorderImageVFill;}else{// Corners are always stretched to fit the corner.unitSize.width=borderWidth[i];unitSize.height=borderHeight[j];fillStyleH=NS_STYLE_BORDER_IMAGE_STRETCH;fillStyleV=NS_STYLE_BORDER_IMAGE_STRETCH;}DrawBorderImageComponent(aRenderingContext,aForFrame,imgContainer,aDirtyRect,destArea,subArea,fillStyleH,fillStyleV,unitSize,aStyleBorder,i*(RIGHT+1)+j);}}}staticvoidDrawBorderImageComponent(nsIRenderingContext&aRenderingContext,nsIFrame*aForFrame,imgIContainer*aImage,constnsRect&aDirtyRect,constnsRect&aFill,constnsIntRect&aSrc,PRUint8aHFill,PRUint8aVFill,constnsSize&aUnitSize,constnsStyleBorder&aStyleBorder,PRUint8aIndex){if(aFill.IsEmpty()||aSrc.IsEmpty())return;// Don't bother trying to cache sub images if the border image is animated// We can only sucessfully call GetAnimated() if we are fully decoded, so default to PR_TRUEPRBoolanimated=PR_TRUE;aImage->GetAnimated(&animated);nsCOMPtr<imgIContainer>subImage;if(animated||(subImage=aStyleBorder.GetSubImage(aIndex))==0){if(NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT,aSrc,imgIContainer::FLAG_SYNC_DECODE,getter_AddRefs(subImage))))return;if(!animated)aStyleBorder.SetSubImage(aIndex,subImage);}gfxPattern::GraphicsFiltergraphicsFilter=nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame);// If we have no tiling in either direction, we can skip the intermediate// scaling step.if((aHFill==NS_STYLE_BORDER_IMAGE_STRETCH&&aVFill==NS_STYLE_BORDER_IMAGE_STRETCH)||(aUnitSize.width==aFill.width&&aUnitSize.height==aFill.height)){nsLayoutUtils::DrawSingleImage(&aRenderingContext,subImage,graphicsFilter,aFill,aDirtyRect,imgIContainer::FLAG_NONE);return;}// Compute the scale and position of the master copy of the image.nsRecttile;switch(aHFill){caseNS_STYLE_BORDER_IMAGE_STRETCH:tile.x=aFill.x;tile.width=aFill.width;break;caseNS_STYLE_BORDER_IMAGE_REPEAT:tile.x=aFill.x+aFill.width/2-aUnitSize.width/2;tile.width=aUnitSize.width;break;caseNS_STYLE_BORDER_IMAGE_ROUND:tile.x=aFill.x;tile.width=aFill.width/ceil(gfxFloat(aFill.width)/aUnitSize.width);break;default:NS_NOTREACHED("unrecognized border-image fill style");}switch(aVFill){caseNS_STYLE_BORDER_IMAGE_STRETCH:tile.y=aFill.y;tile.height=aFill.height;break;caseNS_STYLE_BORDER_IMAGE_REPEAT:tile.y=aFill.y+aFill.height/2-aUnitSize.height/2;tile.height=aUnitSize.height;break;caseNS_STYLE_BORDER_IMAGE_ROUND:tile.y=aFill.y;tile.height=aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);break;default:NS_NOTREACHED("unrecognized border-image fill style");}nsLayoutUtils::DrawImage(&aRenderingContext,subImage,graphicsFilter,tile,aFill,tile.TopLeft(),aDirtyRect,imgIContainer::FLAG_NONE);}// Begin table border-collapsing section// These functions were written to not disrupt the normal ones and yet satisfy some additional requirements// At some point, all functions should be unified to include the additional functionality that these providestaticnscoordRoundIntToPixel(nscoordaValue,nscoordaTwipsPerPixel,PRBoolaRoundDown=PR_FALSE){if(aTwipsPerPixel<=0)// We must be rendering to a device that has a resolution greater than Twips!// In that case, aValue is as accurate as it's going to get.returnaValue;nscoordhalfPixel=NSToCoordRound(aTwipsPerPixel/2.0f);nscoordextra=aValue%aTwipsPerPixel;nscoordfinalValue=(!aRoundDown&&(extra>=halfPixel))?aValue+(aTwipsPerPixel-extra):aValue-extra;returnfinalValue;}staticnscoordRoundFloatToPixel(floataValue,nscoordaTwipsPerPixel,PRBoolaRoundDown=PR_FALSE){returnRoundIntToPixel(NSToCoordRound(aValue),aTwipsPerPixel,aRoundDown);}staticvoidSetPoly(constnsRect&aRect,nsPoint*poly){poly[0].x=aRect.x;poly[0].y=aRect.y;poly[1].x=aRect.x+aRect.width;poly[1].y=aRect.y;poly[2].x=aRect.x+aRect.width;poly[2].y=aRect.y+aRect.height;poly[3].x=aRect.x;poly[3].y=aRect.y+aRect.height;poly[4].x=aRect.x;poly[4].y=aRect.y;}staticvoidDrawSolidBorderSegment(nsIRenderingContext&aContext,nsRectaRect,nscoordaTwipsPerPixel,PRUint8aStartBevelSide=0,nscoordaStartBevelOffset=0,PRUint8aEndBevelSide=0,nscoordaEndBevelOffset=0){if((aRect.width==aTwipsPerPixel)||(aRect.height==aTwipsPerPixel)||((0==aStartBevelOffset)&&(0==aEndBevelOffset))){// simple line or rectangleif((NS_SIDE_TOP==aStartBevelSide)||(NS_SIDE_BOTTOM==aStartBevelSide)){if(1==aRect.height)aContext.DrawLine(aRect.x,aRect.y,aRect.x,aRect.y+aRect.height);elseaContext.FillRect(aRect);}else{if(1==aRect.width)aContext.DrawLine(aRect.x,aRect.y,aRect.x+aRect.width,aRect.y);elseaContext.FillRect(aRect);}}else{// polygon with bevelingnsPointpoly[5];SetPoly(aRect,poly);switch(aStartBevelSide){caseNS_SIDE_TOP:poly[0].x+=aStartBevelOffset;poly[4].x=poly[0].x;break;caseNS_SIDE_BOTTOM:poly[3].x+=aStartBevelOffset;break;caseNS_SIDE_RIGHT:poly[1].y+=aStartBevelOffset;break;caseNS_SIDE_LEFT:poly[0].y+=aStartBevelOffset;poly[4].y=poly[0].y;}switch(aEndBevelSide){caseNS_SIDE_TOP:poly[1].x-=aEndBevelOffset;break;caseNS_SIDE_BOTTOM:poly[2].x-=aEndBevelOffset;break;caseNS_SIDE_RIGHT:poly[2].y-=aEndBevelOffset;break;caseNS_SIDE_LEFT:poly[3].y-=aEndBevelOffset;}aContext.FillPolygon(poly,5);}}staticvoidGetDashInfo(nscoordaBorderLength,nscoordaDashLength,nscoordaTwipsPerPixel,PRInt32&aNumDashSpaces,nscoord&aStartDashLength,nscoord&aEndDashLength){aNumDashSpaces=0;if(aStartDashLength+aDashLength+aEndDashLength>=aBorderLength){aStartDashLength=aBorderLength;aEndDashLength=0;}else{aNumDashSpaces=(aBorderLength-aDashLength)/(2*aDashLength);// round downnscoordextra=aBorderLength-aStartDashLength-aEndDashLength-(((2*aNumDashSpaces)-1)*aDashLength);if(extra>0){nscoordhalf=RoundIntToPixel(extra/2,aTwipsPerPixel);aStartDashLength+=half;aEndDashLength+=(extra-half);}}}voidnsCSSRendering::DrawTableBorderSegment(nsIRenderingContext&aContext,PRUint8aBorderStyle,nscoloraBorderColor,constnsStyleBackground*aBGColor,constnsRect&aBorder,PRInt32aAppUnitsPerCSSPixel,PRUint8aStartBevelSide,nscoordaStartBevelOffset,PRUint8aEndBevelSide,nscoordaEndBevelOffset){aContext.SetColor(aBorderColor);PRBoolhorizontal=((NS_SIDE_TOP==aStartBevelSide)||(NS_SIDE_BOTTOM==aStartBevelSide));nscoordtwipsPerPixel=NSIntPixelsToAppUnits(1,aAppUnitsPerCSSPixel);PRUint8ridgeGroove=NS_STYLE_BORDER_STYLE_RIDGE;if((twipsPerPixel>=aBorder.width)||(twipsPerPixel>=aBorder.height)||(NS_STYLE_BORDER_STYLE_DASHED==aBorderStyle)||(NS_STYLE_BORDER_STYLE_DOTTED==aBorderStyle)){// no beveling for 1 pixel border, dash or dotaStartBevelOffset=0;aEndBevelOffset=0;}gfxContext*ctx=aContext.ThebesContext();gfxContext::AntialiasModeoldMode=ctx->CurrentAntialiasMode();ctx->SetAntialiasMode(gfxContext::MODE_ALIASED);switch(aBorderStyle){caseNS_STYLE_BORDER_STYLE_NONE:caseNS_STYLE_BORDER_STYLE_HIDDEN://NS_ASSERTION(PR_FALSE, "style of none or hidden");break;caseNS_STYLE_BORDER_STYLE_DOTTED:caseNS_STYLE_BORDER_STYLE_DASHED:{nscoorddashLength=(NS_STYLE_BORDER_STYLE_DASHED==aBorderStyle)?DASH_LENGTH:DOT_LENGTH;// make the dash length proportional to the border thicknessdashLength*=(horizontal)?aBorder.height:aBorder.width;// make the min dash length for the ends 1/2 the dash lengthnscoordminDashLength=(NS_STYLE_BORDER_STYLE_DASHED==aBorderStyle)?RoundFloatToPixel(((float)dashLength)/2.0f,twipsPerPixel):dashLength;minDashLength=NS_MAX(minDashLength,twipsPerPixel);nscoordnumDashSpaces=0;nscoordstartDashLength=minDashLength;nscoordendDashLength=minDashLength;if(horizontal){GetDashInfo(aBorder.width,dashLength,twipsPerPixel,numDashSpaces,startDashLength,endDashLength);nsRectrect(aBorder.x,aBorder.y,startDashLength,aBorder.height);DrawSolidBorderSegment(aContext,rect,twipsPerPixel);for(PRInt32spaceX=0;spaceX<numDashSpaces;spaceX++){rect.x+=rect.width+dashLength;rect.width=(spaceX==(numDashSpaces-1))?endDashLength:dashLength;DrawSolidBorderSegment(aContext,rect,twipsPerPixel);}}else{GetDashInfo(aBorder.height,dashLength,twipsPerPixel,numDashSpaces,startDashLength,endDashLength);nsRectrect(aBorder.x,aBorder.y,aBorder.width,startDashLength);DrawSolidBorderSegment(aContext,rect,twipsPerPixel);for(PRInt32spaceY=0;spaceY<numDashSpaces;spaceY++){rect.y+=rect.height+dashLength;rect.height=(spaceY==(numDashSpaces-1))?endDashLength:dashLength;DrawSolidBorderSegment(aContext,rect,twipsPerPixel);}}}break;caseNS_STYLE_BORDER_STYLE_GROOVE:ridgeGroove=NS_STYLE_BORDER_STYLE_GROOVE;// and fall through to ridgecaseNS_STYLE_BORDER_STYLE_RIDGE:if((horizontal&&(twipsPerPixel>=aBorder.height))||(!horizontal&&(twipsPerPixel>=aBorder.width))){// a one pixel borderDrawSolidBorderSegment(aContext,aBorder,twipsPerPixel,aStartBevelSide,aStartBevelOffset,aEndBevelSide,aEndBevelOffset);}else{nscoordstartBevel=(aStartBevelOffset>0)?RoundFloatToPixel(0.5f*(float)aStartBevelOffset,twipsPerPixel,PR_TRUE):0;nscoordendBevel=(aEndBevelOffset>0)?RoundFloatToPixel(0.5f*(float)aEndBevelOffset,twipsPerPixel,PR_TRUE):0;mozilla::css::SideridgeGrooveSide=(horizontal)?NS_SIDE_TOP:NS_SIDE_LEFT;// FIXME: In theory, this should use the visited-dependent// background color, but I don't care.aContext.SetColor(MakeBevelColor(ridgeGrooveSide,ridgeGroove,aBGColor->mBackgroundColor,aBorderColor));nsRectrect(aBorder);nscoordhalf;if(horizontal){// top, bottomhalf=RoundFloatToPixel(0.5f*(float)aBorder.height,twipsPerPixel);rect.height=half;if(NS_SIDE_TOP==aStartBevelSide){rect.x+=startBevel;rect.width-=startBevel;}if(NS_SIDE_TOP==aEndBevelSide){rect.width-=endBevel;}DrawSolidBorderSegment(aContext,rect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}else{// left, righthalf=RoundFloatToPixel(0.5f*(float)aBorder.width,twipsPerPixel);rect.width=half;if(NS_SIDE_LEFT==aStartBevelSide){rect.y+=startBevel;rect.height-=startBevel;}if(NS_SIDE_LEFT==aEndBevelSide){rect.height-=endBevel;}DrawSolidBorderSegment(aContext,rect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}rect=aBorder;ridgeGrooveSide=(NS_SIDE_TOP==ridgeGrooveSide)?NS_SIDE_BOTTOM:NS_SIDE_RIGHT;// FIXME: In theory, this should use the visited-dependent// background color, but I don't care.aContext.SetColor(MakeBevelColor(ridgeGrooveSide,ridgeGroove,aBGColor->mBackgroundColor,aBorderColor));if(horizontal){rect.y=rect.y+half;rect.height=aBorder.height-half;if(NS_SIDE_BOTTOM==aStartBevelSide){rect.x+=startBevel;rect.width-=startBevel;}if(NS_SIDE_BOTTOM==aEndBevelSide){rect.width-=endBevel;}DrawSolidBorderSegment(aContext,rect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}else{rect.x=rect.x+half;rect.width=aBorder.width-half;if(NS_SIDE_RIGHT==aStartBevelSide){rect.y+=aStartBevelOffset-startBevel;rect.height-=startBevel;}if(NS_SIDE_RIGHT==aEndBevelSide){rect.height-=endBevel;}DrawSolidBorderSegment(aContext,rect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}}break;caseNS_STYLE_BORDER_STYLE_DOUBLE:if((aBorder.width>2)&&(aBorder.height>2)){nscoordstartBevel=(aStartBevelOffset>0)?RoundFloatToPixel(0.333333f*(float)aStartBevelOffset,twipsPerPixel):0;nscoordendBevel=(aEndBevelOffset>0)?RoundFloatToPixel(0.333333f*(float)aEndBevelOffset,twipsPerPixel):0;if(horizontal){// top, bottomnscoordthirdHeight=RoundFloatToPixel(0.333333f*(float)aBorder.height,twipsPerPixel);// draw the top line or rectnsRecttopRect(aBorder.x,aBorder.y,aBorder.width,thirdHeight);if(NS_SIDE_TOP==aStartBevelSide){topRect.x+=aStartBevelOffset-startBevel;topRect.width-=aStartBevelOffset-startBevel;}if(NS_SIDE_TOP==aEndBevelSide){topRect.width-=aEndBevelOffset-endBevel;}DrawSolidBorderSegment(aContext,topRect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);// draw the botom line or rectnscoordheightOffset=aBorder.height-thirdHeight;nsRectbottomRect(aBorder.x,aBorder.y+heightOffset,aBorder.width,aBorder.height-heightOffset);if(NS_SIDE_BOTTOM==aStartBevelSide){bottomRect.x+=aStartBevelOffset-startBevel;bottomRect.width-=aStartBevelOffset-startBevel;}if(NS_SIDE_BOTTOM==aEndBevelSide){bottomRect.width-=aEndBevelOffset-endBevel;}DrawSolidBorderSegment(aContext,bottomRect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}else{// left, rightnscoordthirdWidth=RoundFloatToPixel(0.333333f*(float)aBorder.width,twipsPerPixel);nsRectleftRect(aBorder.x,aBorder.y,thirdWidth,aBorder.height);if(NS_SIDE_LEFT==aStartBevelSide){leftRect.y+=aStartBevelOffset-startBevel;leftRect.height-=aStartBevelOffset-startBevel;}if(NS_SIDE_LEFT==aEndBevelSide){leftRect.height-=aEndBevelOffset-endBevel;}DrawSolidBorderSegment(aContext,leftRect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);nscoordwidthOffset=aBorder.width-thirdWidth;nsRectrightRect(aBorder.x+widthOffset,aBorder.y,aBorder.width-widthOffset,aBorder.height);if(NS_SIDE_RIGHT==aStartBevelSide){rightRect.y+=aStartBevelOffset-startBevel;rightRect.height-=aStartBevelOffset-startBevel;}if(NS_SIDE_RIGHT==aEndBevelSide){rightRect.height-=aEndBevelOffset-endBevel;}DrawSolidBorderSegment(aContext,rightRect,twipsPerPixel,aStartBevelSide,startBevel,aEndBevelSide,endBevel);}break;}// else fall through to solidcaseNS_STYLE_BORDER_STYLE_SOLID:DrawSolidBorderSegment(aContext,aBorder,twipsPerPixel,aStartBevelSide,aStartBevelOffset,aEndBevelSide,aEndBevelOffset);break;caseNS_STYLE_BORDER_STYLE_OUTSET:caseNS_STYLE_BORDER_STYLE_INSET:NS_ASSERTION(PR_FALSE,"inset, outset should have been converted to groove, ridge");break;caseNS_STYLE_BORDER_STYLE_AUTO:NS_ASSERTION(PR_FALSE,"Unexpected 'auto' table border");break;}ctx->SetAntialiasMode(oldMode);}// End table border-collapsing sectionvoidnsCSSRendering::PaintDecorationLine(gfxContext*aGfxContext,constnscoloraColor,constgfxPoint&aPt,constgfxSize&aLineSize,constgfxFloataAscent,constgfxFloataOffset,constPRUint8aDecoration,constPRUint8aStyle,constgfxFloataDescentLimit){NS_ASSERTION(aStyle!=DECORATION_STYLE_NONE,"aStyle is none");gfxRectrect=GetTextDecorationRectInternal(aPt,aLineSize,aAscent,aOffset,aDecoration,aStyle,aDescentLimit);if(rect.IsEmpty())return;if(aDecoration!=NS_STYLE_TEXT_DECORATION_UNDERLINE&&aDecoration!=NS_STYLE_TEXT_DECORATION_OVERLINE&&aDecoration!=NS_STYLE_TEXT_DECORATION_LINE_THROUGH){NS_ERROR("Invalid decoration value!");return;}gfxFloatlineHeight=NS_MAX(NS_round(aLineSize.height),1.0);PRBoolcontextIsSaved=PR_FALSE;gfxFloatoldLineWidth;nsRefPtr<gfxPattern>oldPattern;switch(aStyle){caseDECORATION_STYLE_SOLID:caseDECORATION_STYLE_DOUBLE:oldLineWidth=aGfxContext->CurrentLineWidth();oldPattern=aGfxContext->GetPattern();break;caseDECORATION_STYLE_DASHED:{aGfxContext->Save();contextIsSaved=PR_TRUE;aGfxContext->Clip(rect);gfxFloatdashWidth=lineHeight*DOT_LENGTH*DASH_LENGTH;gfxFloatdash[2]={dashWidth,dashWidth};aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);aGfxContext->SetDash(dash,2,0.0);// We should continue to draw the last dash even if it is not in the rect.rect.size.width+=dashWidth;break;}caseDECORATION_STYLE_DOTTED:{aGfxContext->Save();contextIsSaved=PR_TRUE;aGfxContext->Clip(rect);gfxFloatdashWidth=lineHeight*DOT_LENGTH;gfxFloatdash[2];if(lineHeight>2.0){dash[0]=0.0;dash[1]=dashWidth*2.0;aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);}else{dash[0]=dashWidth;dash[1]=dashWidth;}aGfxContext->SetDash(dash,2,0.0);// We should continue to draw the last dot even if it is not in the rect.rect.size.width+=dashWidth;break;}caseDECORATION_STYLE_WAVY:aGfxContext->Save();contextIsSaved=PR_TRUE;aGfxContext->Clip(rect);if(lineHeight>2.0){aGfxContext->SetAntialiasMode(gfxContext::MODE_COVERAGE);}else{// Don't use anti-aliasing here. Because looks like lighter color wavy// line at this case. And probably, users don't think the// non-anti-aliased wavy line is not pretty.aGfxContext->SetAntialiasMode(gfxContext::MODE_ALIASED);}break;default:NS_ERROR("Invalid style value!");return;}// The y position should be set to the middle of the line.rect.pos.y+=lineHeight/2;aGfxContext->SetColor(gfxRGBA(aColor));aGfxContext->SetLineWidth(lineHeight);switch(aStyle){caseDECORATION_STYLE_SOLID:aGfxContext->NewPath();aGfxContext->MoveTo(rect.TopLeft());aGfxContext->LineTo(rect.TopRight());aGfxContext->Stroke();break;caseDECORATION_STYLE_DOUBLE:/** * We are drawing double line as: * * +-------------------------------------------+ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v * | | * | | * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v * +-------------------------------------------+ */aGfxContext->NewPath();aGfxContext->MoveTo(rect.TopLeft());aGfxContext->LineTo(rect.TopRight());rect.size.height-=lineHeight;aGfxContext->MoveTo(rect.BottomLeft());aGfxContext->LineTo(rect.BottomRight());aGfxContext->Stroke();break;caseDECORATION_STYLE_DOTTED:caseDECORATION_STYLE_DASHED:aGfxContext->NewPath();aGfxContext->MoveTo(rect.TopLeft());aGfxContext->LineTo(rect.TopRight());aGfxContext->Stroke();break;caseDECORATION_STYLE_WAVY:{/** * We are drawing wavy line as: * * P: Path, X: Painted pixel * * +---------------------------------------+ * XX|X XXXXXX XXXXXX | * PP|PX XPPPPPPX XPPPPPPX | ^ * XX|XPX XPXXXXXXPX XPXXXXXXPX| | * | XPX XPX XPX XPX XP|X |adv * | XPXXXXXXPX XPXXXXXXPX X|PX | * | XPPPPPPX XPPPPPPX |XPX v * | XXXXXX XXXXXX | XX * +---------------------------------------+ * <---><---> ^ * adv flatLengthAtVertex rightMost * * 1. Always starts from top-left of the drawing area, however, we need * to draw the line from outside of the rect. Because the start * point of the line is not good style if we draw from inside it. * 2. First, draw horizontal line from outside the rect to top-left of * the rect; * 3. Goes down to bottom of the area at 45 degrees. * 4. Slides to right horizontaly, see |flatLengthAtVertex|. * 5. Goes up to top of the area at 45 degrees. * 6. Slides to right horizontaly. * 7. Repeat from 2 until reached to right-most edge of the area. */rect.pos.x+=lineHeight/2.0;aGfxContext->NewPath();gfxPointpt(rect.pos);gfxFloatrightMost=pt.x+rect.Width()+lineHeight;gfxFloatadv=rect.Height()-lineHeight;gfxFloatflatLengthAtVertex=NS_MAX((lineHeight-1.0)*2.0,1.0);pt.x-=lineHeight;aGfxContext->MoveTo(pt);// 1pt.x=rect.pos.x;aGfxContext->LineTo(pt);// 2PRBoolgoDown=PR_TRUE;while(pt.x<rightMost){pt.x+=adv;pt.y+=goDown?adv:-adv;aGfxContext->LineTo(pt);// 3 and 5pt.x+=flatLengthAtVertex;aGfxContext->LineTo(pt);// 4 and 6goDown=!goDown;}aGfxContext->Stroke();break;}default:NS_ERROR("Invalid style value!");break;}if(contextIsSaved){aGfxContext->Restore();}else{aGfxContext->SetPattern(oldPattern);aGfxContext->SetLineWidth(oldLineWidth);}}nsRectnsCSSRendering::GetTextDecorationRect(nsPresContext*aPresContext,constgfxSize&aLineSize,constgfxFloataAscent,constgfxFloataOffset,constPRUint8aDecoration,constPRUint8aStyle,constgfxFloataDescentLimit){NS_ASSERTION(aPresContext,"aPresContext is null");NS_ASSERTION(aStyle!=DECORATION_STYLE_NONE,"aStyle is none");gfxRectrect=GetTextDecorationRectInternal(gfxPoint(0,0),aLineSize,aAscent,aOffset,aDecoration,aStyle,aDescentLimit);// The rect values are already rounded to nearest device pixels.nsRectr;r.x=aPresContext->GfxUnitsToAppUnits(rect.X());r.y=aPresContext->GfxUnitsToAppUnits(rect.Y());r.width=aPresContext->GfxUnitsToAppUnits(rect.Width());r.height=aPresContext->GfxUnitsToAppUnits(rect.Height());returnr;}gfxRectnsCSSRendering::GetTextDecorationRectInternal(constgfxPoint&aPt,constgfxSize&aLineSize,constgfxFloataAscent,constgfxFloataOffset,constPRUint8aDecoration,constPRUint8aStyle,constgfxFloataDescentLimit){NS_ASSERTION(aStyle<=DECORATION_STYLE_WAVY,"Invalid aStyle value");if(aStyle==DECORATION_STYLE_NONE)returngfxRect(0,0,0,0);PRBoolcanLiftUnderline=aDescentLimit>=0.0;gfxRectr;r.pos.x=NS_floor(aPt.x+0.5);r.size.width=NS_round(aLineSize.width);gfxFloatlineHeight=NS_round(aLineSize.height);lineHeight=NS_MAX(lineHeight,1.0);gfxFloatascent=NS_round(aAscent);gfxFloatdescentLimit=NS_floor(aDescentLimit);gfxFloatsuggestedMaxRectHeight=NS_MAX(NS_MIN(ascent,descentLimit),1.0);r.size.height=lineHeight;if(aStyle==DECORATION_STYLE_DOUBLE){/** * We will draw double line as: * * +-------------------------------------------+ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v * | | ^ * | | | gap * | | v * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^ * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v * +-------------------------------------------+ */gfxFloatgap=NS_round(lineHeight/2.0);gap=NS_MAX(gap,1.0);r.size.height=lineHeight*2.0+gap;if(canLiftUnderline){if(r.Height()>suggestedMaxRectHeight){// Don't shrink the line height, because the thickness has some meaning.// We can just shrink the gap at this time.r.size.height=NS_MAX(suggestedMaxRectHeight,lineHeight*2.0+1.0);}}}elseif(aStyle==DECORATION_STYLE_WAVY){/** * We will draw wavy line as: * * +-------------------------------------------+ * |XXXXX XXXXXX XXXXXX | ^ * |XXXXXX XXXXXXXX XXXXXXXX | | lineHeight * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v * | XXX XXX XXX XXX XX| * | XXXXXXXXXX XXXXXXXXXX X| * | XXXXXXXX XXXXXXXX | * | XXXXXX XXXXXX | * +-------------------------------------------+ */r.size.height=lineHeight>2.0?lineHeight*4.0:lineHeight*3.0;if(canLiftUnderline){if(r.Height()>suggestedMaxRectHeight){// Don't shrink the line height even if there is not enough space,// because the thickness has some meaning. E.g., the 1px wavy line and// 2px wavy line can be used for different meaning in IME selections// at same time.r.size.height=NS_MAX(suggestedMaxRectHeight,lineHeight*2.0);}}}gfxFloatbaseline=NS_floor(aPt.y+aAscent+0.5);gfxFloatoffset=0.0;switch(aDecoration){caseNS_STYLE_TEXT_DECORATION_UNDERLINE:offset=aOffset;if(canLiftUnderline){if(descentLimit<-offset+r.Height()){// If we can ignore the offset and the decoration line is overflowing,// we should align the bottom edge of the decoration line rect if it's// possible. Otherwise, we should lift up the top edge of the rect as// far as possible.gfxFloatoffsetBottomAligned=-descentLimit+r.Height();gfxFloatoffsetTopAligned=0.0;offset=NS_MIN(offsetBottomAligned,offsetTopAligned);}}break;caseNS_STYLE_TEXT_DECORATION_OVERLINE:offset=aOffset-lineHeight+r.Height();break;caseNS_STYLE_TEXT_DECORATION_LINE_THROUGH:{gfxFloatextra=NS_floor(r.Height()/2.0+0.5);extra=NS_MAX(extra,lineHeight);offset=aOffset-lineHeight+extra;break;}default:NS_ERROR("Invalid decoration value!");}r.pos.y=baseline-NS_floor(offset+0.5);returnr;}// ------------------// ImageRenderer// ------------------ImageRenderer::ImageRenderer(nsIFrame*aForFrame,constnsStyleImage*aImage,PRUint32aFlags):mForFrame(aForFrame),mImage(aImage),mType(aImage->GetType()),mImageContainer(nsnull),mGradientData(nsnull)#ifdef MOZ_SVG,mPaintServerFrame(nsnull)#endif,mIsReady(PR_FALSE),mSize(0,0),mFlags(aFlags){}ImageRenderer::~ImageRenderer(){}PRBoolImageRenderer::PrepareImage(){if(mImage->IsEmpty()||!mImage->IsComplete()){// Make sure the image is actually decodingmImage->RequestDecode();// We can not prepare the image for rendering if it is not fully loaded.//// Special case: If we requested a sync decode and we have an image, push// on throughnsCOMPtr<imgIContainer>img;if(!((mFlags&FLAG_SYNC_DECODE_IMAGES)&&(mType==eStyleImageType_Image)&&(NS_SUCCEEDED(mImage->GetImageData()->GetImage(getter_AddRefs(img)))&&img)))returnPR_FALSE;}switch(mType){caseeStyleImageType_Image:{nsCOMPtr<imgIContainer>srcImage;mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));NS_ABORT_IF_FALSE(srcImage,"If srcImage is null, mImage->IsComplete() ""should have returned false");if(!mImage->GetCropRect()){mImageContainer.swap(srcImage);}else{nsIntRectactualCropRect;PRBoolisEntireImage;PRBoolsuccess=mImage->ComputeActualCropRect(actualCropRect,&isEntireImage);NS_ASSERTION(success,"ComputeActualCropRect() should not fail here");if(!success||actualCropRect.IsEmpty()){// The cropped image has zero sizereturnPR_FALSE;}if(isEntireImage){// The cropped image is identical to the source imagemImageContainer.swap(srcImage);}else{nsCOMPtr<imgIContainer>subImage;PRUint32aExtractFlags=(mFlags&FLAG_SYNC_DECODE_IMAGES)?(PRUint32)imgIContainer::FLAG_SYNC_DECODE:(PRUint32)imgIContainer::FLAG_NONE;nsresultrv=srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,actualCropRect,aExtractFlags,getter_AddRefs(subImage));if(NS_FAILED(rv)){NS_WARNING("The cropped image contains no pixels to draw; ""maybe the crop rect is outside the image frame rect");returnPR_FALSE;}mImageContainer.swap(subImage);}}mIsReady=PR_TRUE;break;}caseeStyleImageType_Gradient:mGradientData=mImage->GetGradientData();mIsReady=PR_TRUE;break;#ifdef MOZ_SVGcaseeStyleImageType_Element:{nsAutoStringelementId=NS_LITERAL_STRING("#")+nsDependentString(mImage->GetElementId());nsCOMPtr<nsIURI>targetURI;nsCOMPtr<nsIURI>base=mForFrame->GetContent()->GetBaseURI();nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),elementId,mForFrame->GetContent()->GetCurrentDoc(),base);nsSVGPaintingProperty*property=nsSVGEffects::GetPaintingPropertyForURI(targetURI,mForFrame->GetFirstContinuation(),nsSVGEffects::BackgroundImageProperty());if(!property)returnPR_FALSE;mPaintServerFrame=property->GetReferencedFrame();// If the referenced element doesn't have a frame we might still be able// to paint it if it's an <img>, <canvas>, or <video> element.if(!mPaintServerFrame){nsCOMPtr<nsIDOMElement>imageElement=do_QueryInterface(property->GetReferencedElement());mImageElementSurface=nsLayoutUtils::SurfaceFromElement(imageElement);if(!mImageElementSurface.mSurface)returnPR_FALSE;}mIsReady=PR_TRUE;break;}#endifcaseeStyleImageType_Null:default:break;}returnmIsReady;}nsSizeImageRenderer::ComputeSize(constnsSize&aDefault){NS_ASSERTION(mIsReady,"Ensure PrepareImage() has returned true ""before calling me");switch(mType){caseeStyleImageType_Image:{nsIntSizeimageIntSize;mImageContainer->GetWidth(&imageIntSize.width);mImageContainer->GetHeight(&imageIntSize.height);mSize.width=nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);mSize.height=nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);break;}caseeStyleImageType_Gradient:mSize=aDefault;break;#ifdef MOZ_SVGcaseeStyleImageType_Element:{if(mPaintServerFrame){if(mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)){mSize=aDefault;}else{// The intrinsic image size for a generic nsIFrame paint server is// the frame's bbox size rounded to device pixels.PRInt32appUnitsPerDevPixel=mForFrame->PresContext()->AppUnitsPerDevPixel();nsRectrect=nsSVGIntegrationUtils::GetNonSVGUserSpace(mPaintServerFrame);nsRectsize=rect-rect.TopLeft();nsIntRectrounded=size.ToNearestPixels(appUnitsPerDevPixel);mSize=rounded.ToAppUnits(appUnitsPerDevPixel).Size();}}else{NS_ASSERTION(mImageElementSurface.mSurface,"Surface should be ready.");gfxIntSizesize=mImageElementSurface.mSize;mSize.width=nsPresContext::CSSPixelsToAppUnits(size.width);mSize.height=nsPresContext::CSSPixelsToAppUnits(size.height);}break;}#endifcaseeStyleImageType_Null:default:mSize.SizeTo(0,0);break;}returnmSize;}voidImageRenderer::Draw(nsPresContext*aPresContext,nsIRenderingContext&aRenderingContext,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty){if(!mIsReady){NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");return;}if(aDest.IsEmpty()||aFill.IsEmpty()||mSize.width<=0||mSize.height<=0)return;gfxPattern::GraphicsFiltergraphicsFilter=nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);switch(mType){caseeStyleImageType_Image:{PRUint32drawFlags=(mFlags&FLAG_SYNC_DECODE_IMAGES)?(PRUint32)imgIContainer::FLAG_SYNC_DECODE:(PRUint32)imgIContainer::FLAG_NONE;nsLayoutUtils::DrawImage(&aRenderingContext,mImageContainer,graphicsFilter,aDest,aFill,aAnchor,aDirty,drawFlags);break;}caseeStyleImageType_Gradient:nsCSSRendering::PaintGradient(aPresContext,aRenderingContext,mGradientData,aDirty,aDest,aFill);break;#ifdef MOZ_SVGcaseeStyleImageType_Element:if(mPaintServerFrame){nsSVGIntegrationUtils::DrawPaintServer(&aRenderingContext,mForFrame,mPaintServerFrame,graphicsFilter,aDest,aFill,aAnchor,aDirty,mSize);}else{NS_ASSERTION(mImageElementSurface.mSurface,"Surface should be ready.");nsRefPtr<gfxDrawable>surfaceDrawable=newgfxSurfaceDrawable(mImageElementSurface.mSurface,mImageElementSurface.mSize);nsLayoutUtils::DrawPixelSnapped(&aRenderingContext,surfaceDrawable,graphicsFilter,aDest,aFill,aAnchor,aDirty);}break;#endifcaseeStyleImageType_Null:default:break;}}#define MAX_BLUR_RADIUS 300#define MAX_SPREAD_RADIUS 50// -----// nsContextBoxBlur// -----gfxContext*nsContextBoxBlur::Init(constnsRect&aRect,nscoordaSpreadRadius,nscoordaBlurRadius,PRInt32aAppUnitsPerDevPixel,gfxContext*aDestinationCtx,constnsRect&aDirtyRect,constgfxRect*aSkipRect,PRUint32aFlags){if(aRect.IsEmpty()){mContext=nsnull;returnnsnull;}PRInt32blurRadius=static_cast<PRInt32>(aBlurRadius/aAppUnitsPerDevPixel);blurRadius=PR_MIN(blurRadius,MAX_BLUR_RADIUS);PRInt32spreadRadius=static_cast<PRInt32>(aSpreadRadius/aAppUnitsPerDevPixel);spreadRadius=PR_MIN(spreadRadius,MAX_BLUR_RADIUS);mDestinationCtx=aDestinationCtx;// If not blurring, draw directly onto the destination deviceif(blurRadius<=0&&spreadRadius<=0&&!(aFlags&FORCE_MASK)){mContext=aDestinationCtx;returnmContext;}// Convert from app units to device pixelsgfxRectrect=RectToGfxRect(aRect,aAppUnitsPerDevPixel);gfxRectdirtyRect=RectToGfxRect(aDirtyRect,aAppUnitsPerDevPixel);dirtyRect.RoundOut();// Create the temporary surface for blurringmContext=blur.Init(rect,gfxIntSize(spreadRadius,spreadRadius),gfxIntSize(blurRadius,blurRadius),&dirtyRect,aSkipRect);returnmContext;}voidnsContextBoxBlur::DoPaint(){if(mContext==mDestinationCtx)return;blur.Paint(mDestinationCtx);}gfxContext*nsContextBoxBlur::GetContext(){returnmContext;}