Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / base / nsCSSRendering.cpp
blob85a84f731d3588c13fc2d6c4869a51839f6eeeeb
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Mats Palmgren <mats.palmgren@bredband.net>
25 * Takeshi Ichimaru <ayakawa.m@gmail.com>
26 * Masayuki Nakano <masayuki@d-toybox.com>
27 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
28 * Michael Ventnor <m.ventnor@gmail.com>
29 * Rob Arnold <robarnold@mozilla.com>
31 * Alternatively, the contents of this file may be used under the terms of
32 * either of the GNU General Public License Version 2 or later (the "GPL"),
33 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK ***** */
45 /* utility functions for drawing borders and backgrounds */
47 #include "nsStyleConsts.h"
48 #include "nsPresContext.h"
49 #include "nsIImage.h"
50 #include "nsIFrame.h"
51 #include "nsPoint.h"
52 #include "nsRect.h"
53 #include "nsIViewManager.h"
54 #include "nsIPresShell.h"
55 #include "nsFrameManager.h"
56 #include "nsStyleContext.h"
57 #include "nsGkAtoms.h"
58 #include "nsCSSAnonBoxes.h"
59 #include "nsTransform2D.h"
60 #include "nsIDeviceContext.h"
61 #include "nsIContent.h"
62 #include "nsIDocument.h"
63 #include "nsIScrollableFrame.h"
64 #include "imgIRequest.h"
65 #include "imgIContainer.h"
66 #include "gfxIImageFrame.h"
67 #include "nsCSSRendering.h"
68 #include "nsCSSColorUtils.h"
69 #include "nsITheme.h"
70 #include "nsThemeConstants.h"
71 #include "nsIServiceManager.h"
72 #include "nsIHTMLDocument.h"
73 #include "nsLayoutUtils.h"
74 #include "nsINameSpaceManager.h"
75 #include "nsBlockFrame.h"
76 #include "gfxContext.h"
77 #include "nsIInterfaceRequestorUtils.h"
78 #include "gfxPlatform.h"
79 #include "gfxImageSurface.h"
80 #include "nsStyleStructInlines.h"
81 #include "nsCSSFrameConstructor.h"
83 #include "nsCSSRenderingBorders.h"
85 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
86 // recalculating this for each frame in a continuation (perf), hold
87 // a cache of various coordinate information that we need in order
88 // to paint inline backgrounds.
89 struct InlineBackgroundData
91 InlineBackgroundData()
92 : mFrame(nsnull), mBlockFrame(nsnull)
96 ~InlineBackgroundData()
100 void Reset()
102 mBoundingBox.SetRect(0,0,0,0);
103 mContinuationPoint = mLineContinuationPoint = mUnbrokenWidth = 0;
104 mFrame = mBlockFrame = nsnull;
107 nsRect GetContinuousRect(nsIFrame* aFrame)
109 SetFrame(aFrame);
111 nscoord x;
112 if (mBidiEnabled) {
113 x = mLineContinuationPoint;
115 // Scan continuations on the same line as aFrame and accumulate the widths
116 // of frames that are to the left (if this is an LTR block) or right
117 // (if it's RTL) of the current one.
118 PRBool isRtlBlock = (mBlockFrame->GetStyleVisibility()->mDirection ==
119 NS_STYLE_DIRECTION_RTL);
120 nscoord curOffset = aFrame->GetOffsetTo(mBlockFrame).x;
122 nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
123 // If the continuation is fluid we know inlineFrame is not on the same line.
124 // If it's not fluid, we need to test furhter to be sure.
125 while (inlineFrame && !inlineFrame->GetNextInFlow() &&
126 AreOnSameLine(aFrame, inlineFrame)) {
127 nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
128 if(isRtlBlock == (frameXOffset >= curOffset)) {
129 x += inlineFrame->GetSize().width;
131 inlineFrame = inlineFrame->GetPrevContinuation();
134 inlineFrame = aFrame->GetNextContinuation();
135 while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
136 AreOnSameLine(aFrame, inlineFrame)) {
137 nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
138 if(isRtlBlock == (frameXOffset >= curOffset)) {
139 x += inlineFrame->GetSize().width;
141 inlineFrame = inlineFrame->GetNextContinuation();
143 if (isRtlBlock) {
144 // aFrame itself is also to the right of its left edge, so add its width.
145 x += aFrame->GetSize().width;
146 // x is now the distance from the left edge of aFrame to the right edge
147 // of the unbroken content. Change it to indicate the distance from the
148 // left edge of the unbroken content to the left edge of aFrame.
149 x = mUnbrokenWidth - x;
151 } else {
152 x = mContinuationPoint;
155 // Assume background-origin: border and return a rect with offsets
156 // relative to (0,0). If we have a different background-origin,
157 // then our rect should be deflated appropriately by our caller.
158 return nsRect(-x, 0, mUnbrokenWidth, mFrame->GetSize().height);
161 nsRect GetBoundingRect(nsIFrame* aFrame)
163 SetFrame(aFrame);
165 // Move the offsets relative to (0,0) which puts the bounding box into
166 // our coordinate system rather than our parent's. We do this by
167 // moving it the back distance from us to the bounding box.
168 // This also assumes background-origin: border, so our caller will
169 // need to deflate us if needed.
170 nsRect boundingBox(mBoundingBox);
171 nsPoint point = mFrame->GetPosition();
172 boundingBox.MoveBy(-point.x, -point.y);
174 return boundingBox;
177 protected:
178 nsIFrame* mFrame;
179 nscoord mContinuationPoint;
180 nscoord mUnbrokenWidth;
181 nsRect mBoundingBox;
183 PRBool mBidiEnabled;
184 nsBlockFrame* mBlockFrame;
185 nscoord mLineContinuationPoint;
187 void SetFrame(nsIFrame* aFrame)
189 NS_PRECONDITION(aFrame, "Need a frame");
191 nsIFrame *prevContinuation = aFrame->GetPrevContinuation();
193 if (!prevContinuation || mFrame != prevContinuation) {
194 // Ok, we've got the wrong frame. We have to start from scratch.
195 Reset();
196 Init(aFrame);
197 return;
200 // Get our last frame's size and add its width to our continuation
201 // point before we cache the new frame.
202 mContinuationPoint += mFrame->GetSize().width;
204 // If this a new line, update mLineContinuationPoint.
205 if (mBidiEnabled &&
206 (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
207 mLineContinuationPoint = mContinuationPoint;
210 mFrame = aFrame;
213 void Init(nsIFrame* aFrame)
215 // Start with the previous flow frame as our continuation point
216 // is the total of the widths of the previous frames.
217 nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
219 while (inlineFrame) {
220 nsRect rect = inlineFrame->GetRect();
221 mContinuationPoint += rect.width;
222 mUnbrokenWidth += rect.width;
223 mBoundingBox.UnionRect(mBoundingBox, rect);
224 inlineFrame = inlineFrame->GetPrevContinuation();
227 // Next add this frame and subsequent frames to the bounding box and
228 // unbroken width.
229 inlineFrame = aFrame;
230 while (inlineFrame) {
231 nsRect rect = inlineFrame->GetRect();
232 mUnbrokenWidth += rect.width;
233 mBoundingBox.UnionRect(mBoundingBox, rect);
234 inlineFrame = inlineFrame->GetNextContinuation();
237 mFrame = aFrame;
239 mBidiEnabled = aFrame->PresContext()->BidiEnabled();
240 if (mBidiEnabled) {
241 // Find the containing block frame
242 nsIFrame* frame = aFrame;
243 nsresult rv = NS_ERROR_FAILURE;
244 while (frame &&
245 frame->IsFrameOfType(nsIFrame::eLineParticipant) &&
246 NS_FAILED(rv)) {
247 frame = frame->GetParent();
248 rv = frame->QueryInterface(kBlockFrameCID, (void**)&mBlockFrame);
250 NS_ASSERTION(NS_SUCCEEDED(rv) && mBlockFrame, "Cannot find containing block.");
252 mLineContinuationPoint = mContinuationPoint;
256 PRBool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
257 // Assumes that aFrame1 and aFrame2 are both decsendants of mBlockFrame.
258 PRBool isValid1, isValid2;
259 nsBlockInFlowLineIterator it1(mBlockFrame, aFrame1, &isValid1);
260 nsBlockInFlowLineIterator it2(mBlockFrame, aFrame2, &isValid2);
261 return isValid1 && isValid2 && it1.GetLine() == it2.GetLine();
265 /* Local functions */
266 static void DrawBorderImage(nsPresContext* aPresContext,
267 nsIRenderingContext& aRenderingContext,
268 nsIFrame* aForFrame,
269 const nsRect& aBorderArea,
270 const nsStyleBorder& aBorderStyle);
272 static void DrawBorderImageSide(gfxContext *aThebesContext,
273 nsIDeviceContext* aDeviceContext,
274 imgIContainer* aImage,
275 gfxRect& aDestRect,
276 gfxSize aInterSize,
277 gfxRect& aSourceRect,
278 PRUint8 aHFillType,
279 PRUint8 aVFillType);
281 static void PaintBackgroundColor(nsPresContext* aPresContext,
282 nsIRenderingContext& aRenderingContext,
283 nsIFrame* aForFrame,
284 const nsRect& aBgClipArea,
285 const nsStyleBackground& aColor,
286 const nsStyleBorder& aBorder,
287 PRBool aCanPaintNonWhite);
289 static nscolor MakeBevelColor(PRIntn whichSide, PRUint8 style,
290 nscolor aBackgroundColor,
291 nscolor aBorderColor);
293 static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
294 const gfxSize& aLineSize,
295 const gfxFloat aAscent,
296 const gfxFloat aOffset,
297 const PRUint8 aDecoration,
298 const PRUint8 aStyle);
300 /* Returns FALSE iff all returned aTwipsRadii == 0, TRUE otherwise */
301 static PRBool GetBorderRadiusTwips(const nsStyleCorners& aBorderRadius,
302 const nscoord& aFrameWidth,
303 nscoord aTwipsRadii[8]);
305 static InlineBackgroundData* gInlineBGData = nsnull;
307 // Initialize any static variables used by nsCSSRendering.
308 nsresult nsCSSRendering::Init()
310 NS_ASSERTION(!gInlineBGData, "Init called twice");
311 gInlineBGData = new InlineBackgroundData();
312 if (!gInlineBGData)
313 return NS_ERROR_OUT_OF_MEMORY;
315 return NS_OK;
318 // Clean up any global variables used by nsCSSRendering.
319 void nsCSSRendering::Shutdown()
321 delete gInlineBGData;
322 gInlineBGData = nsnull;
326 * Make a bevel color
328 static nscolor
329 MakeBevelColor(PRIntn whichSide, PRUint8 style,
330 nscolor aBackgroundColor, nscolor aBorderColor)
333 nscolor colors[2];
334 nscolor theColor;
336 // Given a background color and a border color
337 // calculate the color used for the shading
338 NS_GetSpecial3DColors(colors, aBackgroundColor, aBorderColor);
340 if ((style == NS_STYLE_BORDER_STYLE_OUTSET) ||
341 (style == NS_STYLE_BORDER_STYLE_RIDGE)) {
342 // Flip colors for these two border styles
343 switch (whichSide) {
344 case NS_SIDE_BOTTOM: whichSide = NS_SIDE_TOP; break;
345 case NS_SIDE_RIGHT: whichSide = NS_SIDE_LEFT; break;
346 case NS_SIDE_TOP: whichSide = NS_SIDE_BOTTOM; break;
347 case NS_SIDE_LEFT: whichSide = NS_SIDE_RIGHT; break;
351 switch (whichSide) {
352 case NS_SIDE_BOTTOM:
353 theColor = colors[1];
354 break;
355 case NS_SIDE_RIGHT:
356 theColor = colors[1];
357 break;
358 case NS_SIDE_TOP:
359 theColor = colors[0];
360 break;
361 case NS_SIDE_LEFT:
362 default:
363 theColor = colors[0];
364 break;
366 return theColor;
369 //----------------------------------------------------------------------
370 // Thebes Border Rendering Code Start
372 // helper function to convert a nsRect to a gfxRect
373 static gfxRect
374 RectToGfxRect(const nsRect& rect, nscoord twipsPerPixel)
376 return gfxRect(gfxFloat(rect.x) / twipsPerPixel,
377 gfxFloat(rect.y) / twipsPerPixel,
378 gfxFloat(rect.width) / twipsPerPixel,
379 gfxFloat(rect.height) / twipsPerPixel);
383 * Compute the float-pixel radii that should be used for drawing
384 * this border/outline, given the various input bits.
386 * If a side is skipped via skipSides, its corners are forced to 0.
387 * All corner radii are then adjusted so they do not require more
388 * space than outerRect, according to the algorithm in css3-background.
390 static void
391 ComputePixelRadii(const nscoord *aTwipsRadii,
392 const nsRect& outerRect,
393 PRIntn skipSides,
394 nscoord twipsPerPixel,
395 gfxCornerSizes *oBorderRadii)
397 nscoord twipsRadii[8];
398 memcpy(twipsRadii, aTwipsRadii, sizeof twipsRadii);
400 if (skipSides & SIDE_BIT_TOP) {
401 twipsRadii[NS_CORNER_TOP_LEFT_X] = 0;
402 twipsRadii[NS_CORNER_TOP_LEFT_Y] = 0;
403 twipsRadii[NS_CORNER_TOP_RIGHT_X] = 0;
404 twipsRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
407 if (skipSides & SIDE_BIT_RIGHT) {
408 twipsRadii[NS_CORNER_TOP_RIGHT_X] = 0;
409 twipsRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
410 twipsRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
411 twipsRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
414 if (skipSides & SIDE_BIT_BOTTOM) {
415 twipsRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
416 twipsRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
417 twipsRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
418 twipsRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
421 if (skipSides & SIDE_BIT_LEFT) {
422 twipsRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
423 twipsRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
424 twipsRadii[NS_CORNER_TOP_LEFT_X] = 0;
425 twipsRadii[NS_CORNER_TOP_LEFT_Y] = 0;
428 gfxFloat radii[8];
429 NS_FOR_CSS_HALF_CORNERS(corner)
430 radii[corner] = twipsRadii[corner] / twipsPerPixel;
432 // css3-background specifies this algorithm for reducing
433 // corner radii when they are too big.
434 gfxFloat maxWidth = outerRect.width / twipsPerPixel;
435 gfxFloat maxHeight = outerRect.height / twipsPerPixel;
436 gfxFloat f = 1.0f;
437 NS_FOR_CSS_SIDES(side) {
438 PRUint32 hc1 = NS_SIDE_TO_HALF_CORNER(side, PR_FALSE, PR_TRUE);
439 PRUint32 hc2 = NS_SIDE_TO_HALF_CORNER(side, PR_TRUE, PR_TRUE);
440 gfxFloat length = NS_SIDE_IS_VERTICAL(side) ? maxHeight : maxWidth;
441 gfxFloat sum = radii[hc1] + radii[hc2];
442 // avoid floating point division in the normal case
443 if (length < sum)
444 f = PR_MIN(f, length/sum);
446 if (f < 1.0) {
447 NS_FOR_CSS_HALF_CORNERS(corner) {
448 radii[corner] *= f;
452 (*oBorderRadii)[C_TL] = gfxSize(radii[NS_CORNER_TOP_LEFT_X],
453 radii[NS_CORNER_TOP_LEFT_Y]);
454 (*oBorderRadii)[C_TR] = gfxSize(radii[NS_CORNER_TOP_RIGHT_X],
455 radii[NS_CORNER_TOP_RIGHT_Y]);
456 (*oBorderRadii)[C_BR] = gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],
457 radii[NS_CORNER_BOTTOM_RIGHT_Y]);
458 (*oBorderRadii)[C_BL] = gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],
459 radii[NS_CORNER_BOTTOM_LEFT_Y]);
462 void
463 nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
464 nsIRenderingContext& aRenderingContext,
465 nsIFrame* aForFrame,
466 const nsRect& aDirtyRect,
467 const nsRect& aBorderArea,
468 const nsStyleBorder& aBorderStyle,
469 nsStyleContext* aStyleContext,
470 PRIntn aSkipSides)
472 nsMargin border;
473 nscoord twipsRadii[8];
474 nsCompatibility compatMode = aPresContext->CompatibilityMode();
476 SN("++ PaintBorder");
478 // Check to see if we have an appearance defined. If so, we let the theme
479 // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
480 // may be different! Always use |aStyleContext|!
481 const nsStyleDisplay* displayData = aStyleContext->GetStyleDisplay();
482 if (displayData->mAppearance) {
483 nsITheme *theme = aPresContext->GetTheme();
484 if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, displayData->mAppearance))
485 return; // Let the theme handle it.
488 if (aBorderStyle.IsBorderImageLoaded()) {
489 DrawBorderImage(aPresContext, aRenderingContext, aForFrame,
490 aBorderArea, aBorderStyle);
491 return;
494 // Get our style context's color struct.
495 const nsStyleColor* ourColor = aStyleContext->GetStyleColor();
497 // in NavQuirks mode we want to use the parent's context as a starting point
498 // for determining the background color
499 const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground
500 (aStyleContext, compatMode == eCompatibility_NavQuirks ? PR_TRUE : PR_FALSE);
502 border = aBorderStyle.GetComputedBorder();
503 if ((0 == border.left) && (0 == border.right) &&
504 (0 == border.top) && (0 == border.bottom)) {
505 // Empty border area
506 return;
509 GetBorderRadiusTwips(aBorderStyle.mBorderRadius, aForFrame->GetSize().width,
510 twipsRadii);
512 // Turn off rendering for all of the zero sized sides
513 if (aSkipSides & SIDE_BIT_TOP) border.top = 0;
514 if (aSkipSides & SIDE_BIT_RIGHT) border.right = 0;
515 if (aSkipSides & SIDE_BIT_BOTTOM) border.bottom = 0;
516 if (aSkipSides & SIDE_BIT_LEFT) border.left = 0;
518 // get the inside and outside parts of the border
519 nsRect outerRect(aBorderArea);
521 SF(" outerRect: %d %d %d %d\n", outerRect.x, outerRect.y, outerRect.width, outerRect.height);
523 // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
525 // Get our conversion values
526 nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
528 // convert outer and inner rects
529 gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
531 // convert the border widths
532 gfxFloat borderWidths[4] = { border.top / twipsPerPixel,
533 border.right / twipsPerPixel,
534 border.bottom / twipsPerPixel,
535 border.left / twipsPerPixel };
537 // convert the radii
538 gfxCornerSizes borderRadii;
539 ComputePixelRadii(twipsRadii, outerRect, aSkipSides, twipsPerPixel,
540 &borderRadii);
542 PRUint8 borderStyles[4];
543 nscolor borderColors[4];
544 nsBorderColors *compositeColors[4];
546 // pull out styles, colors, composite colors
547 NS_FOR_CSS_SIDES (i) {
548 PRBool foreground;
549 borderStyles[i] = aBorderStyle.GetBorderStyle(i);
550 aBorderStyle.GetBorderColor(i, borderColors[i], foreground);
551 aBorderStyle.GetCompositeColors(i, &compositeColors[i]);
553 if (foreground)
554 borderColors[i] = ourColor->mColor;
557 SF(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
559 // start drawing
560 gfxContext *ctx = aRenderingContext.ThebesContext();
562 ctx->Save();
564 #if 0
565 // this will draw a transparent red backround underneath the oRect area
566 ctx->Save();
567 ctx->Rectangle(oRect);
568 ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
569 ctx->Fill();
570 ctx->Restore();
571 #endif
573 //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
575 nsCSSBorderRenderer br(twipsPerPixel,
576 ctx,
577 oRect,
578 borderStyles,
579 borderWidths,
580 borderRadii,
581 borderColors,
582 compositeColors,
583 aSkipSides,
584 bgColor->mBackgroundColor);
585 br.DrawBorders();
587 ctx->Restore();
589 SN();
592 static nsRect
593 GetOutlineInnerRect(nsIFrame* aFrame)
595 nsRect* savedOutlineInnerRect = static_cast<nsRect*>
596 (aFrame->GetProperty(nsGkAtoms::outlineInnerRectProperty));
597 if (savedOutlineInnerRect)
598 return *savedOutlineInnerRect;
599 return aFrame->GetOverflowRect();
602 void
603 nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
604 nsIRenderingContext& aRenderingContext,
605 nsIFrame* aForFrame,
606 const nsRect& aDirtyRect,
607 const nsRect& aBorderArea,
608 const nsStyleBorder& aBorderStyle,
609 const nsStyleOutline& aOutlineStyle,
610 nsStyleContext* aStyleContext)
612 nscoord twipsRadii[8];
614 // Get our style context's color struct.
615 const nsStyleColor* ourColor = aStyleContext->GetStyleColor();
617 nscoord width;
618 aOutlineStyle.GetOutlineWidth(width);
620 if (width == 0) {
621 // Empty outline
622 return;
625 const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground
626 (aStyleContext, PR_FALSE);
628 // get the radius for our outline
629 GetBorderRadiusTwips(aOutlineStyle.mOutlineRadius, aBorderArea.width,
630 twipsRadii);
632 // When the outline property is set on :-moz-anonymous-block or
633 // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
634 // outline from the inline that was broken because it contained a
635 // block. In that case, we don't want a really wide outline if the
636 // block inside the inline is narrow, so union the actual contents of
637 // the anonymous blocks.
638 nsIFrame *frameForArea = aForFrame;
639 do {
640 nsIAtom *pseudoType = frameForArea->GetStyleContext()->GetPseudoType();
641 if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
642 pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
643 break;
644 // If we're done, we really want it and all its later siblings.
645 frameForArea = frameForArea->GetFirstChild(nsnull);
646 NS_ASSERTION(frameForArea, "anonymous block with no children?");
647 } while (frameForArea);
648 nsRect innerRect; // relative to aBorderArea.TopLeft()
649 if (frameForArea == aForFrame) {
650 innerRect = GetOutlineInnerRect(aForFrame);
651 } else {
652 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
653 // The outline has already been included in aForFrame's overflow
654 // area, but not in those of its descendants, so we have to
655 // include it. Otherwise we'll end up drawing the outline inside
656 // the border.
657 nsRect r(GetOutlineInnerRect(frameForArea) +
658 frameForArea->GetOffsetTo(aForFrame));
659 innerRect.UnionRect(innerRect, r);
663 innerRect += aBorderArea.TopLeft();
664 nscoord offset = aOutlineStyle.mOutlineOffset;
665 innerRect.Inflate(offset, offset);
666 // If the dirty rect is completely inside the border area (e.g., only the
667 // content is being painted), then we can skip out now
668 // XXX this isn't exactly true for rounded borders, where the inside curves may
669 // encroach into the content area. A safer calculation would be to
670 // shorten insideRect by the radius one each side before performing this test.
671 if (innerRect.Contains(aDirtyRect))
672 return;
674 nsRect outerRect = innerRect;
675 outerRect.Inflate(width, width);
677 // Get our conversion values
678 nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
680 // get the outer rectangles
681 gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
683 // convert the radii
684 nsMargin outlineMargin(width, width, width, width);
685 gfxCornerSizes outlineRadii;
686 ComputePixelRadii(twipsRadii, outerRect, 0, twipsPerPixel,
687 &outlineRadii);
689 PRUint8 outlineStyle = aOutlineStyle.GetOutlineStyle();
690 PRUint8 outlineStyles[4] = { outlineStyle,
691 outlineStyle,
692 outlineStyle,
693 outlineStyle };
695 nscolor outlineColor;
696 // PR_FALSE means use the initial color; PR_TRUE means a color was
697 // set.
698 if (!aOutlineStyle.GetOutlineColor(outlineColor))
699 outlineColor = ourColor->mColor;
700 nscolor outlineColors[4] = { outlineColor,
701 outlineColor,
702 outlineColor,
703 outlineColor };
705 // convert the border widths
706 gfxFloat outlineWidths[4] = { width / twipsPerPixel,
707 width / twipsPerPixel,
708 width / twipsPerPixel,
709 width / twipsPerPixel };
711 // start drawing
712 gfxContext *ctx = aRenderingContext.ThebesContext();
714 ctx->Save();
716 nsCSSBorderRenderer br(twipsPerPixel,
717 ctx,
718 oRect,
719 outlineStyles,
720 outlineWidths,
721 outlineRadii,
722 outlineColors,
723 nsnull, 0,
724 bgColor->mBackgroundColor);
725 br.DrawBorders();
727 ctx->Restore();
729 SN();
732 void
733 nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
734 nsIRenderingContext& aRenderingContext,
735 const nsRect& aFocusRect,
736 nscolor aColor)
738 nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
739 nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
741 gfxRect focusRect(RectToGfxRect(aFocusRect, oneDevPixel));
743 gfxCornerSizes focusRadii;
745 nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
746 ComputePixelRadii(twipsRadii, aFocusRect, 0, oneDevPixel, &focusRadii);
748 gfxFloat focusWidths[4] = { oneCSSPixel / oneDevPixel,
749 oneCSSPixel / oneDevPixel,
750 oneCSSPixel / oneDevPixel,
751 oneCSSPixel / oneDevPixel };
753 PRUint8 focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
754 NS_STYLE_BORDER_STYLE_DOTTED,
755 NS_STYLE_BORDER_STYLE_DOTTED,
756 NS_STYLE_BORDER_STYLE_DOTTED };
757 nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
759 gfxContext *ctx = aRenderingContext.ThebesContext();
761 ctx->Save();
763 // Because this renders a dotted border, the background color
764 // should not be used. Therefore, we provide a value that will
765 // be blatantly wrong if it ever does get used. (If this becomes
766 // something that CSS can style, this function will then have access
767 // to a style context and can use the same logic that PaintBorder
768 // and PaintOutline do.)
769 nsCSSBorderRenderer br(oneDevPixel,
770 ctx,
771 focusRect,
772 focusStyles,
773 focusWidths,
774 focusRadii,
775 focusColors,
776 nsnull, 0,
777 NS_RGB(255, 0, 0));
778 br.DrawBorders();
780 ctx->Restore();
782 SN();
785 // Thebes Border Rendering Code End
786 //----------------------------------------------------------------------
789 //----------------------------------------------------------------------
792 * Computes the placement of a background image.
794 * @param aOriginBounds is the box to which the tiling position should be
795 * relative
796 * This should correspond to 'background-origin' for the frame,
797 * except when painting on the canvas, in which case the origin bounds
798 * should be the bounds of the root element's frame.
799 * @param aTopLeft the top-left corner where an image tile should be drawn
800 * @param aAnchorPoint a point which should be pixel-aligned by
801 * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS
802 * specifies a percentage (including 'right' or 'bottom'), in which case
803 * it's that percentage within of aOriginBounds. So 'right' would set
804 * aAnchorPoint.x to aOriginBounds.XMost().
806 * Points are returned relative to aOriginBounds.
808 static void
809 ComputeBackgroundAnchorPoint(const nsStyleBackground& aColor,
810 const nsSize& aOriginBounds,
811 const nsSize& aImageSize,
812 nsPoint* aTopLeft,
813 nsPoint* aAnchorPoint)
815 if (NS_STYLE_BG_X_POSITION_LENGTH & aColor.mBackgroundFlags) {
816 aTopLeft->x = aAnchorPoint->x = aColor.mBackgroundXPosition.mCoord;
818 else if (NS_STYLE_BG_X_POSITION_PERCENT & aColor.mBackgroundFlags) {
819 double percent = aColor.mBackgroundXPosition.mFloat;
820 aAnchorPoint->x = NSToCoordRound(percent*aOriginBounds.width);
821 aTopLeft->x = NSToCoordRound(percent*(aOriginBounds.width - aImageSize.width));
823 else {
824 aTopLeft->x = aAnchorPoint->x = 0;
827 if (NS_STYLE_BG_Y_POSITION_LENGTH & aColor.mBackgroundFlags) {
828 aTopLeft->y = aAnchorPoint->y = aColor.mBackgroundYPosition.mCoord;
830 else if (NS_STYLE_BG_Y_POSITION_PERCENT & aColor.mBackgroundFlags) {
831 double percent = aColor.mBackgroundYPosition.mFloat;
832 aAnchorPoint->y = NSToCoordRound(percent*aOriginBounds.height);
833 aTopLeft->y = NSToCoordRound(percent*(aOriginBounds.height - aImageSize.height));
835 else {
836 aTopLeft->y = aAnchorPoint->y = 0;
840 const nsStyleBackground*
841 nsCSSRendering::FindNonTransparentBackground(nsStyleContext* aContext,
842 PRBool aStartAtParent /*= PR_FALSE*/)
844 NS_ASSERTION(aContext, "Cannot find NonTransparentBackground in a null context" );
846 const nsStyleBackground* result = nsnull;
847 nsStyleContext* context = nsnull;
848 if (aStartAtParent) {
849 context = aContext->GetParent();
851 if (!context) {
852 context = aContext;
855 while (context) {
856 result = context->GetStyleBackground();
857 if (NS_GET_A(result->mBackgroundColor) > 0)
858 break;
860 context = context->GetParent();
862 return result;
867 * |FindBackground| finds the correct style data to use to paint the
868 * background. It is responsible for handling the following two
869 * statements in section 14.2 of CSS2:
871 * The background of the box generated by the root element covers the
872 * entire canvas.
874 * For HTML documents, however, we recommend that authors specify the
875 * background for the BODY element rather than the HTML element. User
876 * agents should observe the following precedence rules to fill in the
877 * background: if the value of the 'background' property for the HTML
878 * element is different from 'transparent' then use it, else use the
879 * value of the 'background' property for the BODY element. If the
880 * resulting value is 'transparent', the rendering is undefined.
882 * Thus, in our implementation, it is responsible for ensuring that:
883 * + we paint the correct background on the |nsCanvasFrame|,
884 * |nsRootBoxFrame|, or |nsPageFrame|,
885 * + we don't paint the background on the root element, and
886 * + we don't paint the background on the BODY element in *some* cases,
887 * and for SGML-based HTML documents only.
889 * |FindBackground| returns true if a background should be painted, and
890 * the resulting style context to use for the background information
891 * will be filled in to |aBackground|. It fills in a boolean indicating
892 * whether the frame is the canvas frame to allow PaintBackground to
893 * ensure that it always paints something non-transparent for the
894 * canvas.
897 // Returns true if aFrame is a canvas frame.
898 // We need to treat the viewport as canvas because, even though
899 // it does not actually paint a background, we need to get the right
900 // background style so we correctly detect transparent documents.
901 inline PRBool
902 IsCanvasFrame(nsIFrame *aFrame)
904 nsIAtom* frameType = aFrame->GetType();
905 return frameType == nsGkAtoms::canvasFrame ||
906 frameType == nsGkAtoms::rootFrame ||
907 frameType == nsGkAtoms::pageFrame ||
908 frameType == nsGkAtoms::pageContentFrame ||
909 frameType == nsGkAtoms::viewportFrame;
912 inline PRBool
913 FindCanvasBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
914 const nsStyleBackground** aBackground)
916 if (aRootElementFrame) {
917 const nsStyleBackground* result = aRootElementFrame->GetStyleBackground();
919 // Check if we need to do propagation from BODY rather than HTML.
920 if (result->IsTransparent()) {
921 nsIContent* content = aRootElementFrame->GetContent();
922 // The root element content can't be null. We wouldn't know what
923 // frame to create for aRootElementFrame.
924 // Use |GetOwnerDoc| so it works during destruction.
925 nsIDocument* document = content->GetOwnerDoc();
926 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
927 if (htmlDoc) {
928 nsIContent* bodyContent = htmlDoc->GetBodyContentExternal();
929 // We need to null check the body node (bug 118829) since
930 // there are cases, thanks to the fix for bug 5569, where we
931 // will reflow a document with no body. In particular, if a
932 // SCRIPT element in the head blocks the parser and then has a
933 // SCRIPT that does "document.location.href = 'foo'", then
934 // nsParser::Terminate will call |DidBuildModel| methods
935 // through to the content sink, which will call |StartLayout|
936 // and thus |InitialReflow| on the pres shell. See bug 119351
937 // for the ugly details.
938 if (bodyContent) {
939 nsIFrame *bodyFrame = aForFrame->PresContext()->GetPresShell()->
940 GetPrimaryFrameFor(bodyContent);
941 if (bodyFrame)
942 result = bodyFrame->GetStyleBackground();
947 *aBackground = result;
948 } else {
949 // This should always give transparent, so we'll fill it in with the
950 // default color if needed. This seems to happen a bit while a page is
951 // being loaded.
952 *aBackground = aForFrame->GetStyleBackground();
955 return PR_TRUE;
958 inline PRBool
959 FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
960 const nsStyleBackground** aBackground)
962 if (aForFrame == aRootElementFrame) {
963 // We must have propagated our background to the viewport or canvas. Abort.
964 return PR_FALSE;
967 *aBackground = aForFrame->GetStyleBackground();
969 // Return true unless the frame is for a BODY element whose background
970 // was propagated to the viewport.
972 nsIContent* content = aForFrame->GetContent();
973 if (!content || content->Tag() != nsGkAtoms::body)
974 return PR_TRUE; // not frame for a "body" element
975 // It could be a non-HTML "body" element but that's OK, we'd fail the
976 // bodyContent check below
978 if (aForFrame->GetStyleContext()->GetPseudoType())
979 return PR_TRUE; // A pseudo-element frame.
981 // We should only look at the <html> background if we're in an HTML document
982 nsIDocument* document = content->GetOwnerDoc();
983 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
984 if (!htmlDoc)
985 return PR_TRUE;
987 nsIContent* bodyContent = htmlDoc->GetBodyContentExternal();
988 if (bodyContent != content)
989 return PR_TRUE; // this wasn't the background that was propagated
991 // This can be called even when there's no root element yet, during frame
992 // construction, via nsLayoutUtils::FrameHasTransparency and
993 // nsContainerFrame::SyncFrameViewProperties.
994 if (!aRootElementFrame)
995 return PR_TRUE;
997 const nsStyleBackground* htmlBG = aRootElementFrame->GetStyleBackground();
998 return !htmlBG->IsTransparent();
1001 PRBool
1002 nsCSSRendering::FindBackground(nsPresContext* aPresContext,
1003 nsIFrame* aForFrame,
1004 const nsStyleBackground** aBackground,
1005 PRBool* aIsCanvas)
1007 nsIFrame* rootElementFrame =
1008 aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1009 PRBool isCanvasFrame = IsCanvasFrame(aForFrame);
1010 *aIsCanvas = isCanvasFrame;
1011 return isCanvasFrame
1012 ? FindCanvasBackground(aForFrame, rootElementFrame, aBackground)
1013 : FindElementBackground(aForFrame, rootElementFrame, aBackground);
1016 void
1017 nsCSSRendering::DidPaint()
1019 gInlineBGData->Reset();
1022 static PRBool
1023 GetBorderRadiusTwips(const nsStyleCorners& aBorderRadius,
1024 const nscoord& aFrameWidth, nscoord aTwipsRadii[8])
1026 PRBool result = PR_FALSE;
1028 // Convert percentage values
1029 NS_FOR_CSS_HALF_CORNERS(i) {
1030 const nsStyleCoord c = aBorderRadius.Get(i);
1032 switch (c.GetUnit()) {
1033 case eStyleUnit_Percent:
1034 aTwipsRadii[i] = (nscoord)(c.GetPercentValue() * aFrameWidth);
1035 break;
1037 case eStyleUnit_Coord:
1038 aTwipsRadii[i] = c.GetCoordValue();
1039 break;
1041 default:
1042 NS_NOTREACHED("GetBorderRadiusTwips: bad unit");
1043 aTwipsRadii[i] = 0;
1044 break;
1047 if (aTwipsRadii[i])
1048 result = PR_TRUE;
1050 return result;
1053 void
1054 nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext,
1055 nsIRenderingContext& aRenderingContext,
1056 nsIFrame* aForFrame,
1057 const nsPoint& aForFramePt,
1058 const nsRect& aDirtyRect)
1060 nsMargin borderValues;
1061 PRIntn sidesToSkip;
1062 nsRect frameRect;
1064 const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
1065 borderValues = styleBorder->GetActualBorder();
1066 sidesToSkip = aForFrame->GetSkipSides();
1067 frameRect = nsRect(aForFramePt, aForFrame->GetSize());
1069 // Get any border radius, since box-shadow must also have rounded corners if the frame does
1070 nscoord twipsRadii[8];
1071 PRBool hasBorderRadius = GetBorderRadiusTwips(styleBorder->mBorderRadius,
1072 frameRect.width, twipsRadii);
1073 nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1075 gfxCornerSizes borderRadii;
1076 ComputePixelRadii(twipsRadii, frameRect, sidesToSkip,
1077 twipsPerPixel, &borderRadii);
1079 gfxRect frameGfxRect = RectToGfxRect(frameRect, twipsPerPixel);
1080 gfxRect dirtyGfxRect = RectToGfxRect(aDirtyRect, twipsPerPixel);
1082 for (PRUint32 i = styleBorder->mBoxShadow->Length(); i > 0; --i) {
1083 nsCSSShadowItem* shadowItem = styleBorder->mBoxShadow->ShadowAt(i - 1);
1084 gfxRect shadowRect(frameRect.x, frameRect.y, frameRect.width, frameRect.height);
1085 shadowRect.MoveBy(gfxPoint(shadowItem->mXOffset, shadowItem->mYOffset));
1086 shadowRect.Outset(shadowItem->mSpread);
1088 gfxRect shadowRectPlusBlur = shadowRect;
1089 shadowRect.ScaleInverse(twipsPerPixel);
1090 shadowRect.RoundOut();
1092 // shadowRect won't include the blur, so make an extra rect here that includes the blur
1093 // for use in the even-odd rule below.
1094 nscoord blurRadius = shadowItem->mRadius;
1095 shadowRectPlusBlur.Outset(blurRadius);
1096 shadowRectPlusBlur.ScaleInverse(twipsPerPixel);
1097 shadowRectPlusBlur.RoundOut();
1099 gfxContext* renderContext = aRenderingContext.ThebesContext();
1100 nsRefPtr<gfxContext> shadowContext;
1101 nsContextBoxBlur blurringArea;
1103 // shadowRect is already in device pixels, pass 1 as the appunits/pixel value
1104 blurRadius /= twipsPerPixel;
1105 shadowContext = blurringArea.Init(shadowRect, blurRadius, 1, renderContext, dirtyGfxRect);
1106 if (!shadowContext)
1107 continue;
1109 // Set the shadow color; if not specified, use the foreground color
1110 nscolor shadowColor;
1111 if (shadowItem->mHasColor)
1112 shadowColor = shadowItem->mColor;
1113 else
1114 shadowColor = aForFrame->GetStyleColor()->mColor;
1116 renderContext->Save();
1117 renderContext->SetColor(gfxRGBA(shadowColor));
1119 // Clip out the area of the actual frame so the shadow is not shown within
1120 // the frame
1121 renderContext->NewPath();
1122 renderContext->Rectangle(shadowRectPlusBlur);
1123 if (hasBorderRadius)
1124 renderContext->RoundedRectangle(frameGfxRect, borderRadii);
1125 else
1126 renderContext->Rectangle(frameGfxRect);
1127 renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
1128 renderContext->Clip();
1130 // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1131 // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1132 // surface? If we have no blur, we're painting this fill on the actual content surface
1133 // (renderContext == shadowContext) which is why we set up the color and clip
1134 // before doing this.
1135 shadowContext->NewPath();
1136 if (hasBorderRadius)
1137 shadowContext->RoundedRectangle(shadowRect, borderRadii);
1138 else
1139 shadowContext->Rectangle(shadowRect);
1140 shadowContext->Fill();
1142 blurringArea.DoPaint();
1143 renderContext->Restore();
1147 void
1148 nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
1149 nsIRenderingContext& aRenderingContext,
1150 nsIFrame* aForFrame,
1151 const nsRect& aDirtyRect,
1152 const nsRect& aBorderArea,
1153 PRBool aUsePrintSettings,
1154 nsRect* aBGClipRect)
1156 NS_PRECONDITION(aForFrame,
1157 "Frame is expected to be provided to PaintBackground");
1159 PRBool isCanvas;
1160 const nsStyleBackground *color;
1161 const nsStyleBorder* border = aForFrame->GetStyleBorder();
1163 if (!FindBackground(aPresContext, aForFrame, &color, &isCanvas)) {
1164 // we don't want to bail out of moz-appearance is set on a root
1165 // node. If it has a parent content node, bail because it's not
1166 // a root, other wise keep going in order to let the theme stuff
1167 // draw the background. The canvas really should be drawing the
1168 // bg, but there's no way to hook that up via css.
1169 if (!aForFrame->GetStyleDisplay()->mAppearance) {
1170 return;
1173 nsIContent* content = aForFrame->GetContent();
1174 if (!content || content->GetParent()) {
1175 return;
1178 color = aForFrame->GetStyleBackground();
1180 if (!isCanvas) {
1181 PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
1182 aDirtyRect, aBorderArea, *color, *border,
1183 aUsePrintSettings, aBGClipRect);
1184 return;
1187 nsStyleBackground canvasColor(*color);
1189 nsIViewManager* vm = aPresContext->GetViewManager();
1191 if (NS_GET_A(canvasColor.mBackgroundColor) < 255) {
1192 // If the window is intended to be opaque, ensure that we always
1193 // paint an opaque color for its root element, in case there's no
1194 // background at all or a partly transparent image.
1195 nsIView* rView;
1196 vm->GetRootView(rView);
1197 if (!rView->GetParent() &&
1198 (!rView->HasWidget() ||
1199 rView->GetWidget()->GetTransparencyMode() == eTransparencyOpaque)) {
1200 nscolor backColor = aPresContext->DefaultBackgroundColor();
1201 NS_ASSERTION(NS_GET_A(backColor) == 255,
1202 "default background color is not opaque");
1204 canvasColor.mBackgroundColor =
1205 NS_ComposeColors(backColor, canvasColor.mBackgroundColor);
1209 vm->SetDefaultBackgroundColor(canvasColor.mBackgroundColor);
1211 PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
1212 aDirtyRect, aBorderArea, canvasColor,
1213 *border, aUsePrintSettings, aBGClipRect);
1216 static PRBool
1217 IsSolidBorderEdge(const nsStyleBorder& aBorder, PRUint32 aSide)
1219 if (aBorder.GetActualBorder().side(aSide) == 0)
1220 return PR_TRUE;
1221 if (aBorder.GetBorderStyle(aSide) != NS_STYLE_BORDER_STYLE_SOLID)
1222 return PR_FALSE;
1224 // If we're using a border image, assume it's not fully opaque,
1225 // because we may not even have the image loaded at this point, and
1226 // even if we did, checking whether the relevant tile is fully
1227 // opaque would be too much work.
1228 if (aBorder.GetBorderImage())
1229 return PR_FALSE;
1231 nscolor color;
1232 PRBool isForeground;
1233 aBorder.GetBorderColor(aSide, color, isForeground);
1235 // We don't know the foreground color here, so if it's being used
1236 // we must assume it might be transparent.
1237 if (isForeground)
1238 return PR_FALSE;
1240 return NS_GET_A(color) == 255;
1244 * Returns true if all border edges are either missing or opaque.
1246 static PRBool
1247 IsSolidBorder(const nsStyleBorder& aBorder)
1249 if (aBorder.mBorderColors ||
1250 nsLayoutUtils::HasNonZeroCorner(aBorder.mBorderRadius))
1251 return PR_FALSE;
1252 for (PRUint32 i = 0; i < 4; ++i) {
1253 if (!IsSolidBorderEdge(aBorder, i))
1254 return PR_FALSE;
1256 return PR_TRUE;
1259 void
1260 nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
1261 nsIRenderingContext& aRenderingContext,
1262 nsIFrame* aForFrame,
1263 const nsRect& aDirtyRect,
1264 const nsRect& aBorderArea,
1265 const nsStyleBackground& aColor,
1266 const nsStyleBorder& aBorder,
1267 PRBool aUsePrintSettings,
1268 nsRect* aBGClipRect)
1270 NS_PRECONDITION(aForFrame,
1271 "Frame is expected to be provided to PaintBackground");
1273 PRBool canDrawBackgroundImage = PR_TRUE;
1274 PRBool canDrawBackgroundColor = PR_TRUE;
1276 if (aUsePrintSettings) {
1277 canDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
1278 canDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
1281 // Check to see if we have an appearance defined. If so, we let the theme
1282 // renderer draw the background and bail out.
1283 const nsStyleDisplay* displayData = aForFrame->GetStyleDisplay();
1284 if (displayData->mAppearance) {
1285 nsITheme *theme = aPresContext->GetTheme();
1286 if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, displayData->mAppearance)) {
1287 nsRect dirty;
1288 dirty.IntersectRect(aDirtyRect, aBorderArea);
1289 theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
1290 displayData->mAppearance, aBorderArea, dirty);
1291 return;
1295 // Same coordinate space as aBorderArea
1296 nsRect bgClipArea;
1297 if (aBGClipRect) {
1298 bgClipArea = *aBGClipRect;
1300 else {
1301 // The background is rendered over the 'background-clip' area.
1302 bgClipArea = aBorderArea;
1303 // If the border is solid, then clip the background to the padding-box
1304 // so that we don't draw unnecessary tiles.
1305 if (aColor.mBackgroundClip != NS_STYLE_BG_CLIP_BORDER ||
1306 IsSolidBorder(aBorder)) {
1307 nsMargin border = aForFrame->GetUsedBorder();
1308 aForFrame->ApplySkipSides(border);
1309 bgClipArea.Deflate(border);
1313 gfxContext *ctx = aRenderingContext.ThebesContext();
1315 // The actual dirty rect is the intersection of the 'background-clip'
1316 // area and the dirty rect we were given
1317 nsRect dirtyRect;
1318 if (!dirtyRect.IntersectRect(bgClipArea, aDirtyRect)) {
1319 // Nothing to paint
1320 return;
1323 // if there is no background image or background images are turned off, try a color.
1324 if (!aColor.mBackgroundImage || !canDrawBackgroundImage) {
1325 PaintBackgroundColor(aPresContext, aRenderingContext, aForFrame, bgClipArea,
1326 aColor, aBorder, canDrawBackgroundColor);
1327 return;
1330 // We have a background image
1332 // Lookup the image
1333 imgIRequest *req = aPresContext->LoadImage(aColor.mBackgroundImage,
1334 aForFrame);
1336 PRUint32 status = imgIRequest::STATUS_ERROR;
1337 if (req)
1338 req->GetImageStatus(&status);
1340 if (!req || !(status & imgIRequest::STATUS_FRAME_COMPLETE) || !(status & imgIRequest::STATUS_SIZE_AVAILABLE)) {
1341 PaintBackgroundColor(aPresContext, aRenderingContext, aForFrame, bgClipArea,
1342 aColor, aBorder, canDrawBackgroundColor);
1343 return;
1346 nsCOMPtr<imgIContainer> image;
1347 req->GetImage(getter_AddRefs(image));
1349 nsSize imageSize;
1350 image->GetWidth(&imageSize.width);
1351 image->GetHeight(&imageSize.height);
1353 imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width);
1354 imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height);
1356 req = nsnull;
1358 // relative to aBorderArea
1359 nsRect bgOriginRect;
1361 nsIAtom* frameType = aForFrame->GetType();
1362 nsIFrame* geometryFrame = aForFrame;
1363 if (frameType == nsGkAtoms::inlineFrame ||
1364 frameType == nsGkAtoms::positionedInlineFrame) {
1365 switch (aColor.mBackgroundInlinePolicy) {
1366 case NS_STYLE_BG_INLINE_POLICY_EACH_BOX:
1367 bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size());
1368 break;
1369 case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:
1370 bgOriginRect = gInlineBGData->GetBoundingRect(aForFrame);
1371 break;
1372 default:
1373 NS_ERROR("Unknown background-inline-policy value! "
1374 "Please, teach me what to do.");
1375 case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS:
1376 bgOriginRect = gInlineBGData->GetContinuousRect(aForFrame);
1377 break;
1379 } else if (frameType == nsGkAtoms::canvasFrame) {
1380 geometryFrame = aForFrame->GetFirstChild(nsnull);
1381 NS_ASSERTION(geometryFrame, "A canvas with a background "
1382 "image had no child frame, which is impossible according to CSS. "
1383 "Make sure there isn't a background image specified on the "
1384 "|:viewport| pseudo-element in |html.css|.");
1385 bgOriginRect = geometryFrame->GetRect();
1386 } else {
1387 bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size());
1390 // Background images are tiled over the 'background-clip' area
1391 // but the origin of the tiling is based on the 'background-origin' area
1392 if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_BORDER) {
1393 nsMargin border = geometryFrame->GetUsedBorder();
1394 geometryFrame->ApplySkipSides(border);
1395 bgOriginRect.Deflate(border);
1396 if (aColor.mBackgroundOrigin != NS_STYLE_BG_ORIGIN_PADDING) {
1397 nsMargin padding = geometryFrame->GetUsedPadding();
1398 geometryFrame->ApplySkipSides(padding);
1399 bgOriginRect.Deflate(padding);
1400 NS_ASSERTION(aColor.mBackgroundOrigin == NS_STYLE_BG_ORIGIN_CONTENT,
1401 "unknown background-origin value");
1405 PRBool needBackgroundColor = NS_GET_A(aColor.mBackgroundColor) > 0;
1406 PRIntn repeat = aColor.mBackgroundRepeat;
1408 switch (repeat) {
1409 case NS_STYLE_BG_REPEAT_X:
1410 break;
1411 case NS_STYLE_BG_REPEAT_Y:
1412 break;
1413 case NS_STYLE_BG_REPEAT_XY:
1414 if (needBackgroundColor) {
1415 // If the image is completely opaque, we do not need to paint the
1416 // background color
1417 nsCOMPtr<gfxIImageFrame> gfxImgFrame;
1418 image->GetCurrentFrame(getter_AddRefs(gfxImgFrame));
1419 if (gfxImgFrame) {
1420 gfxImgFrame->GetNeedsBackground(&needBackgroundColor);
1422 /* check for tiling of a image where frame smaller than container */
1423 nsSize iSize;
1424 image->GetWidth(&iSize.width);
1425 image->GetHeight(&iSize.height);
1426 nsRect iframeRect;
1427 gfxImgFrame->GetRect(iframeRect);
1428 if (iSize.width != iframeRect.width ||
1429 iSize.height != iframeRect.height) {
1430 needBackgroundColor = PR_TRUE;
1434 break;
1435 case NS_STYLE_BG_REPEAT_OFF:
1436 default:
1437 NS_ASSERTION(repeat == NS_STYLE_BG_REPEAT_OFF, "unknown background-repeat value");
1438 break;
1441 // The background color is rendered over the 'background-clip' area
1442 if (needBackgroundColor) {
1443 PaintBackgroundColor(aPresContext, aRenderingContext, aForFrame, bgClipArea,
1444 aColor, aBorder, canDrawBackgroundColor);
1447 // Compute the anchor point.
1449 // relative to aBorderArea.TopLeft() (which is where the top-left
1450 // of aForFrame's border-box will be rendered)
1451 nsPoint imageTopLeft, anchor;
1452 if (NS_STYLE_BG_ATTACHMENT_FIXED == aColor.mBackgroundAttachment) {
1453 // If it's a fixed background attachment, then the image is placed
1454 // relative to the viewport, which is the area of the root frame
1455 // in a screen context or the page content frame in a print context.
1457 // Remember that we've drawn position-varying content in this prescontext
1458 aPresContext->SetRenderedPositionVaryingContent();
1460 nsIFrame* topFrame =
1461 aPresContext->PresShell()->FrameManager()->GetRootFrame();
1462 NS_ASSERTION(topFrame, "no root frame");
1463 nsIFrame* pageContentFrame = nsnull;
1464 if (aPresContext->IsPaginated()) {
1465 pageContentFrame =
1466 nsLayoutUtils::GetClosestFrameOfType(aForFrame, nsGkAtoms::pageContentFrame);
1467 if (pageContentFrame) {
1468 topFrame = pageContentFrame;
1470 // else this is an embedded shell and its root frame is what we want
1473 nsRect viewportArea(nsPoint(0, 0), topFrame->GetSize());
1475 if (!pageContentFrame) {
1476 // Subtract the size of scrollbars.
1477 nsIScrollableFrame* scrollableFrame =
1478 aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
1479 if (scrollableFrame) {
1480 nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes();
1481 viewportArea.Deflate(scrollbars);
1485 // Get the anchor point, relative to the viewport.
1486 ComputeBackgroundAnchorPoint(aColor, viewportArea.Size(), imageSize,
1487 &imageTopLeft, &anchor);
1489 // Convert the anchor point from viewport coordinates to aForFrame
1490 // coordinates.
1491 nsPoint offset = viewportArea.TopLeft() - aForFrame->GetOffsetTo(topFrame);
1492 imageTopLeft += offset;
1493 anchor += offset;
1494 } else {
1495 ComputeBackgroundAnchorPoint(aColor, bgOriginRect.Size(), imageSize,
1496 &imageTopLeft, &anchor);
1497 imageTopLeft += bgOriginRect.TopLeft();
1498 anchor += bgOriginRect.TopLeft();
1501 ctx->Save();
1503 nscoord borderRadii[8];
1504 PRBool haveRadius = GetBorderRadiusTwips(aBorder.mBorderRadius,
1505 aForFrame->GetSize().width,
1506 borderRadii);
1507 if (haveRadius) {
1508 nscoord appUnitsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1509 gfxCornerSizes radii;
1510 ComputePixelRadii(borderRadii, bgClipArea,
1511 aForFrame ? aForFrame->GetSkipSides() : 0,
1512 appUnitsPerPixel, &radii);
1514 gfxRect oRect(RectToGfxRect(bgClipArea, appUnitsPerPixel));
1515 oRect.Round();
1516 oRect.Condition();
1518 ctx->NewPath();
1519 ctx->RoundedRectangle(oRect, radii);
1520 ctx->Clip();
1523 nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize);
1524 nsRect fillArea = destArea;
1525 if (repeat & NS_STYLE_BG_REPEAT_X) {
1526 fillArea.x = bgClipArea.x;
1527 fillArea.width = bgClipArea.width;
1529 if (repeat & NS_STYLE_BG_REPEAT_Y) {
1530 fillArea.y = bgClipArea.y;
1531 fillArea.height = bgClipArea.height;
1533 fillArea.IntersectRect(fillArea, bgClipArea);
1535 nsLayoutUtils::DrawImage(&aRenderingContext, image,
1536 destArea, fillArea, anchor + aBorderArea.TopLeft(), dirtyRect);
1538 ctx->Restore();
1541 static void
1542 DrawBorderImage(nsPresContext* aPresContext,
1543 nsIRenderingContext& aRenderingContext,
1544 nsIFrame* aForFrame, const nsRect& aBorderArea,
1545 const nsStyleBorder& aBorderStyle)
1547 float percent;
1548 nsStyleCoord borderImageSplit[4];
1549 PRInt32 borderImageSplitInt[4];
1550 nsMargin border;
1551 gfxFloat borderTop, borderRight, borderBottom, borderLeft;
1552 gfxFloat borderImageSplitGfx[4];
1554 border = aBorderStyle.GetActualBorder();
1555 if ((0 == border.left) && (0 == border.right) &&
1556 (0 == border.top) && (0 == border.bottom)) {
1557 // Empty border area
1558 return;
1561 borderImageSplit[NS_SIDE_TOP] = aBorderStyle.mBorderImageSplit.GetTop();
1562 borderImageSplit[NS_SIDE_RIGHT] = aBorderStyle.mBorderImageSplit.GetRight();
1563 borderImageSplit[NS_SIDE_BOTTOM] = aBorderStyle.mBorderImageSplit.GetBottom();
1564 borderImageSplit[NS_SIDE_LEFT] = aBorderStyle.mBorderImageSplit.GetLeft();
1566 imgIRequest *req = aPresContext->LoadBorderImage(aBorderStyle.GetBorderImage(), aForFrame);
1568 nsCOMPtr<imgIContainer> image;
1569 req->GetImage(getter_AddRefs(image));
1571 nsSize imageSize;
1572 image->GetWidth(&imageSize.width);
1573 image->GetHeight(&imageSize.height);
1574 imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageSize.width);
1575 imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageSize.height);
1577 // convert percentage values
1578 NS_FOR_CSS_SIDES(side) {
1579 borderImageSplitInt[side] = 0;
1580 switch (borderImageSplit[side].GetUnit()) {
1581 case eStyleUnit_Percent:
1582 percent = borderImageSplit[side].GetPercentValue();
1583 if (side == NS_SIDE_TOP || side == NS_SIDE_BOTTOM)
1584 borderImageSplitInt[side] = (nscoord)(percent * imageSize.height);
1585 else
1586 borderImageSplitInt[side] = (nscoord)(percent * imageSize.width);
1587 break;
1588 case eStyleUnit_Integer:
1589 borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].
1590 GetIntValue());
1591 break;
1592 case eStyleUnit_Factor:
1593 borderImageSplitInt[side] = nsPresContext::CSSPixelsToAppUnits(borderImageSplit[side].GetFactorValue());
1594 break;
1595 default:
1596 break;
1600 gfxContext *thebesCtx = aRenderingContext.ThebesContext();
1601 nsCOMPtr<nsIDeviceContext> dc;
1602 aRenderingContext.GetDeviceContext(*getter_AddRefs(dc));
1604 NS_FOR_CSS_SIDES(side) {
1605 borderImageSplitGfx[side] = nsPresContext::AppUnitsToFloatCSSPixels(borderImageSplitInt[side]);
1608 borderTop = dc->AppUnitsToGfxUnits(border.top);
1609 borderRight = dc->AppUnitsToGfxUnits(border.right);
1610 borderBottom = dc->AppUnitsToGfxUnits(border.bottom);
1611 borderLeft = dc->AppUnitsToGfxUnits(border.left);
1613 gfxSize gfxImageSize;
1614 gfxImageSize.width = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.width);
1615 gfxImageSize.height = nsPresContext::AppUnitsToFloatCSSPixels(imageSize.height);
1617 nsRect outerRect(aBorderArea);
1618 gfxRect rectToDraw,
1619 rectToDrawSource;
1621 gfxRect clipRect;
1622 clipRect.pos.x = dc->AppUnitsToGfxUnits(outerRect.x);
1623 clipRect.pos.y = dc->AppUnitsToGfxUnits(outerRect.y);
1624 clipRect.size.width = dc->AppUnitsToGfxUnits(outerRect.width);
1625 clipRect.size.height = dc->AppUnitsToGfxUnits(outerRect.height);
1626 if (thebesCtx->UserToDevicePixelSnapped(clipRect))
1627 clipRect = thebesCtx->DeviceToUser(clipRect);
1629 thebesCtx->Save();
1630 thebesCtx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
1632 gfxSize middleSize(clipRect.size.width - (borderLeft + borderRight),
1633 clipRect.size.height - (borderTop + borderBottom));
1635 // middle size in source space
1636 gfxIntSize middleSizeSource(gfxImageSize.width - (borderImageSplitGfx[NS_SIDE_RIGHT] + borderImageSplitGfx[NS_SIDE_LEFT]),
1637 gfxImageSize.height - (borderImageSplitGfx[NS_SIDE_TOP] + borderImageSplitGfx[NS_SIDE_BOTTOM]));
1639 gfxSize interSizeTop, interSizeBottom, interSizeLeft, interSizeRight,
1640 interSizeMiddle;
1641 gfxFloat topScale = borderTop/borderImageSplitGfx[NS_SIDE_TOP];
1642 gfxFloat bottomScale = borderBottom/borderImageSplitGfx[NS_SIDE_BOTTOM];
1643 gfxFloat leftScale = borderLeft/borderImageSplitGfx[NS_SIDE_LEFT];
1644 gfxFloat rightScale = borderRight/borderImageSplitGfx[NS_SIDE_RIGHT];
1645 gfxFloat middleScaleH,
1646 middleScaleV;
1647 // TODO: check for nan and properly check for inf
1648 if (topScale != 0.0 && borderImageSplitGfx[NS_SIDE_TOP] != 0.0) {
1649 middleScaleH = topScale;
1650 } else if (bottomScale != 0.0 && borderImageSplitGfx[NS_SIDE_BOTTOM] != 0.0) {
1651 middleScaleH = bottomScale;
1652 } else {
1653 middleScaleH = 1.0;
1656 if (leftScale != 0.0 && borderImageSplitGfx[NS_SIDE_LEFT] != 0.0) {
1657 middleScaleV = leftScale;
1658 } else if (rightScale != 0.0 && borderImageSplitGfx[NS_SIDE_RIGHT] != 0.0) {
1659 middleScaleV = rightScale;
1660 } else {
1661 middleScaleV = 1.0;
1664 interSizeTop.height = borderTop;
1665 interSizeTop.width = middleSizeSource.width*topScale;
1667 interSizeBottom.height = borderBottom;
1668 interSizeBottom.width = middleSizeSource.width*bottomScale;
1670 interSizeLeft.width = borderLeft;
1671 interSizeLeft.height = middleSizeSource.height*leftScale;
1673 interSizeRight.width = borderRight;
1674 interSizeRight.height = middleSizeSource.height*rightScale;
1676 interSizeMiddle.width = middleSizeSource.width*middleScaleH;
1677 interSizeMiddle.height = middleSizeSource.height*middleScaleV;
1679 // draw top left corner
1680 rectToDraw = clipRect;
1681 rectToDraw.size.width = borderLeft;
1682 rectToDraw.size.height = borderTop;
1683 rectToDrawSource.pos.x = 0;
1684 rectToDrawSource.pos.y = 0;
1685 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
1686 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
1687 DrawBorderImageSide(thebesCtx, dc, image,
1688 rectToDraw, rectToDraw.size, rectToDrawSource,
1689 NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
1691 // draw top
1692 rectToDraw = clipRect;
1693 rectToDraw.pos.x += borderLeft;
1694 rectToDraw.size.width = middleSize.width;
1695 rectToDraw.size.height = borderTop;
1696 rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
1697 rectToDrawSource.pos.y = 0;
1698 rectToDrawSource.size.width = middleSizeSource.width;
1699 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
1700 DrawBorderImageSide(thebesCtx, dc, image,
1701 rectToDraw, interSizeTop, rectToDrawSource,
1702 aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH);
1704 // draw top right corner
1705 rectToDraw = clipRect;
1706 rectToDraw.pos.x += clipRect.size.width - borderRight;
1707 rectToDraw.size.width = borderRight;
1708 rectToDraw.size.height = borderTop;
1709 rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
1710 rectToDrawSource.pos.y = 0;
1711 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
1712 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_TOP];
1713 DrawBorderImageSide(thebesCtx, dc, image,
1714 rectToDraw, rectToDraw.size, rectToDrawSource,
1715 NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
1717 // draw right
1718 rectToDraw = clipRect;
1719 rectToDraw.pos.x += clipRect.size.width - borderRight;
1720 rectToDraw.pos.y += borderTop;
1721 rectToDraw.size.width = borderRight;
1722 rectToDraw.size.height = middleSize.height;
1723 rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
1724 rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
1725 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
1726 rectToDrawSource.size.height = middleSizeSource.height;
1727 DrawBorderImageSide(thebesCtx, dc, image,
1728 rectToDraw, interSizeRight, rectToDrawSource,
1729 NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill);
1731 // draw bottom right corner
1732 rectToDraw = clipRect;
1733 rectToDraw.pos.x += clipRect.size.width - borderRight;
1734 rectToDraw.pos.y += clipRect.size.height - borderBottom;
1735 rectToDraw.size.width = borderRight;
1736 rectToDraw.size.height = borderBottom;
1737 rectToDrawSource.pos.x = gfxImageSize.width - borderImageSplitGfx[NS_SIDE_RIGHT];
1738 rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
1739 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_RIGHT];
1740 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
1741 DrawBorderImageSide(thebesCtx, dc, image,
1742 rectToDraw, rectToDraw.size, rectToDrawSource,
1743 NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
1745 // draw bottom
1746 rectToDraw = clipRect;
1747 rectToDraw.pos.x += borderLeft;
1748 rectToDraw.pos.y += clipRect.size.height - borderBottom;
1749 rectToDraw.size.width = middleSize.width;
1750 rectToDraw.size.height = borderBottom;
1751 rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
1752 rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
1753 rectToDrawSource.size.width = middleSizeSource.width;
1754 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
1755 DrawBorderImageSide(thebesCtx, dc, image,
1756 rectToDraw, interSizeBottom, rectToDrawSource,
1757 aBorderStyle.mBorderImageHFill, NS_STYLE_BORDER_IMAGE_STRETCH);
1759 // draw bottom left corner
1760 rectToDraw = clipRect;
1761 rectToDraw.pos.y += clipRect.size.height - borderBottom;
1762 rectToDraw.size.width = borderLeft;
1763 rectToDraw.size.height = borderBottom;
1764 rectToDrawSource.pos.x = 0;
1765 rectToDrawSource.pos.y = gfxImageSize.height - borderImageSplitGfx[NS_SIDE_BOTTOM];
1766 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
1767 rectToDrawSource.size.height = borderImageSplitGfx[NS_SIDE_BOTTOM];
1768 DrawBorderImageSide(thebesCtx, dc, image,
1769 rectToDraw, rectToDraw.size, rectToDrawSource,
1770 NS_STYLE_BORDER_IMAGE_STRETCH, NS_STYLE_BORDER_IMAGE_STRETCH);
1772 // draw left
1773 rectToDraw = clipRect;
1774 rectToDraw.pos.y += borderTop;
1775 rectToDraw.size.width = borderLeft;
1776 rectToDraw.size.height = middleSize.height;
1777 rectToDrawSource.pos.x = 0;
1778 rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
1779 rectToDrawSource.size.width = borderImageSplitGfx[NS_SIDE_LEFT];
1780 rectToDrawSource.size.height = middleSizeSource.height;
1781 DrawBorderImageSide(thebesCtx, dc, image,
1782 rectToDraw, interSizeLeft, rectToDrawSource,
1783 NS_STYLE_BORDER_IMAGE_STRETCH, aBorderStyle.mBorderImageVFill);
1785 // Draw middle
1786 rectToDraw = clipRect;
1787 rectToDraw.pos.x += borderLeft;
1788 rectToDraw.pos.y += borderTop;
1789 rectToDraw.size.width = middleSize.width;
1790 rectToDraw.size.height = middleSize.height;
1791 rectToDrawSource.pos.x = borderImageSplitGfx[NS_SIDE_LEFT];
1792 rectToDrawSource.pos.y = borderImageSplitGfx[NS_SIDE_TOP];
1793 rectToDrawSource.size = middleSizeSource;
1794 DrawBorderImageSide(thebesCtx, dc, image,
1795 rectToDraw, interSizeMiddle, rectToDrawSource,
1796 aBorderStyle.mBorderImageHFill, aBorderStyle.mBorderImageVFill);
1798 thebesCtx->PopGroupToSource();
1799 thebesCtx->SetOperator(gfxContext::OPERATOR_OVER);
1800 thebesCtx->Paint();
1801 thebesCtx->Restore();
1804 static void
1805 DrawBorderImageSide(gfxContext *aThebesContext,
1806 nsIDeviceContext* aDeviceContext,
1807 imgIContainer* aImage,
1808 gfxRect& aDestRect,
1809 gfxSize aInterSize, // non-ref to allow aliasing
1810 gfxRect& aSourceRect,
1811 PRUint8 aHFillType,
1812 PRUint8 aVFillType)
1814 if (aDestRect.size.width < 1.0 || aDestRect.size.height < 1.0 ||
1815 aSourceRect.size.width < 1.0 || aSourceRect.size.height < 1.0) {
1816 return;
1819 gfxIntSize gfxSourceSize((PRInt32)aSourceRect.size.width,
1820 (PRInt32)aSourceRect.size.height);
1822 // where the actual border ends up being rendered
1823 if (aThebesContext->UserToDevicePixelSnapped(aDestRect))
1824 aDestRect = aThebesContext->DeviceToUser(aDestRect);
1825 if (aThebesContext->UserToDevicePixelSnapped(aSourceRect))
1826 aSourceRect = aThebesContext->DeviceToUser(aSourceRect);
1828 if (aDestRect.size.height < 1.0 ||
1829 aDestRect.size.width < 1.0)
1830 return;
1832 if (aInterSize.width < 1.0 ||
1833 aInterSize.height < 1.0)
1834 return;
1836 // Surface will hold just the part of the source image specified by the aSourceRect
1837 // but at a different size
1838 nsRefPtr<gfxASurface> interSurface =
1839 gfxPlatform::GetPlatform()->CreateOffscreenSurface(
1840 gfxSourceSize, gfxASurface::ImageFormatARGB32);
1842 gfxMatrix srcMatrix;
1843 // Adjust the matrix scale for Step 1 of the spec
1844 srcMatrix.Scale(aSourceRect.size.width/aInterSize.width,
1845 aSourceRect.size.height/aInterSize.height);
1847 nsCOMPtr<gfxIImageFrame> frame;
1848 nsresult rv = aImage->GetCurrentFrame(getter_AddRefs(frame));
1849 if(NS_FAILED(rv))
1850 return;
1851 nsCOMPtr<nsIImage> image;
1852 image = do_GetInterface(frame);
1853 if(!image)
1854 return;
1856 // surface for the whole image
1857 nsRefPtr<gfxPattern> imagePattern;
1858 rv = image->GetPattern(getter_AddRefs(imagePattern));
1859 if(NS_FAILED(rv) || !imagePattern)
1860 return;
1862 gfxMatrix mat;
1863 mat.Translate(aSourceRect.pos);
1864 imagePattern->SetMatrix(mat);
1866 // Straightforward blit - no resizing
1867 nsRefPtr<gfxContext> srcCtx = new gfxContext(interSurface);
1868 srcCtx->SetPattern(imagePattern);
1869 srcCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
1870 srcCtx->Paint();
1871 srcCtx = nsnull;
1875 // offset to make the middle tile centered in the middle of the border
1876 gfxPoint renderOffset(0, 0);
1877 gfxSize rectSize(aDestRect.size);
1879 aThebesContext->Save();
1880 aThebesContext->Clip(aDestRect);
1882 gfxFloat hScale(1.0), vScale(1.0);
1884 nsRefPtr<gfxPattern> pattern = new gfxPattern(interSurface);
1885 pattern->SetExtend(gfxPattern::EXTEND_PAD_EDGE);
1886 switch (aHFillType) {
1887 case NS_STYLE_BORDER_IMAGE_REPEAT:
1888 renderOffset.x = (rectSize.width - aInterSize.width*NS_ceil(rectSize.width/aInterSize.width))*-0.5;
1889 aDestRect.pos.x -= renderOffset.x;
1890 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
1891 break;
1892 case NS_STYLE_BORDER_IMAGE_ROUND:
1893 hScale = aInterSize.width*(NS_ceil(aDestRect.size.width/aInterSize.width)/aDestRect.size.width);
1894 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
1895 break;
1896 case NS_STYLE_BORDER_IMAGE_STRETCH:
1897 default:
1898 hScale = aInterSize.width/aDestRect.size.width;
1899 break;
1902 switch (aVFillType) {
1903 case NS_STYLE_BORDER_IMAGE_REPEAT:
1904 renderOffset.y = (rectSize.height - aInterSize.height*NS_ceil(rectSize.height/aInterSize.height))*-0.5;
1905 aDestRect.pos.y -= renderOffset.y;
1906 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
1907 break;
1908 case NS_STYLE_BORDER_IMAGE_ROUND:
1909 vScale = aInterSize.height*(NS_ceil(aDestRect.size.height/aInterSize.height)/aDestRect.size.height);
1910 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
1911 break;
1912 case NS_STYLE_BORDER_IMAGE_STRETCH:
1913 default:
1914 vScale = aInterSize.height/aDestRect.size.height;
1915 break;
1918 // Adjust the matrix scale for Step 2 of the spec
1919 srcMatrix.Scale(hScale,vScale);
1920 pattern->SetMatrix(srcMatrix);
1922 // render
1923 aThebesContext->Translate(aDestRect.pos);
1924 aThebesContext->SetPattern(pattern);
1925 aThebesContext->NewPath();
1926 aThebesContext->Rectangle(gfxRect(renderOffset, rectSize));
1927 aThebesContext->SetOperator(gfxContext::OPERATOR_ADD);
1928 aThebesContext->Fill();
1929 aThebesContext->Restore();
1932 static void
1933 PaintBackgroundColor(nsPresContext* aPresContext,
1934 nsIRenderingContext& aRenderingContext,
1935 nsIFrame* aForFrame,
1936 const nsRect& aBgClipArea,
1937 const nsStyleBackground& aColor,
1938 const nsStyleBorder& aBorder,
1939 PRBool aCanPaintNonWhite)
1941 // If we're only allowed to paint white, then don't bail out on transparent
1942 // color if we're not completely transparent. See the corresponding check
1943 // for whether we're allowed to paint background images in
1944 // PaintBackgroundWithSC before the first call to PaintBackgroundColor.
1945 if (NS_GET_A(aColor.mBackgroundColor) == 0 &&
1946 (aCanPaintNonWhite || aColor.IsTransparent())) {
1947 // nothing to paint
1948 return;
1951 nscolor color = aColor.mBackgroundColor;
1952 if (!aCanPaintNonWhite) {
1953 color = NS_RGB(255, 255, 255);
1955 aRenderingContext.SetColor(color);
1957 if (!nsLayoutUtils::HasNonZeroCorner(aBorder.mBorderRadius)) {
1958 aRenderingContext.FillRect(aBgClipArea);
1959 return;
1962 gfxContext *ctx = aRenderingContext.ThebesContext();
1964 // needed for our border thickness
1965 nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
1967 nscoord borderRadii[8];
1968 GetBorderRadiusTwips(aBorder.mBorderRadius, aForFrame->GetSize().width,
1969 borderRadii);
1971 // the bgClipArea is the outside
1972 gfxRect oRect(RectToGfxRect(aBgClipArea, appUnitsPerPixel));
1973 oRect.Round();
1974 oRect.Condition();
1975 if (oRect.IsEmpty())
1976 return;
1978 // convert the radii
1979 gfxCornerSizes radii;
1980 ComputePixelRadii(borderRadii, aBgClipArea,
1981 aForFrame ? aForFrame->GetSkipSides() : 0,
1982 appUnitsPerPixel, &radii);
1984 // Add 1.0 to any border radii; if we don't, the border and background
1985 // curves will combine to have fringing at the rounded corners. Since
1986 // alpha is used for coverage, we have problems because the border and
1987 // background should have identical coverage, and the border should
1988 // overlay the background exactly. The way to avoid this is by using
1989 // a supersampling scheme, but we don't have the mechanism in place to do
1990 // this. So, this will do for now.
1991 for (int i = 0; i < 4; i++) {
1992 if (radii[i].width > 0.0)
1993 radii[i].width += 1.0;
1994 if (radii[i].height > 0.0)
1995 radii[i].height += 1.0;
1998 ctx->NewPath();
1999 ctx->RoundedRectangle(oRect, radii);
2000 ctx->Fill();
2004 // Begin table border-collapsing section
2005 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
2006 // At some point, all functions should be unified to include the additional functionality that these provide
2008 static nscoord
2009 RoundIntToPixel(nscoord aValue,
2010 nscoord aTwipsPerPixel,
2011 PRBool aRoundDown = PR_FALSE)
2013 if (aTwipsPerPixel <= 0)
2014 // We must be rendering to a device that has a resolution greater than Twips!
2015 // In that case, aValue is as accurate as it's going to get.
2016 return aValue;
2018 nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
2019 nscoord extra = aValue % aTwipsPerPixel;
2020 nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
2021 return finalValue;
2024 static nscoord
2025 RoundFloatToPixel(float aValue,
2026 nscoord aTwipsPerPixel,
2027 PRBool aRoundDown = PR_FALSE)
2029 return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
2032 static void
2033 SetPoly(const nsRect& aRect,
2034 nsPoint* poly)
2036 poly[0].x = aRect.x;
2037 poly[0].y = aRect.y;
2038 poly[1].x = aRect.x + aRect.width;
2039 poly[1].y = aRect.y;
2040 poly[2].x = aRect.x + aRect.width;
2041 poly[2].y = aRect.y + aRect.height;
2042 poly[3].x = aRect.x;
2043 poly[3].y = aRect.y + aRect.height;
2044 poly[4].x = aRect.x;
2045 poly[4].y = aRect.y;
2048 static void
2049 DrawSolidBorderSegment(nsIRenderingContext& aContext,
2050 nsRect aRect,
2051 nscoord aTwipsPerPixel,
2052 PRUint8 aStartBevelSide = 0,
2053 nscoord aStartBevelOffset = 0,
2054 PRUint8 aEndBevelSide = 0,
2055 nscoord aEndBevelOffset = 0)
2058 if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) ||
2059 ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
2060 // simple line or rectangle
2061 if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) {
2062 if (1 == aRect.height)
2063 aContext.DrawLine(aRect.x, aRect.y, aRect.x, aRect.y + aRect.height);
2064 else
2065 aContext.FillRect(aRect);
2067 else {
2068 if (1 == aRect.width)
2069 aContext.DrawLine(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y);
2070 else
2071 aContext.FillRect(aRect);
2074 else {
2075 // polygon with beveling
2076 nsPoint poly[5];
2077 SetPoly(aRect, poly);
2078 switch(aStartBevelSide) {
2079 case NS_SIDE_TOP:
2080 poly[0].x += aStartBevelOffset;
2081 poly[4].x = poly[0].x;
2082 break;
2083 case NS_SIDE_BOTTOM:
2084 poly[3].x += aStartBevelOffset;
2085 break;
2086 case NS_SIDE_RIGHT:
2087 poly[1].y += aStartBevelOffset;
2088 break;
2089 case NS_SIDE_LEFT:
2090 poly[0].y += aStartBevelOffset;
2091 poly[4].y = poly[0].y;
2094 switch(aEndBevelSide) {
2095 case NS_SIDE_TOP:
2096 poly[1].x -= aEndBevelOffset;
2097 break;
2098 case NS_SIDE_BOTTOM:
2099 poly[2].x -= aEndBevelOffset;
2100 break;
2101 case NS_SIDE_RIGHT:
2102 poly[2].y -= aEndBevelOffset;
2103 break;
2104 case NS_SIDE_LEFT:
2105 poly[3].y -= aEndBevelOffset;
2108 aContext.FillPolygon(poly, 5);
2114 static void
2115 GetDashInfo(nscoord aBorderLength,
2116 nscoord aDashLength,
2117 nscoord aTwipsPerPixel,
2118 PRInt32& aNumDashSpaces,
2119 nscoord& aStartDashLength,
2120 nscoord& aEndDashLength)
2122 aNumDashSpaces = 0;
2123 if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
2124 aStartDashLength = aBorderLength;
2125 aEndDashLength = 0;
2127 else {
2128 aNumDashSpaces = aBorderLength / (2 * aDashLength); // round down
2129 nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
2130 if (extra > 0) {
2131 nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
2132 aStartDashLength += half;
2133 aEndDashLength += (extra - half);
2138 void
2139 nsCSSRendering::DrawTableBorderSegment(nsIRenderingContext& aContext,
2140 PRUint8 aBorderStyle,
2141 nscolor aBorderColor,
2142 const nsStyleBackground* aBGColor,
2143 const nsRect& aBorder,
2144 PRInt32 aAppUnitsPerCSSPixel,
2145 PRUint8 aStartBevelSide,
2146 nscoord aStartBevelOffset,
2147 PRUint8 aEndBevelSide,
2148 nscoord aEndBevelOffset)
2150 aContext.SetColor (aBorderColor);
2152 PRBool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide));
2153 nscoord twipsPerPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel);
2154 PRUint8 ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
2156 if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) ||
2157 (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
2158 // no beveling for 1 pixel border, dash or dot
2159 aStartBevelOffset = 0;
2160 aEndBevelOffset = 0;
2163 gfxContext *ctx = aContext.ThebesContext();
2164 gfxContext::AntialiasMode oldMode = ctx->CurrentAntialiasMode();
2165 ctx->SetAntialiasMode(gfxContext::MODE_ALIASED);
2167 switch (aBorderStyle) {
2168 case NS_STYLE_BORDER_STYLE_NONE:
2169 case NS_STYLE_BORDER_STYLE_HIDDEN:
2170 //NS_ASSERTION(PR_FALSE, "style of none or hidden");
2171 break;
2172 case NS_STYLE_BORDER_STYLE_DOTTED:
2173 case NS_STYLE_BORDER_STYLE_DASHED:
2175 nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
2176 // make the dash length proportional to the border thickness
2177 dashLength *= (horizontal) ? aBorder.height : aBorder.width;
2178 // make the min dash length for the ends 1/2 the dash length
2179 nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle)
2180 ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength;
2181 minDashLength = PR_MAX(minDashLength, twipsPerPixel);
2182 nscoord numDashSpaces = 0;
2183 nscoord startDashLength = minDashLength;
2184 nscoord endDashLength = minDashLength;
2185 if (horizontal) {
2186 GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
2187 nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
2188 DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
2189 for (PRInt32 spaceX = 0; spaceX < numDashSpaces; spaceX++) {
2190 rect.x += rect.width + dashLength;
2191 rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength;
2192 DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
2195 else {
2196 GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
2197 nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
2198 DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
2199 for (PRInt32 spaceY = 0; spaceY < numDashSpaces; spaceY++) {
2200 rect.y += rect.height + dashLength;
2201 rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength;
2202 DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
2206 break;
2207 case NS_STYLE_BORDER_STYLE_GROOVE:
2208 ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
2209 case NS_STYLE_BORDER_STYLE_RIDGE:
2210 if ((horizontal && (twipsPerPixel >= aBorder.height)) ||
2211 (!horizontal && (twipsPerPixel >= aBorder.width))) {
2212 // a one pixel border
2213 DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset,
2214 aEndBevelSide, aEndBevelOffset);
2216 else {
2217 nscoord startBevel = (aStartBevelOffset > 0)
2218 ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, PR_TRUE) : 0;
2219 nscoord endBevel = (aEndBevelOffset > 0)
2220 ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, PR_TRUE) : 0;
2221 PRUint8 ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT;
2222 aContext.SetColor (
2223 MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
2224 nsRect rect(aBorder);
2225 nscoord half;
2226 if (horizontal) { // top, bottom
2227 half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
2228 rect.height = half;
2229 if (NS_SIDE_TOP == aStartBevelSide) {
2230 rect.x += startBevel;
2231 rect.width -= startBevel;
2233 if (NS_SIDE_TOP == aEndBevelSide) {
2234 rect.width -= endBevel;
2236 DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
2237 startBevel, aEndBevelSide, endBevel);
2239 else { // left, right
2240 half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel);
2241 rect.width = half;
2242 if (NS_SIDE_LEFT == aStartBevelSide) {
2243 rect.y += startBevel;
2244 rect.height -= startBevel;
2246 if (NS_SIDE_LEFT == aEndBevelSide) {
2247 rect.height -= endBevel;
2249 DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
2250 startBevel, aEndBevelSide, endBevel);
2253 rect = aBorder;
2254 ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT;
2255 aContext.SetColor (
2256 MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
2257 if (horizontal) {
2258 rect.y = rect.y + half;
2259 rect.height = aBorder.height - half;
2260 if (NS_SIDE_BOTTOM == aStartBevelSide) {
2261 rect.x += startBevel;
2262 rect.width -= startBevel;
2264 if (NS_SIDE_BOTTOM == aEndBevelSide) {
2265 rect.width -= endBevel;
2267 DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
2268 startBevel, aEndBevelSide, endBevel);
2270 else {
2271 rect.x = rect.x + half;
2272 rect.width = aBorder.width - half;
2273 if (NS_SIDE_RIGHT == aStartBevelSide) {
2274 rect.y += aStartBevelOffset - startBevel;
2275 rect.height -= startBevel;
2277 if (NS_SIDE_RIGHT == aEndBevelSide) {
2278 rect.height -= endBevel;
2280 DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
2281 startBevel, aEndBevelSide, endBevel);
2284 break;
2285 case NS_STYLE_BORDER_STYLE_DOUBLE:
2286 if ((aBorder.width > 2) && (aBorder.height > 2)) {
2287 nscoord startBevel = (aStartBevelOffset > 0)
2288 ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0;
2289 nscoord endBevel = (aEndBevelOffset > 0)
2290 ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0;
2291 if (horizontal) { // top, bottom
2292 nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel);
2294 // draw the top line or rect
2295 nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
2296 if (NS_SIDE_TOP == aStartBevelSide) {
2297 topRect.x += aStartBevelOffset - startBevel;
2298 topRect.width -= aStartBevelOffset - startBevel;
2300 if (NS_SIDE_TOP == aEndBevelSide) {
2301 topRect.width -= aEndBevelOffset - endBevel;
2303 DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide,
2304 startBevel, aEndBevelSide, endBevel);
2306 // draw the botom line or rect
2307 nscoord heightOffset = aBorder.height - thirdHeight;
2308 nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
2309 if (NS_SIDE_BOTTOM == aStartBevelSide) {
2310 bottomRect.x += aStartBevelOffset - startBevel;
2311 bottomRect.width -= aStartBevelOffset - startBevel;
2313 if (NS_SIDE_BOTTOM == aEndBevelSide) {
2314 bottomRect.width -= aEndBevelOffset - endBevel;
2316 DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide,
2317 startBevel, aEndBevelSide, endBevel);
2319 else { // left, right
2320 nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel);
2322 nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
2323 if (NS_SIDE_LEFT == aStartBevelSide) {
2324 leftRect.y += aStartBevelOffset - startBevel;
2325 leftRect.height -= aStartBevelOffset - startBevel;
2327 if (NS_SIDE_LEFT == aEndBevelSide) {
2328 leftRect.height -= aEndBevelOffset - endBevel;
2330 DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide,
2331 startBevel, aEndBevelSide, endBevel);
2333 nscoord widthOffset = aBorder.width - thirdWidth;
2334 nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
2335 if (NS_SIDE_RIGHT == aStartBevelSide) {
2336 rightRect.y += aStartBevelOffset - startBevel;
2337 rightRect.height -= aStartBevelOffset - startBevel;
2339 if (NS_SIDE_RIGHT == aEndBevelSide) {
2340 rightRect.height -= aEndBevelOffset - endBevel;
2342 DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide,
2343 startBevel, aEndBevelSide, endBevel);
2345 break;
2347 // else fall through to solid
2348 case NS_STYLE_BORDER_STYLE_SOLID:
2349 DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide,
2350 aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
2351 break;
2352 case NS_STYLE_BORDER_STYLE_OUTSET:
2353 case NS_STYLE_BORDER_STYLE_INSET:
2354 NS_ASSERTION(PR_FALSE, "inset, outset should have been converted to groove, ridge");
2355 break;
2356 case NS_STYLE_BORDER_STYLE_AUTO:
2357 NS_ASSERTION(PR_FALSE, "Unexpected 'auto' table border");
2358 break;
2361 ctx->SetAntialiasMode(oldMode);
2364 // End table border-collapsing section
2366 void
2367 nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
2368 const nscolor aColor,
2369 const gfxPoint& aPt,
2370 const gfxSize& aLineSize,
2371 const gfxFloat aAscent,
2372 const gfxFloat aOffset,
2373 const PRUint8 aDecoration,
2374 const PRUint8 aStyle)
2376 gfxRect rect =
2377 GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
2378 aDecoration, aStyle);
2379 if (rect.IsEmpty())
2380 return;
2382 if (aDecoration != NS_STYLE_TEXT_DECORATION_UNDERLINE &&
2383 aDecoration != NS_STYLE_TEXT_DECORATION_OVERLINE &&
2384 aDecoration != NS_STYLE_TEXT_DECORATION_LINE_THROUGH)
2386 NS_ERROR("Invalid decoration value!");
2387 return;
2390 gfxFloat lineHeight = PR_MAX(NS_round(aLineSize.height), 1.0);
2391 PRBool contextIsSaved = PR_FALSE;
2393 gfxFloat oldLineWidth;
2394 nsRefPtr<gfxPattern> oldPattern;
2396 switch (aStyle) {
2397 case NS_STYLE_BORDER_STYLE_SOLID:
2398 case NS_STYLE_BORDER_STYLE_DOUBLE:
2399 oldLineWidth = aGfxContext->CurrentLineWidth();
2400 oldPattern = aGfxContext->GetPattern();
2401 break;
2402 case NS_STYLE_BORDER_STYLE_DASHED: {
2403 aGfxContext->Save();
2404 contextIsSaved = PR_TRUE;
2405 gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
2406 gfxFloat dash[2] = { dashWidth, dashWidth };
2407 aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
2408 aGfxContext->SetDash(dash, 2, 0.0);
2409 break;
2411 case NS_STYLE_BORDER_STYLE_DOTTED: {
2412 aGfxContext->Save();
2413 contextIsSaved = PR_TRUE;
2414 gfxFloat dashWidth = lineHeight * DOT_LENGTH;
2415 gfxFloat dash[2];
2416 if (lineHeight > 2.0) {
2417 dash[0] = 0.0;
2418 dash[1] = dashWidth * 2.0;
2419 aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
2420 } else {
2421 dash[0] = dashWidth;
2422 dash[1] = dashWidth;
2424 aGfxContext->SetDash(dash, 2, 0.0);
2425 break;
2427 default:
2428 NS_ERROR("Invalid style value!");
2429 return;
2432 // The y position should be set to the middle of the line.
2433 rect.pos.y += lineHeight / 2;
2435 aGfxContext->SetColor(gfxRGBA(aColor));
2436 aGfxContext->SetLineWidth(lineHeight);
2437 switch (aStyle) {
2438 case NS_STYLE_BORDER_STYLE_SOLID:
2439 aGfxContext->NewPath();
2440 aGfxContext->MoveTo(rect.TopLeft());
2441 aGfxContext->LineTo(rect.TopRight());
2442 aGfxContext->Stroke();
2443 break;
2444 case NS_STYLE_BORDER_STYLE_DOUBLE:
2445 aGfxContext->NewPath();
2446 aGfxContext->MoveTo(rect.TopLeft());
2447 aGfxContext->LineTo(rect.TopRight());
2448 rect.size.height -= lineHeight;
2449 aGfxContext->MoveTo(rect.BottomLeft());
2450 aGfxContext->LineTo(rect.BottomRight());
2451 aGfxContext->Stroke();
2452 break;
2453 case NS_STYLE_BORDER_STYLE_DOTTED:
2454 case NS_STYLE_BORDER_STYLE_DASHED:
2455 aGfxContext->NewPath();
2456 aGfxContext->MoveTo(rect.TopLeft());
2457 aGfxContext->LineTo(rect.TopRight());
2458 aGfxContext->Stroke();
2459 break;
2460 default:
2461 NS_ERROR("Invalid style value!");
2462 break;
2465 if (contextIsSaved) {
2466 aGfxContext->Restore();
2467 } else {
2468 aGfxContext->SetPattern(oldPattern);
2469 aGfxContext->SetLineWidth(oldLineWidth);
2473 nsRect
2474 nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
2475 const gfxSize& aLineSize,
2476 const gfxFloat aAscent,
2477 const gfxFloat aOffset,
2478 const PRUint8 aDecoration,
2479 const PRUint8 aStyle)
2481 NS_ASSERTION(aPresContext, "aPresContext is null");
2483 gfxRect rect =
2484 GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
2485 aDecoration, aStyle);
2486 // The rect values are already rounded to nearest device pixels.
2487 nsRect r;
2488 r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
2489 r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
2490 r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
2491 r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
2492 return r;
2495 static gfxRect
2496 GetTextDecorationRectInternal(const gfxPoint& aPt,
2497 const gfxSize& aLineSize,
2498 const gfxFloat aAscent,
2499 const gfxFloat aOffset,
2500 const PRUint8 aDecoration,
2501 const PRUint8 aStyle)
2503 gfxRect r;
2504 r.pos.x = NS_floor(aPt.x + 0.5);
2505 r.size.width = NS_round(aLineSize.width);
2507 gfxFloat basesize = NS_round(aLineSize.height);
2508 basesize = PR_MAX(basesize, 1.0);
2509 r.size.height = basesize;
2510 if (aStyle == NS_STYLE_BORDER_STYLE_DOUBLE) {
2511 gfxFloat gap = NS_round(basesize / 2.0);
2512 gap = PR_MAX(gap, 1.0);
2513 r.size.height = basesize * 2.0 + gap;
2514 } else {
2515 r.size.height = basesize;
2518 gfxFloat baseline = NS_floor(aPt.y + aAscent + 0.5);
2519 gfxFloat offset = 0;
2520 switch (aDecoration) {
2521 case NS_STYLE_TEXT_DECORATION_UNDERLINE:
2522 offset = aOffset;
2523 break;
2524 case NS_STYLE_TEXT_DECORATION_OVERLINE:
2525 offset = aOffset - basesize + r.Height();
2526 break;
2527 case NS_STYLE_TEXT_DECORATION_LINE_THROUGH: {
2528 gfxFloat extra = NS_floor(r.Height() / 2.0 + 0.5);
2529 extra = PR_MAX(extra, basesize);
2530 offset = aOffset - basesize + extra;
2531 break;
2533 default:
2534 NS_ERROR("Invalid decoration value!");
2536 r.pos.y = baseline - NS_floor(offset + 0.5);
2537 return r;
2540 // -----
2541 // nsContextBoxBlur
2542 // -----
2543 gfxContext*
2544 nsContextBoxBlur::Init(const gfxRect& aRect, nscoord aBlurRadius,
2545 PRInt32 aAppUnitsPerDevPixel,
2546 gfxContext* aDestinationCtx,
2547 const gfxRect& aDirtyRect)
2549 mDestinationCtx = aDestinationCtx;
2551 PRInt32 blurRadius = static_cast<PRInt32>(aBlurRadius / aAppUnitsPerDevPixel);
2553 // if not blurring, draw directly onto the destination device
2554 if (blurRadius <= 0) {
2555 mContext = aDestinationCtx;
2556 return mContext;
2559 // Convert from app units to device pixels
2560 gfxRect rect = aRect;
2561 rect.ScaleInverse(aAppUnitsPerDevPixel);
2563 if (rect.IsEmpty()) {
2564 mContext = aDestinationCtx;
2565 return mContext;
2568 gfxRect dirtyRect = aDirtyRect;
2569 dirtyRect.ScaleInverse(aAppUnitsPerDevPixel);
2571 mDestinationCtx = aDestinationCtx;
2573 // Create the temporary surface for blurring
2574 mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius), &dirtyRect);
2575 return mContext;
2578 void
2579 nsContextBoxBlur::DoPaint()
2581 if (mContext == mDestinationCtx)
2582 return;
2584 blur.Paint(mDestinationCtx);
2587 gfxContext*
2588 nsContextBoxBlur::GetContext()
2590 return mContext;