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
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Mats Palmgren <matspal@gmail.com>
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>
30 : * Jeff Walden <jwalden+code@mit.edu>
31 : *
32 : * Alternatively, the contents of this file may be used under the terms of
33 : * either of the GNU General Public License Version 2 or later (the "GPL"),
34 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 : * in which case the provisions of the GPL or the LGPL are applicable instead
36 : * of those above. If you wish to allow use of your version of this file only
37 : * under the terms of either the GPL or the LGPL, and not to allow others to
38 : * use your version of this file under the terms of the MPL, indicate your
39 : * decision by deleting the provisions above and replace them with the notice
40 : * and other provisions required by the GPL or the LGPL. If you do not delete
41 : * the provisions above, a recipient may use your version of this file under
42 : * the terms of any one of the MPL, the GPL or the LGPL.
43 : *
44 : * ***** END LICENSE BLOCK ***** */
45 :
46 : /* utility functions for drawing borders and backgrounds */
47 :
48 : #include "nsStyleConsts.h"
49 : #include "nsPresContext.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 "nsIContent.h"
61 : #include "nsIDocument.h"
62 : #include "nsIScrollableFrame.h"
63 : #include "imgIRequest.h"
64 : #include "imgIContainer.h"
65 : #include "nsCSSRendering.h"
66 : #include "nsCSSColorUtils.h"
67 : #include "nsITheme.h"
68 : #include "nsThemeConstants.h"
69 : #include "nsIServiceManager.h"
70 : #include "nsLayoutUtils.h"
71 : #include "nsINameSpaceManager.h"
72 : #include "nsBlockFrame.h"
73 : #include "gfxContext.h"
74 : #include "nsIInterfaceRequestorUtils.h"
75 : #include "gfxPlatform.h"
76 : #include "gfxImageSurface.h"
77 : #include "nsStyleStructInlines.h"
78 : #include "nsCSSFrameConstructor.h"
79 : #include "nsCSSProps.h"
80 : #include "nsContentUtils.h"
81 : #include "nsSVGEffects.h"
82 : #include "nsSVGIntegrationUtils.h"
83 : #include "gfxDrawable.h"
84 :
85 : #include "nsCSSRenderingBorders.h"
86 :
87 : using namespace mozilla;
88 :
89 : /**
90 : * This is a small wrapper class to encapsulate image drawing that can draw an
91 : * nsStyleImage image, which may internally be a real image, a sub image, or a
92 : * CSS gradient.
93 : *
94 : * @note Always call the member functions in the order of PrepareImage(),
95 : * ComputeSize(), and Draw().
96 : */
97 : class ImageRenderer {
98 : public:
99 : enum {
100 : FLAG_SYNC_DECODE_IMAGES = 0x01
101 : };
102 : ImageRenderer(nsIFrame* aForFrame, const nsStyleImage* aImage, PRUint32 aFlags);
103 : ~ImageRenderer();
104 : /**
105 : * Populates member variables to get ready for rendering.
106 : * @return true iff the image is ready, and there is at least a pixel to
107 : * draw.
108 : */
109 : bool PrepareImage();
110 : /**
111 : * @return the image size in appunits when rendered, after accounting for the
112 : * background positioning area, background-size, and the image's intrinsic
113 : * dimensions (if any).
114 : */
115 : nsSize ComputeSize(const nsStyleBackground::Size& aLayerSize,
116 : const nsSize& aBgPositioningArea);
117 : /**
118 : * Draws the image to the target rendering context.
119 : * @see nsLayoutUtils::DrawImage() for other parameters
120 : */
121 : void Draw(nsPresContext* aPresContext,
122 : nsRenderingContext& aRenderingContext,
123 : const nsRect& aDest,
124 : const nsRect& aFill,
125 : const nsPoint& aAnchor,
126 : const nsRect& aDirty);
127 :
128 : private:
129 : /*
130 : * Compute the "unscaled" dimensions of the image in aUnscaled{Width,Height}
131 : * and aRatio. Whether the image has a height and width are indicated by
132 : * aHaveWidth and aHaveHeight. If the image doesn't have a ratio, aRatio will
133 : * be (0, 0).
134 : */
135 : void ComputeUnscaledDimensions(const nsSize& aBgPositioningArea,
136 : nscoord& aUnscaledWidth, bool& aHaveWidth,
137 : nscoord& aUnscaledHeight, bool& aHaveHeight,
138 : nsSize& aRatio);
139 :
140 : /*
141 : * Using the previously-computed unscaled width and height (if each are
142 : * valid, as indicated by aHaveWidth/aHaveHeight), compute the size at which
143 : * the image should actually render.
144 : */
145 : nsSize
146 : ComputeDrawnSize(const nsStyleBackground::Size& aLayerSize,
147 : const nsSize& aBgPositioningArea,
148 : nscoord aUnscaledWidth, bool aHaveWidth,
149 : nscoord aUnscaledHeight, bool aHaveHeight,
150 : const nsSize& aIntrinsicRatio);
151 :
152 : nsIFrame* mForFrame;
153 : const nsStyleImage* mImage;
154 : nsStyleImageType mType;
155 : nsCOMPtr<imgIContainer> mImageContainer;
156 : nsRefPtr<nsStyleGradient> mGradientData;
157 : nsIFrame* mPaintServerFrame;
158 : nsLayoutUtils::SurfaceFromElementResult mImageElementSurface;
159 : bool mIsReady;
160 : nsSize mSize; // unscaled size of the image, in app units
161 : PRUint32 mFlags;
162 : };
163 :
164 : // To avoid storing this data on nsInlineFrame (bloat) and to avoid
165 : // recalculating this for each frame in a continuation (perf), hold
166 : // a cache of various coordinate information that we need in order
167 : // to paint inline backgrounds.
168 : struct InlineBackgroundData
169 : {
170 1404 : InlineBackgroundData()
171 1404 : : mFrame(nsnull), mBlockFrame(nsnull)
172 : {
173 1404 : }
174 :
175 1403 : ~InlineBackgroundData()
176 1403 : {
177 1403 : }
178 :
179 0 : void Reset()
180 : {
181 0 : mBoundingBox.SetRect(0,0,0,0);
182 0 : mContinuationPoint = mLineContinuationPoint = mUnbrokenWidth = 0;
183 0 : mFrame = mBlockFrame = nsnull;
184 0 : }
185 :
186 0 : nsRect GetContinuousRect(nsIFrame* aFrame)
187 : {
188 0 : SetFrame(aFrame);
189 :
190 : nscoord x;
191 0 : if (mBidiEnabled) {
192 0 : x = mLineContinuationPoint;
193 :
194 : // Scan continuations on the same line as aFrame and accumulate the widths
195 : // of frames that are to the left (if this is an LTR block) or right
196 : // (if it's RTL) of the current one.
197 0 : bool isRtlBlock = (mBlockFrame->GetStyleVisibility()->mDirection ==
198 0 : NS_STYLE_DIRECTION_RTL);
199 0 : nscoord curOffset = aFrame->GetOffsetTo(mBlockFrame).x;
200 :
201 : // No need to use our GetPrevContinuation/GetNextContinuation methods
202 : // here, since ib special siblings are certainly not on the same line.
203 :
204 0 : nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
205 : // If the continuation is fluid we know inlineFrame is not on the same line.
206 : // If it's not fluid, we need to test further to be sure.
207 0 : while (inlineFrame && !inlineFrame->GetNextInFlow() &&
208 0 : AreOnSameLine(aFrame, inlineFrame)) {
209 0 : nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
210 0 : if(isRtlBlock == (frameXOffset >= curOffset)) {
211 0 : x += inlineFrame->GetSize().width;
212 : }
213 0 : inlineFrame = inlineFrame->GetPrevContinuation();
214 : }
215 :
216 0 : inlineFrame = aFrame->GetNextContinuation();
217 0 : while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
218 0 : AreOnSameLine(aFrame, inlineFrame)) {
219 0 : nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
220 0 : if(isRtlBlock == (frameXOffset >= curOffset)) {
221 0 : x += inlineFrame->GetSize().width;
222 : }
223 0 : inlineFrame = inlineFrame->GetNextContinuation();
224 : }
225 0 : if (isRtlBlock) {
226 : // aFrame itself is also to the right of its left edge, so add its width.
227 0 : x += aFrame->GetSize().width;
228 : // x is now the distance from the left edge of aFrame to the right edge
229 : // of the unbroken content. Change it to indicate the distance from the
230 : // left edge of the unbroken content to the left edge of aFrame.
231 0 : x = mUnbrokenWidth - x;
232 : }
233 : } else {
234 0 : x = mContinuationPoint;
235 : }
236 :
237 : // Assume background-origin: border and return a rect with offsets
238 : // relative to (0,0). If we have a different background-origin,
239 : // then our rect should be deflated appropriately by our caller.
240 0 : return nsRect(-x, 0, mUnbrokenWidth, mFrame->GetSize().height);
241 : }
242 :
243 0 : nsRect GetBoundingRect(nsIFrame* aFrame)
244 : {
245 0 : SetFrame(aFrame);
246 :
247 : // Move the offsets relative to (0,0) which puts the bounding box into
248 : // our coordinate system rather than our parent's. We do this by
249 : // moving it the back distance from us to the bounding box.
250 : // This also assumes background-origin: border, so our caller will
251 : // need to deflate us if needed.
252 0 : nsRect boundingBox(mBoundingBox);
253 0 : nsPoint point = mFrame->GetPosition();
254 0 : boundingBox.MoveBy(-point.x, -point.y);
255 :
256 : return boundingBox;
257 : }
258 :
259 : protected:
260 : nsIFrame* mFrame;
261 : nsBlockFrame* mBlockFrame;
262 : nsRect mBoundingBox;
263 : nscoord mContinuationPoint;
264 : nscoord mUnbrokenWidth;
265 : nscoord mLineContinuationPoint;
266 : bool mBidiEnabled;
267 :
268 0 : void SetFrame(nsIFrame* aFrame)
269 : {
270 0 : NS_PRECONDITION(aFrame, "Need a frame");
271 :
272 0 : nsIFrame *prevContinuation = GetPrevContinuation(aFrame);
273 :
274 0 : if (!prevContinuation || mFrame != prevContinuation) {
275 : // Ok, we've got the wrong frame. We have to start from scratch.
276 0 : Reset();
277 0 : Init(aFrame);
278 0 : return;
279 : }
280 :
281 : // Get our last frame's size and add its width to our continuation
282 : // point before we cache the new frame.
283 0 : mContinuationPoint += mFrame->GetSize().width;
284 :
285 : // If this a new line, update mLineContinuationPoint.
286 0 : if (mBidiEnabled &&
287 0 : (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
288 0 : mLineContinuationPoint = mContinuationPoint;
289 : }
290 :
291 0 : mFrame = aFrame;
292 : }
293 :
294 0 : nsIFrame* GetPrevContinuation(nsIFrame* aFrame)
295 : {
296 0 : nsIFrame* prevCont = aFrame->GetPrevContinuation();
297 0 : if (!prevCont && (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
298 : nsIFrame* block = static_cast<nsIFrame*>
299 0 : (aFrame->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
300 0 : if (block) {
301 : // The {ib} properties are only stored on first continuations
302 0 : NS_ASSERTION(!block->GetPrevContinuation(),
303 : "Incorrect value for IBSplitSpecialPrevSibling");
304 : prevCont = static_cast<nsIFrame*>
305 0 : (block->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()));
306 0 : NS_ASSERTION(prevCont, "How did that happen?");
307 : }
308 : }
309 0 : return prevCont;
310 : }
311 :
312 0 : nsIFrame* GetNextContinuation(nsIFrame* aFrame)
313 : {
314 0 : nsIFrame* nextCont = aFrame->GetNextContinuation();
315 0 : if (!nextCont && (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
316 : // The {ib} properties are only stored on first continuations
317 0 : aFrame = aFrame->GetFirstContinuation();
318 : nsIFrame* block = static_cast<nsIFrame*>
319 0 : (aFrame->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
320 0 : if (block) {
321 : nextCont = static_cast<nsIFrame*>
322 0 : (block->Properties().Get(nsIFrame::IBSplitSpecialSibling()));
323 0 : NS_ASSERTION(nextCont, "How did that happen?");
324 : }
325 : }
326 0 : return nextCont;
327 : }
328 :
329 0 : void Init(nsIFrame* aFrame)
330 : {
331 : // Start with the previous flow frame as our continuation point
332 : // is the total of the widths of the previous frames.
333 0 : nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
334 :
335 0 : while (inlineFrame) {
336 0 : nsRect rect = inlineFrame->GetRect();
337 0 : mContinuationPoint += rect.width;
338 0 : mUnbrokenWidth += rect.width;
339 0 : mBoundingBox.UnionRect(mBoundingBox, rect);
340 0 : inlineFrame = GetPrevContinuation(inlineFrame);
341 : }
342 :
343 : // Next add this frame and subsequent frames to the bounding box and
344 : // unbroken width.
345 0 : inlineFrame = aFrame;
346 0 : while (inlineFrame) {
347 0 : nsRect rect = inlineFrame->GetRect();
348 0 : mUnbrokenWidth += rect.width;
349 0 : mBoundingBox.UnionRect(mBoundingBox, rect);
350 0 : inlineFrame = GetNextContinuation(inlineFrame);
351 : }
352 :
353 0 : mFrame = aFrame;
354 :
355 0 : mBidiEnabled = aFrame->PresContext()->BidiEnabled();
356 0 : if (mBidiEnabled) {
357 : // Find the containing block frame
358 0 : nsIFrame* frame = aFrame;
359 0 : do {
360 0 : frame = frame->GetParent();
361 0 : mBlockFrame = do_QueryFrame(frame);
362 : }
363 0 : while (frame && frame->IsFrameOfType(nsIFrame::eLineParticipant));
364 :
365 0 : NS_ASSERTION(mBlockFrame, "Cannot find containing block.");
366 :
367 0 : mLineContinuationPoint = mContinuationPoint;
368 : }
369 0 : }
370 :
371 0 : bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
372 : bool isValid1, isValid2;
373 0 : nsBlockInFlowLineIterator it1(mBlockFrame, aFrame1, &isValid1);
374 0 : nsBlockInFlowLineIterator it2(mBlockFrame, aFrame2, &isValid2);
375 : return isValid1 && isValid2 &&
376 : // Make sure aFrame1 and aFrame2 are in the same continuation of
377 : // mBlockFrame.
378 0 : it1.GetContainer() == it2.GetContainer() &&
379 : // And on the same line in it
380 0 : it1.GetLine() == it2.GetLine();
381 : }
382 : };
383 :
384 : /* Local functions */
385 : static void DrawBorderImage(nsPresContext* aPresContext,
386 : nsRenderingContext& aRenderingContext,
387 : nsIFrame* aForFrame,
388 : const nsRect& aBorderArea,
389 : const nsStyleBorder& aStyleBorder,
390 : const nsRect& aDirtyRect);
391 :
392 : static void DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
393 : nsIFrame* aForFrame,
394 : imgIContainer* aImage,
395 : const nsRect& aDirtyRect,
396 : const nsRect& aFill,
397 : const nsIntRect& aSrc,
398 : PRUint8 aHFill,
399 : PRUint8 aVFill,
400 : const nsSize& aUnitSize,
401 : const nsStyleBorder& aStyleBorder,
402 : PRUint8 aIndex);
403 :
404 : static nscolor MakeBevelColor(mozilla::css::Side whichSide, PRUint8 style,
405 : nscolor aBackgroundColor,
406 : nscolor aBorderColor);
407 :
408 : static InlineBackgroundData* gInlineBGData = nsnull;
409 :
410 : // Initialize any static variables used by nsCSSRendering.
411 1404 : void nsCSSRendering::Init()
412 : {
413 1404 : NS_ASSERTION(!gInlineBGData, "Init called twice");
414 1404 : gInlineBGData = new InlineBackgroundData();
415 1404 : }
416 :
417 : // Clean up any global variables used by nsCSSRendering.
418 1403 : void nsCSSRendering::Shutdown()
419 : {
420 1403 : delete gInlineBGData;
421 1403 : gInlineBGData = nsnull;
422 1403 : }
423 :
424 : /**
425 : * Make a bevel color
426 : */
427 : static nscolor
428 0 : MakeBevelColor(mozilla::css::Side whichSide, PRUint8 style,
429 : nscolor aBackgroundColor, nscolor aBorderColor)
430 : {
431 :
432 : nscolor colors[2];
433 : nscolor theColor;
434 :
435 : // Given a background color and a border color
436 : // calculate the color used for the shading
437 0 : NS_GetSpecial3DColors(colors, aBackgroundColor, aBorderColor);
438 :
439 0 : if ((style == NS_STYLE_BORDER_STYLE_OUTSET) ||
440 : (style == NS_STYLE_BORDER_STYLE_RIDGE)) {
441 : // Flip colors for these two border styles
442 0 : switch (whichSide) {
443 0 : case NS_SIDE_BOTTOM: whichSide = NS_SIDE_TOP; break;
444 0 : case NS_SIDE_RIGHT: whichSide = NS_SIDE_LEFT; break;
445 0 : case NS_SIDE_TOP: whichSide = NS_SIDE_BOTTOM; break;
446 0 : case NS_SIDE_LEFT: whichSide = NS_SIDE_RIGHT; break;
447 : }
448 : }
449 :
450 0 : switch (whichSide) {
451 : case NS_SIDE_BOTTOM:
452 0 : theColor = colors[1];
453 0 : break;
454 : case NS_SIDE_RIGHT:
455 0 : theColor = colors[1];
456 0 : break;
457 : case NS_SIDE_TOP:
458 0 : theColor = colors[0];
459 0 : break;
460 : case NS_SIDE_LEFT:
461 : default:
462 0 : theColor = colors[0];
463 0 : break;
464 : }
465 0 : return theColor;
466 : }
467 :
468 : //----------------------------------------------------------------------
469 : // Thebes Border Rendering Code Start
470 :
471 : /*
472 : * Compute the float-pixel radii that should be used for drawing
473 : * this border/outline, given the various input bits.
474 : */
475 : /* static */ void
476 0 : nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
477 : nscoord aAppUnitsPerPixel,
478 : gfxCornerSizes *oBorderRadii)
479 : {
480 : gfxFloat radii[8];
481 0 : NS_FOR_CSS_HALF_CORNERS(corner)
482 0 : radii[corner] = gfxFloat(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
483 :
484 0 : (*oBorderRadii)[C_TL] = gfxSize(radii[NS_CORNER_TOP_LEFT_X],
485 0 : radii[NS_CORNER_TOP_LEFT_Y]);
486 0 : (*oBorderRadii)[C_TR] = gfxSize(radii[NS_CORNER_TOP_RIGHT_X],
487 0 : radii[NS_CORNER_TOP_RIGHT_Y]);
488 0 : (*oBorderRadii)[C_BR] = gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],
489 0 : radii[NS_CORNER_BOTTOM_RIGHT_Y]);
490 0 : (*oBorderRadii)[C_BL] = gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],
491 0 : radii[NS_CORNER_BOTTOM_LEFT_Y]);
492 0 : }
493 :
494 : void
495 0 : nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
496 : nsRenderingContext& aRenderingContext,
497 : nsIFrame* aForFrame,
498 : const nsRect& aDirtyRect,
499 : const nsRect& aBorderArea,
500 : nsStyleContext* aStyleContext,
501 : PRIntn aSkipSides)
502 : {
503 0 : nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
504 0 : const nsStyleBorder *styleBorder = aStyleContext->GetStyleBorder();
505 : // Don't check RelevantLinkVisited here, since we want to take the
506 : // same amount of time whether or not it's true.
507 0 : if (!styleIfVisited) {
508 : PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
509 : aDirtyRect, aBorderArea, *styleBorder,
510 0 : aStyleContext, aSkipSides);
511 0 : return;
512 : }
513 :
514 0 : nsStyleBorder newStyleBorder(*styleBorder);
515 : // We're making an ephemeral stack copy here, so just copy this debug-only
516 : // member to prevent assertions.
517 : #ifdef DEBUG
518 0 : newStyleBorder.mImageTracked = styleBorder->mImageTracked;
519 : #endif
520 :
521 0 : NS_FOR_CSS_SIDES(side) {
522 : newStyleBorder.SetBorderColor(side,
523 : aStyleContext->GetVisitedDependentColor(
524 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]));
525 : }
526 : PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
527 : aDirtyRect, aBorderArea, newStyleBorder,
528 0 : aStyleContext, aSkipSides);
529 :
530 : #ifdef DEBUG
531 0 : newStyleBorder.mImageTracked = false;
532 : #endif
533 : }
534 :
535 : void
536 0 : nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
537 : nsRenderingContext& aRenderingContext,
538 : nsIFrame* aForFrame,
539 : const nsRect& aDirtyRect,
540 : const nsRect& aBorderArea,
541 : const nsStyleBorder& aStyleBorder,
542 : nsStyleContext* aStyleContext,
543 : PRIntn aSkipSides)
544 : {
545 0 : nsMargin border;
546 : nscoord twipsRadii[8];
547 0 : nsCompatibility compatMode = aPresContext->CompatibilityMode();
548 :
549 0 : SN("++ PaintBorder");
550 :
551 : // Check to see if we have an appearance defined. If so, we let the theme
552 : // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
553 : // may be different! Always use |aStyleContext|!
554 0 : const nsStyleDisplay* displayData = aStyleContext->GetStyleDisplay();
555 0 : if (displayData->mAppearance) {
556 0 : nsITheme *theme = aPresContext->GetTheme();
557 0 : if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, displayData->mAppearance))
558 0 : return; // Let the theme handle it.
559 : }
560 :
561 0 : if (aStyleBorder.IsBorderImageLoaded()) {
562 : DrawBorderImage(aPresContext, aRenderingContext, aForFrame,
563 0 : aBorderArea, aStyleBorder, aDirtyRect);
564 0 : return;
565 : }
566 :
567 : // Get our style context's color struct.
568 0 : const nsStyleColor* ourColor = aStyleContext->GetStyleColor();
569 :
570 : // in NavQuirks mode we want to use the parent's context as a starting point
571 : // for determining the background color
572 : nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
573 0 : (aForFrame, compatMode == eCompatibility_NavQuirks ? true : false);
574 0 : nsStyleContext* bgContext = bgFrame->GetStyleContext();
575 : nscolor bgColor =
576 0 : bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
577 :
578 0 : border = aStyleBorder.GetComputedBorder();
579 0 : if ((0 == border.left) && (0 == border.right) &&
580 : (0 == border.top) && (0 == border.bottom)) {
581 : // Empty border area
582 0 : return;
583 : }
584 :
585 0 : nsSize frameSize = aForFrame->GetSize();
586 0 : if (&aStyleBorder == aForFrame->GetStyleBorder() &&
587 0 : frameSize == aBorderArea.Size()) {
588 0 : aForFrame->GetBorderRadii(twipsRadii);
589 : } else {
590 : nsIFrame::ComputeBorderRadii(aStyleBorder.mBorderRadius, frameSize,
591 0 : aBorderArea.Size(), aSkipSides, twipsRadii);
592 : }
593 :
594 : // Turn off rendering for all of the zero sized sides
595 0 : if (aSkipSides & SIDE_BIT_TOP) border.top = 0;
596 0 : if (aSkipSides & SIDE_BIT_RIGHT) border.right = 0;
597 0 : if (aSkipSides & SIDE_BIT_BOTTOM) border.bottom = 0;
598 0 : if (aSkipSides & SIDE_BIT_LEFT) border.left = 0;
599 :
600 : // get the inside and outside parts of the border
601 0 : nsRect outerRect(aBorderArea);
602 :
603 0 : SF(" outerRect: %d %d %d %d\n", outerRect.x, outerRect.y, outerRect.width, outerRect.height);
604 :
605 : // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
606 :
607 : // Get our conversion values
608 0 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
609 :
610 : // convert outer and inner rects
611 0 : gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
612 :
613 : // convert the border widths
614 : gfxFloat borderWidths[4] = { gfxFloat(border.top / twipsPerPixel),
615 : gfxFloat(border.right / twipsPerPixel),
616 : gfxFloat(border.bottom / twipsPerPixel),
617 0 : gfxFloat(border.left / twipsPerPixel) };
618 :
619 : // convert the radii
620 0 : gfxCornerSizes borderRadii;
621 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
622 :
623 : PRUint8 borderStyles[4];
624 : nscolor borderColors[4];
625 : nsBorderColors *compositeColors[4];
626 :
627 : // pull out styles, colors, composite colors
628 0 : NS_FOR_CSS_SIDES (i) {
629 : bool foreground;
630 0 : borderStyles[i] = aStyleBorder.GetBorderStyle(i);
631 0 : aStyleBorder.GetBorderColor(i, borderColors[i], foreground);
632 0 : aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
633 :
634 0 : if (foreground)
635 0 : borderColors[i] = ourColor->mColor;
636 : }
637 :
638 0 : SF(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
639 :
640 : // start drawing
641 0 : gfxContext *ctx = aRenderingContext.ThebesContext();
642 :
643 0 : ctx->Save();
644 :
645 : #if 0
646 : // this will draw a transparent red backround underneath the oRect area
647 : ctx->Save();
648 : ctx->Rectangle(oRect);
649 : ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
650 : ctx->Fill();
651 : ctx->Restore();
652 : #endif
653 :
654 : //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
655 :
656 : nsCSSBorderRenderer br(twipsPerPixel,
657 : ctx,
658 : oRect,
659 : borderStyles,
660 : borderWidths,
661 : borderRadii,
662 : borderColors,
663 : compositeColors,
664 : aSkipSides,
665 0 : bgColor);
666 0 : br.DrawBorders();
667 :
668 0 : ctx->Restore();
669 :
670 0 : SN();
671 : }
672 :
673 : static nsRect
674 0 : GetOutlineInnerRect(nsIFrame* aFrame)
675 : {
676 : nsRect* savedOutlineInnerRect = static_cast<nsRect*>
677 0 : (aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty()));
678 0 : if (savedOutlineInnerRect)
679 0 : return *savedOutlineInnerRect;
680 : // FIXME (bug 599652): We probably want something narrower than either
681 : // overflow rect here, but for now use the visual overflow in order to
682 : // be consistent with ComputeOutlineAndEffectsRect in nsFrame.cpp.
683 0 : return aFrame->GetVisualOverflowRect();
684 : }
685 :
686 : void
687 0 : nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
688 : nsRenderingContext& aRenderingContext,
689 : nsIFrame* aForFrame,
690 : const nsRect& aDirtyRect,
691 : const nsRect& aBorderArea,
692 : nsStyleContext* aStyleContext)
693 : {
694 : nscoord twipsRadii[8];
695 :
696 : // Get our style context's color struct.
697 0 : const nsStyleOutline* ourOutline = aStyleContext->GetStyleOutline();
698 :
699 : nscoord width;
700 0 : ourOutline->GetOutlineWidth(width);
701 :
702 0 : if (width == 0) {
703 : // Empty outline
704 0 : return;
705 : }
706 :
707 : nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
708 0 : (aForFrame, false);
709 0 : nsStyleContext* bgContext = bgFrame->GetStyleContext();
710 : nscolor bgColor =
711 0 : bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
712 :
713 : // When the outline property is set on :-moz-anonymous-block or
714 : // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
715 : // outline from the inline that was broken because it contained a
716 : // block. In that case, we don't want a really wide outline if the
717 : // block inside the inline is narrow, so union the actual contents of
718 : // the anonymous blocks.
719 0 : nsIFrame *frameForArea = aForFrame;
720 0 : do {
721 0 : nsIAtom *pseudoType = frameForArea->GetStyleContext()->GetPseudo();
722 0 : if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
723 : pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
724 0 : break;
725 : // If we're done, we really want it and all its later siblings.
726 0 : frameForArea = frameForArea->GetFirstPrincipalChild();
727 0 : NS_ASSERTION(frameForArea, "anonymous block with no children?");
728 : } while (frameForArea);
729 0 : nsRect innerRect; // relative to aBorderArea.TopLeft()
730 0 : if (frameForArea == aForFrame) {
731 0 : innerRect = GetOutlineInnerRect(aForFrame);
732 : } else {
733 0 : for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
734 : // The outline has already been included in aForFrame's overflow
735 : // area, but not in those of its descendants, so we have to
736 : // include it. Otherwise we'll end up drawing the outline inside
737 : // the border.
738 0 : nsRect r(GetOutlineInnerRect(frameForArea) +
739 0 : frameForArea->GetOffsetTo(aForFrame));
740 0 : innerRect.UnionRect(innerRect, r);
741 : }
742 : }
743 :
744 0 : innerRect += aBorderArea.TopLeft();
745 0 : nscoord offset = ourOutline->mOutlineOffset;
746 0 : innerRect.Inflate(offset, offset);
747 : // If the dirty rect is completely inside the border area (e.g., only the
748 : // content is being painted), then we can skip out now
749 : // XXX this isn't exactly true for rounded borders, where the inside curves may
750 : // encroach into the content area. A safer calculation would be to
751 : // shorten insideRect by the radius one each side before performing this test.
752 0 : if (innerRect.Contains(aDirtyRect))
753 : return;
754 :
755 0 : nsRect outerRect = innerRect;
756 0 : outerRect.Inflate(width, width);
757 :
758 : // get the radius for our outline
759 0 : nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(),
760 0 : outerRect.Size(), 0, twipsRadii);
761 :
762 : // Get our conversion values
763 0 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
764 :
765 : // get the outer rectangles
766 0 : gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
767 :
768 : // convert the radii
769 0 : nsMargin outlineMargin(width, width, width, width);
770 0 : gfxCornerSizes outlineRadii;
771 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii);
772 :
773 0 : PRUint8 outlineStyle = ourOutline->GetOutlineStyle();
774 : PRUint8 outlineStyles[4] = { outlineStyle,
775 : outlineStyle,
776 : outlineStyle,
777 0 : outlineStyle };
778 :
779 : // This handles treating the initial color as 'currentColor'; if we
780 : // ever want 'invert' back we'll need to do a bit of work here too.
781 : nscolor outlineColor =
782 0 : aStyleContext->GetVisitedDependentColor(eCSSProperty_outline_color);
783 : nscolor outlineColors[4] = { outlineColor,
784 : outlineColor,
785 : outlineColor,
786 0 : outlineColor };
787 :
788 : // convert the border widths
789 : gfxFloat outlineWidths[4] = { gfxFloat(width / twipsPerPixel),
790 : gfxFloat(width / twipsPerPixel),
791 : gfxFloat(width / twipsPerPixel),
792 0 : gfxFloat(width / twipsPerPixel) };
793 :
794 : // start drawing
795 0 : gfxContext *ctx = aRenderingContext.ThebesContext();
796 :
797 0 : ctx->Save();
798 :
799 : nsCSSBorderRenderer br(twipsPerPixel,
800 : ctx,
801 : oRect,
802 : outlineStyles,
803 : outlineWidths,
804 : outlineRadii,
805 : outlineColors,
806 : nsnull, 0,
807 0 : bgColor);
808 0 : br.DrawBorders();
809 :
810 0 : ctx->Restore();
811 :
812 0 : SN();
813 : }
814 :
815 : void
816 0 : nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
817 : nsRenderingContext& aRenderingContext,
818 : const nsRect& aFocusRect,
819 : nscolor aColor)
820 : {
821 0 : nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
822 0 : nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
823 :
824 0 : gfxRect focusRect(nsLayoutUtils::RectToGfxRect(aFocusRect, oneDevPixel));
825 :
826 0 : gfxCornerSizes focusRadii;
827 : {
828 0 : nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
829 0 : ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
830 : }
831 : gfxFloat focusWidths[4] = { gfxFloat(oneCSSPixel / oneDevPixel),
832 : gfxFloat(oneCSSPixel / oneDevPixel),
833 : gfxFloat(oneCSSPixel / oneDevPixel),
834 0 : gfxFloat(oneCSSPixel / oneDevPixel) };
835 :
836 : PRUint8 focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
837 : NS_STYLE_BORDER_STYLE_DOTTED,
838 : NS_STYLE_BORDER_STYLE_DOTTED,
839 0 : NS_STYLE_BORDER_STYLE_DOTTED };
840 0 : nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
841 :
842 0 : gfxContext *ctx = aRenderingContext.ThebesContext();
843 :
844 0 : ctx->Save();
845 :
846 : // Because this renders a dotted border, the background color
847 : // should not be used. Therefore, we provide a value that will
848 : // be blatantly wrong if it ever does get used. (If this becomes
849 : // something that CSS can style, this function will then have access
850 : // to a style context and can use the same logic that PaintBorder
851 : // and PaintOutline do.)
852 : nsCSSBorderRenderer br(oneDevPixel,
853 : ctx,
854 : focusRect,
855 : focusStyles,
856 : focusWidths,
857 : focusRadii,
858 : focusColors,
859 : nsnull, 0,
860 0 : NS_RGB(255, 0, 0));
861 0 : br.DrawBorders();
862 :
863 0 : ctx->Restore();
864 :
865 0 : SN();
866 0 : }
867 :
868 : // Thebes Border Rendering Code End
869 : //----------------------------------------------------------------------
870 :
871 :
872 : //----------------------------------------------------------------------
873 :
874 : /**
875 : * Computes the placement of a background image.
876 : *
877 : * @param aOriginBounds is the box to which the tiling position should be
878 : * relative
879 : * This should correspond to 'background-origin' for the frame,
880 : * except when painting on the canvas, in which case the origin bounds
881 : * should be the bounds of the root element's frame.
882 : * @param aTopLeft the top-left corner where an image tile should be drawn
883 : * @param aAnchorPoint a point which should be pixel-aligned by
884 : * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS
885 : * specifies a percentage (including 'right' or 'bottom'), in which case
886 : * it's that percentage within of aOriginBounds. So 'right' would set
887 : * aAnchorPoint.x to aOriginBounds.XMost().
888 : *
889 : * Points are returned relative to aOriginBounds.
890 : */
891 : static void
892 0 : ComputeBackgroundAnchorPoint(const nsStyleBackground::Layer& aLayer,
893 : const nsSize& aOriginBounds,
894 : const nsSize& aImageSize,
895 : nsPoint* aTopLeft,
896 : nsPoint* aAnchorPoint)
897 : {
898 0 : double percentX = aLayer.mPosition.mXPosition.mPercent;
899 0 : nscoord lengthX = aLayer.mPosition.mXPosition.mLength;
900 0 : aAnchorPoint->x = lengthX + NSToCoordRound(percentX*aOriginBounds.width);
901 : aTopLeft->x = lengthX +
902 0 : NSToCoordRound(percentX*(aOriginBounds.width - aImageSize.width));
903 :
904 0 : double percentY = aLayer.mPosition.mYPosition.mPercent;
905 0 : nscoord lengthY = aLayer.mPosition.mYPosition.mLength;
906 0 : aAnchorPoint->y = lengthY + NSToCoordRound(percentY*aOriginBounds.height);
907 : aTopLeft->y = lengthY +
908 0 : NSToCoordRound(percentY*(aOriginBounds.height - aImageSize.height));
909 0 : }
910 :
911 : nsIFrame*
912 0 : nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
913 : bool aStartAtParent /*= false*/)
914 : {
915 0 : NS_ASSERTION(aFrame, "Cannot find NonTransparentBackgroundFrame in a null frame");
916 :
917 0 : nsIFrame* frame = nsnull;
918 0 : if (aStartAtParent) {
919 : frame = nsLayoutUtils::GetParentOrPlaceholderFor(
920 0 : aFrame->PresContext()->FrameManager(), aFrame);
921 : }
922 0 : if (!frame) {
923 0 : frame = aFrame;
924 : }
925 :
926 0 : while (frame) {
927 : // No need to call GetVisitedDependentColor because it always uses
928 : // this alpha component anyway.
929 0 : if (NS_GET_A(frame->GetStyleBackground()->mBackgroundColor) > 0)
930 0 : break;
931 :
932 0 : if (frame->IsThemed())
933 0 : break;
934 :
935 : nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(
936 0 : frame->PresContext()->FrameManager(), frame);
937 0 : if (!parent)
938 0 : break;
939 :
940 0 : frame = parent;
941 : }
942 0 : return frame;
943 : }
944 :
945 : // Returns true if aFrame is a canvas frame.
946 : // We need to treat the viewport as canvas because, even though
947 : // it does not actually paint a background, we need to get the right
948 : // background style so we correctly detect transparent documents.
949 : bool
950 0 : nsCSSRendering::IsCanvasFrame(nsIFrame* aFrame)
951 : {
952 0 : nsIAtom* frameType = aFrame->GetType();
953 : return frameType == nsGkAtoms::canvasFrame ||
954 : frameType == nsGkAtoms::rootFrame ||
955 : frameType == nsGkAtoms::pageContentFrame ||
956 0 : frameType == nsGkAtoms::viewportFrame;
957 : }
958 :
959 : nsIFrame*
960 0 : nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame)
961 : {
962 0 : const nsStyleBackground* result = aForFrame->GetStyleBackground();
963 :
964 : // Check if we need to do propagation from BODY rather than HTML.
965 0 : if (!result->IsTransparent()) {
966 0 : return aForFrame;
967 : }
968 :
969 0 : nsIContent* content = aForFrame->GetContent();
970 : // The root element content can't be null. We wouldn't know what
971 : // frame to create for aFrame.
972 : // Use |OwnerDoc| so it works during destruction.
973 0 : if (!content) {
974 0 : return aForFrame;
975 : }
976 :
977 0 : nsIDocument* document = content->OwnerDoc();
978 :
979 0 : dom::Element* bodyContent = document->GetBodyElement();
980 : // We need to null check the body node (bug 118829) since
981 : // there are cases, thanks to the fix for bug 5569, where we
982 : // will reflow a document with no body. In particular, if a
983 : // SCRIPT element in the head blocks the parser and then has a
984 : // SCRIPT that does "document.location.href = 'foo'", then
985 : // nsParser::Terminate will call |DidBuildModel| methods
986 : // through to the content sink, which will call |StartLayout|
987 : // and thus |InitialReflow| on the pres shell. See bug 119351
988 : // for the ugly details.
989 0 : if (!bodyContent) {
990 0 : return aForFrame;
991 : }
992 :
993 0 : nsIFrame *bodyFrame = bodyContent->GetPrimaryFrame();
994 0 : if (!bodyFrame) {
995 0 : return aForFrame;
996 : }
997 :
998 0 : return nsLayoutUtils::GetStyleFrame(bodyFrame);
999 : }
1000 :
1001 : /**
1002 : * |FindBackground| finds the correct style data to use to paint the
1003 : * background. It is responsible for handling the following two
1004 : * statements in section 14.2 of CSS2:
1005 : *
1006 : * The background of the box generated by the root element covers the
1007 : * entire canvas.
1008 : *
1009 : * For HTML documents, however, we recommend that authors specify the
1010 : * background for the BODY element rather than the HTML element. User
1011 : * agents should observe the following precedence rules to fill in the
1012 : * background: if the value of the 'background' property for the HTML
1013 : * element is different from 'transparent' then use it, else use the
1014 : * value of the 'background' property for the BODY element. If the
1015 : * resulting value is 'transparent', the rendering is undefined.
1016 : *
1017 : * Thus, in our implementation, it is responsible for ensuring that:
1018 : * + we paint the correct background on the |nsCanvasFrame|,
1019 : * |nsRootBoxFrame|, or |nsPageFrame|,
1020 : * + we don't paint the background on the root element, and
1021 : * + we don't paint the background on the BODY element in *some* cases,
1022 : * and for SGML-based HTML documents only.
1023 : *
1024 : * |FindBackground| returns true if a background should be painted, and
1025 : * the resulting style context to use for the background information
1026 : * will be filled in to |aBackground|.
1027 : */
1028 : nsStyleContext*
1029 0 : nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
1030 : {
1031 0 : return FindBackgroundStyleFrame(aForFrame)->GetStyleContext();
1032 : }
1033 :
1034 : inline bool
1035 0 : FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
1036 : nsStyleContext** aBackgroundSC)
1037 : {
1038 0 : if (aForFrame == aRootElementFrame) {
1039 : // We must have propagated our background to the viewport or canvas. Abort.
1040 0 : return false;
1041 : }
1042 :
1043 0 : *aBackgroundSC = aForFrame->GetStyleContext();
1044 :
1045 : // Return true unless the frame is for a BODY element whose background
1046 : // was propagated to the viewport.
1047 :
1048 0 : nsIContent* content = aForFrame->GetContent();
1049 0 : if (!content || content->Tag() != nsGkAtoms::body)
1050 0 : return true; // not frame for a "body" element
1051 : // It could be a non-HTML "body" element but that's OK, we'd fail the
1052 : // bodyContent check below
1053 :
1054 0 : if (aForFrame->GetStyleContext()->GetPseudo())
1055 0 : return true; // A pseudo-element frame.
1056 :
1057 : // We should only look at the <html> background if we're in an HTML document
1058 0 : nsIDocument* document = content->OwnerDoc();
1059 :
1060 0 : dom::Element* bodyContent = document->GetBodyElement();
1061 0 : if (bodyContent != content)
1062 0 : return true; // this wasn't the background that was propagated
1063 :
1064 : // This can be called even when there's no root element yet, during frame
1065 : // construction, via nsLayoutUtils::FrameHasTransparency and
1066 : // nsContainerFrame::SyncFrameViewProperties.
1067 0 : if (!aRootElementFrame)
1068 0 : return true;
1069 :
1070 0 : const nsStyleBackground* htmlBG = aRootElementFrame->GetStyleBackground();
1071 0 : return !htmlBG->IsTransparent();
1072 : }
1073 :
1074 : bool
1075 0 : nsCSSRendering::FindBackground(nsPresContext* aPresContext,
1076 : nsIFrame* aForFrame,
1077 : nsStyleContext** aBackgroundSC)
1078 : {
1079 : nsIFrame* rootElementFrame =
1080 0 : aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1081 0 : if (IsCanvasFrame(aForFrame)) {
1082 0 : *aBackgroundSC = FindCanvasBackground(aForFrame, rootElementFrame);
1083 0 : return true;
1084 : } else {
1085 0 : return FindElementBackground(aForFrame, rootElementFrame, aBackgroundSC);
1086 : }
1087 : }
1088 :
1089 : void
1090 0 : nsCSSRendering::DidPaint()
1091 : {
1092 0 : gInlineBGData->Reset();
1093 0 : }
1094 :
1095 : void
1096 0 : nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
1097 : nsRenderingContext& aRenderingContext,
1098 : nsIFrame* aForFrame,
1099 : const nsRect& aFrameArea,
1100 : const nsRect& aDirtyRect)
1101 : {
1102 0 : const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
1103 0 : nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
1104 0 : if (!shadows)
1105 0 : return;
1106 0 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1107 :
1108 : bool hasBorderRadius;
1109 : bool nativeTheme; // mutually exclusive with hasBorderRadius
1110 0 : gfxCornerSizes borderRadii;
1111 :
1112 : // Get any border radius, since box-shadow must also have rounded corners if the frame does
1113 0 : const nsStyleDisplay* styleDisplay = aForFrame->GetStyleDisplay();
1114 : nsITheme::Transparency transparency;
1115 0 : if (aForFrame->IsThemed(styleDisplay, &transparency)) {
1116 : // We don't respect border-radius for native-themed widgets
1117 0 : hasBorderRadius = false;
1118 : // For opaque (rectangular) theme widgets we can take the generic
1119 : // border-box path with border-radius disabled.
1120 0 : nativeTheme = transparency != nsITheme::eOpaque;
1121 : } else {
1122 0 : nativeTheme = false;
1123 : nscoord twipsRadii[8];
1124 0 : NS_ASSERTION(aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
1125 0 : hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
1126 0 : if (hasBorderRadius) {
1127 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
1128 : }
1129 : }
1130 :
1131 : nsRect frameRect =
1132 0 : nativeTheme ? aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() : aFrameArea;
1133 0 : gfxRect frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect, twipsPerPixel));
1134 0 : frameGfxRect.Round();
1135 :
1136 : // We don't show anything that intersects with the frame we're blurring on. So tell the
1137 : // blurrer not to do unnecessary work there.
1138 0 : gfxRect skipGfxRect = frameGfxRect;
1139 0 : bool useSkipGfxRect = true;
1140 0 : if (nativeTheme) {
1141 : // Optimize non-leaf native-themed frames by skipping computing pixels
1142 : // in the padding-box. We assume the padding-box is going to be painted
1143 : // opaquely for non-leaf frames.
1144 : // XXX this may not be a safe assumption; we should make this go away
1145 : // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
1146 0 : useSkipGfxRect = !aForFrame->IsLeaf();
1147 : nsRect paddingRect =
1148 0 : aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
1149 0 : skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
1150 0 : } else if (hasBorderRadius) {
1151 : skipGfxRect.Deflate(gfxMargin(
1152 0 : 0, NS_MAX(borderRadii[C_TL].height, borderRadii[C_TR].height),
1153 0 : 0, NS_MAX(borderRadii[C_BL].height, borderRadii[C_BR].height)));
1154 : }
1155 :
1156 0 : for (PRUint32 i = shadows->Length(); i > 0; --i) {
1157 0 : nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
1158 0 : if (shadowItem->mInset)
1159 0 : continue;
1160 :
1161 0 : nsRect shadowRect = frameRect;
1162 0 : shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
1163 : nscoord pixelSpreadRadius;
1164 0 : if (nativeTheme) {
1165 0 : pixelSpreadRadius = shadowItem->mSpread;
1166 : } else {
1167 0 : shadowRect.Inflate(shadowItem->mSpread, shadowItem->mSpread);
1168 0 : pixelSpreadRadius = 0;
1169 : }
1170 :
1171 : // shadowRect won't include the blur, so make an extra rect here that includes the blur
1172 : // for use in the even-odd rule below.
1173 0 : nsRect shadowRectPlusBlur = shadowRect;
1174 0 : nscoord blurRadius = shadowItem->mRadius;
1175 : shadowRectPlusBlur.Inflate(
1176 0 : nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel));
1177 :
1178 : gfxRect shadowGfxRect =
1179 0 : nsLayoutUtils::RectToGfxRect(shadowRect, twipsPerPixel);
1180 : gfxRect shadowGfxRectPlusBlur =
1181 0 : nsLayoutUtils::RectToGfxRect(shadowRectPlusBlur, twipsPerPixel);
1182 0 : shadowGfxRect.Round();
1183 0 : shadowGfxRectPlusBlur.RoundOut();
1184 :
1185 0 : gfxContext* renderContext = aRenderingContext.ThebesContext();
1186 0 : nsRefPtr<gfxContext> shadowContext;
1187 0 : nsContextBoxBlur blurringArea;
1188 :
1189 : // When getting the widget shape from the native theme, we're going
1190 : // to draw the widget into the shadow surface to create a mask.
1191 : // We need to ensure that there actually *is* a shadow surface
1192 : // and that we're not going to draw directly into renderContext.
1193 : shadowContext =
1194 : blurringArea.Init(shadowRect, pixelSpreadRadius,
1195 : blurRadius, twipsPerPixel, renderContext, aDirtyRect,
1196 : useSkipGfxRect ? &skipGfxRect : nsnull,
1197 0 : nativeTheme ? nsContextBoxBlur::FORCE_MASK : 0);
1198 0 : if (!shadowContext)
1199 0 : continue;
1200 :
1201 : // Set the shadow color; if not specified, use the foreground color
1202 : nscolor shadowColor;
1203 0 : if (shadowItem->mHasColor)
1204 0 : shadowColor = shadowItem->mColor;
1205 : else
1206 0 : shadowColor = aForFrame->GetStyleColor()->mColor;
1207 :
1208 0 : renderContext->Save();
1209 0 : renderContext->SetColor(gfxRGBA(shadowColor));
1210 :
1211 : // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1212 : // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1213 : // surface? If we have no blur, we're painting this fill on the actual content surface
1214 : // (renderContext == shadowContext) which is why we set up the color and clip
1215 : // before doing this.
1216 0 : if (nativeTheme) {
1217 : // We don't clip the border-box from the shadow, nor any other box.
1218 : // We assume that the native theme is going to paint over the shadow.
1219 :
1220 : // Draw the widget shape
1221 0 : gfxContextMatrixAutoSaveRestore save(shadowContext);
1222 0 : nsRefPtr<nsRenderingContext> wrapperCtx = new nsRenderingContext();
1223 0 : wrapperCtx->Init(aPresContext->DeviceContext(), shadowContext);
1224 : wrapperCtx->Translate(nsPoint(shadowItem->mXOffset,
1225 0 : shadowItem->mYOffset));
1226 :
1227 0 : nsRect nativeRect;
1228 0 : nativeRect.IntersectRect(frameRect, aDirtyRect);
1229 0 : aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx, aForFrame,
1230 0 : styleDisplay->mAppearance, aFrameArea, nativeRect);
1231 : } else {
1232 : // Clip out the area of the actual frame so the shadow is not shown within
1233 : // the frame
1234 0 : renderContext->NewPath();
1235 0 : renderContext->Rectangle(shadowGfxRectPlusBlur);
1236 0 : if (hasBorderRadius) {
1237 0 : renderContext->RoundedRectangle(frameGfxRect, borderRadii);
1238 : } else {
1239 0 : renderContext->Rectangle(frameGfxRect);
1240 : }
1241 :
1242 0 : renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
1243 0 : renderContext->Clip();
1244 :
1245 0 : shadowContext->NewPath();
1246 0 : if (hasBorderRadius) {
1247 0 : gfxCornerSizes clipRectRadii;
1248 0 : gfxFloat spreadDistance = -shadowItem->mSpread / twipsPerPixel;
1249 0 : gfxFloat borderSizes[4] = { 0, 0, 0, 0 };
1250 :
1251 : // We only give the spread radius to corners with a radius on them, otherwise we'll
1252 : // give a rounded shadow corner to a frame corner with 0 border radius, should
1253 : // the author use non-uniform border radii sizes (border-top-left-radius etc)
1254 : // (bug 514670)
1255 0 : if (borderRadii[C_TL].width > 0 || borderRadii[C_BL].width > 0) {
1256 0 : borderSizes[NS_SIDE_LEFT] = spreadDistance;
1257 : }
1258 :
1259 0 : if (borderRadii[C_TL].height > 0 || borderRadii[C_TR].height > 0) {
1260 0 : borderSizes[NS_SIDE_TOP] = spreadDistance;
1261 : }
1262 :
1263 0 : if (borderRadii[C_TR].width > 0 || borderRadii[C_BR].width > 0) {
1264 0 : borderSizes[NS_SIDE_RIGHT] = spreadDistance;
1265 : }
1266 :
1267 0 : if (borderRadii[C_BL].height > 0 || borderRadii[C_BR].height > 0) {
1268 0 : borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
1269 : }
1270 :
1271 : nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
1272 0 : &clipRectRadii);
1273 0 : shadowContext->RoundedRectangle(shadowGfxRect, clipRectRadii);
1274 : } else {
1275 0 : shadowContext->Rectangle(shadowGfxRect);
1276 : }
1277 0 : shadowContext->Fill();
1278 : }
1279 :
1280 0 : blurringArea.DoPaint();
1281 0 : renderContext->Restore();
1282 : }
1283 : }
1284 :
1285 : void
1286 0 : nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
1287 : nsRenderingContext& aRenderingContext,
1288 : nsIFrame* aForFrame,
1289 : const nsRect& aFrameArea,
1290 : const nsRect& aDirtyRect)
1291 : {
1292 0 : const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
1293 0 : nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
1294 0 : if (!shadows)
1295 0 : return;
1296 0 : if (aForFrame->IsThemed() && aForFrame->GetContent() &&
1297 0 : !nsContentUtils::IsChromeDoc(aForFrame->GetContent()->GetCurrentDoc())) {
1298 : // There's no way of getting hold of a shape corresponding to a
1299 : // "padding-box" for native-themed widgets, so just don't draw
1300 : // inner box-shadows for them. But we allow chrome to paint inner
1301 : // box shadows since chrome can be aware of the platform theme.
1302 0 : return;
1303 : }
1304 :
1305 : // Get any border radius, since box-shadow must also have rounded corners
1306 : // if the frame does.
1307 : nscoord twipsRadii[8];
1308 0 : NS_ASSERTION(aForFrame->GetType() == nsGkAtoms::fieldSetFrame ||
1309 : aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
1310 0 : bool hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
1311 0 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1312 :
1313 0 : nsRect paddingRect = aFrameArea;
1314 0 : nsMargin border = aForFrame->GetUsedBorder();
1315 0 : aForFrame->ApplySkipSides(border);
1316 0 : paddingRect.Deflate(border);
1317 :
1318 0 : gfxCornerSizes innerRadii;
1319 0 : if (hasBorderRadius) {
1320 0 : gfxCornerSizes borderRadii;
1321 :
1322 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
1323 : gfxFloat borderSizes[4] = {
1324 : gfxFloat(border.top / twipsPerPixel),
1325 : gfxFloat(border.right / twipsPerPixel),
1326 : gfxFloat(border.bottom / twipsPerPixel),
1327 : gfxFloat(border.left / twipsPerPixel)
1328 0 : };
1329 : nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
1330 0 : &innerRadii);
1331 : }
1332 :
1333 0 : for (PRUint32 i = shadows->Length(); i > 0; --i) {
1334 0 : nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
1335 0 : if (!shadowItem->mInset)
1336 0 : continue;
1337 :
1338 : /*
1339 : * shadowRect: the frame's padding rect
1340 : * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect
1341 : * so that blurs still happen properly near the edges
1342 : * shadowClipRect: the area on the temporary surface within shadowPaintRect
1343 : * that we will NOT paint in
1344 : */
1345 0 : nscoord blurRadius = shadowItem->mRadius;
1346 : nsMargin blurMargin =
1347 0 : nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel);
1348 0 : nsRect shadowPaintRect = paddingRect;
1349 0 : shadowPaintRect.Inflate(blurMargin);
1350 :
1351 0 : nsRect shadowClipRect = paddingRect;
1352 0 : shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
1353 0 : shadowClipRect.Deflate(shadowItem->mSpread, shadowItem->mSpread);
1354 :
1355 0 : gfxCornerSizes clipRectRadii;
1356 0 : if (hasBorderRadius) {
1357 : // Calculate the radii the inner clipping rect will have
1358 0 : gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
1359 0 : gfxFloat borderSizes[4] = {0, 0, 0, 0};
1360 :
1361 : // See PaintBoxShadowOuter and bug 514670
1362 0 : if (innerRadii[C_TL].width > 0 || innerRadii[C_BL].width > 0) {
1363 0 : borderSizes[NS_SIDE_LEFT] = spreadDistance;
1364 : }
1365 :
1366 0 : if (innerRadii[C_TL].height > 0 || innerRadii[C_TR].height > 0) {
1367 0 : borderSizes[NS_SIDE_TOP] = spreadDistance;
1368 : }
1369 :
1370 0 : if (innerRadii[C_TR].width > 0 || innerRadii[C_BR].width > 0) {
1371 0 : borderSizes[NS_SIDE_RIGHT] = spreadDistance;
1372 : }
1373 :
1374 0 : if (innerRadii[C_BL].height > 0 || innerRadii[C_BR].height > 0) {
1375 0 : borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
1376 : }
1377 :
1378 : nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
1379 0 : &clipRectRadii);
1380 : }
1381 :
1382 : // Set the "skip rect" to the area within the frame that we don't paint in,
1383 : // including after blurring.
1384 0 : nsRect skipRect = shadowClipRect;
1385 0 : skipRect.Deflate(blurMargin);
1386 0 : gfxRect skipGfxRect = nsLayoutUtils::RectToGfxRect(skipRect, twipsPerPixel);
1387 0 : if (hasBorderRadius) {
1388 : skipGfxRect.Deflate(
1389 0 : gfxMargin(0, NS_MAX(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height),
1390 0 : 0, NS_MAX(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height)));
1391 : }
1392 :
1393 : // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
1394 : // unchanged. And by construction the gfxSkipRect is not touched by the
1395 : // rendered shadow (even after blurring), so those pixels must be completely
1396 : // transparent in the shadow, so drawing them changes nothing.
1397 0 : gfxContext* renderContext = aRenderingContext.ThebesContext();
1398 0 : nsRefPtr<gfxContext> shadowContext;
1399 0 : nsContextBoxBlur blurringArea;
1400 : shadowContext =
1401 : blurringArea.Init(shadowPaintRect, 0, blurRadius, twipsPerPixel,
1402 0 : renderContext, aDirtyRect, &skipGfxRect);
1403 0 : if (!shadowContext)
1404 0 : continue;
1405 :
1406 : // Set the shadow color; if not specified, use the foreground color
1407 : nscolor shadowColor;
1408 0 : if (shadowItem->mHasColor)
1409 0 : shadowColor = shadowItem->mColor;
1410 : else
1411 0 : shadowColor = aForFrame->GetStyleColor()->mColor;
1412 :
1413 0 : renderContext->Save();
1414 0 : renderContext->SetColor(gfxRGBA(shadowColor));
1415 :
1416 : // Clip the context to the area of the frame's padding rect, so no part of the
1417 : // shadow is painted outside. Also cut out anything beyond where the inset shadow
1418 : // will be.
1419 : gfxRect shadowGfxRect =
1420 0 : nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
1421 0 : shadowGfxRect.Round();
1422 0 : renderContext->NewPath();
1423 0 : if (hasBorderRadius)
1424 0 : renderContext->RoundedRectangle(shadowGfxRect, innerRadii, false);
1425 : else
1426 0 : renderContext->Rectangle(shadowGfxRect);
1427 0 : renderContext->Clip();
1428 :
1429 : // Fill the surface minus the area within the frame that we should
1430 : // not paint in, and blur and apply it.
1431 : gfxRect shadowPaintGfxRect =
1432 0 : nsLayoutUtils::RectToGfxRect(shadowPaintRect, twipsPerPixel);
1433 0 : shadowPaintGfxRect.RoundOut();
1434 : gfxRect shadowClipGfxRect =
1435 0 : nsLayoutUtils::RectToGfxRect(shadowClipRect, twipsPerPixel);
1436 0 : shadowClipGfxRect.Round();
1437 0 : shadowContext->NewPath();
1438 0 : shadowContext->Rectangle(shadowPaintGfxRect);
1439 0 : if (hasBorderRadius)
1440 0 : shadowContext->RoundedRectangle(shadowClipGfxRect, clipRectRadii, false);
1441 : else
1442 0 : shadowContext->Rectangle(shadowClipGfxRect);
1443 0 : shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
1444 0 : shadowContext->Fill();
1445 :
1446 0 : blurringArea.DoPaint();
1447 0 : renderContext->Restore();
1448 : }
1449 : }
1450 :
1451 : void
1452 0 : nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
1453 : nsRenderingContext& aRenderingContext,
1454 : nsIFrame* aForFrame,
1455 : const nsRect& aDirtyRect,
1456 : const nsRect& aBorderArea,
1457 : PRUint32 aFlags,
1458 : nsRect* aBGClipRect)
1459 : {
1460 0 : NS_PRECONDITION(aForFrame,
1461 : "Frame is expected to be provided to PaintBackground");
1462 :
1463 : nsStyleContext *sc;
1464 0 : if (!FindBackground(aPresContext, aForFrame, &sc)) {
1465 : // We don't want to bail out if moz-appearance is set on a root
1466 : // node. If it has a parent content node, bail because it's not
1467 : // a root, other wise keep going in order to let the theme stuff
1468 : // draw the background. The canvas really should be drawing the
1469 : // bg, but there's no way to hook that up via css.
1470 0 : if (!aForFrame->GetStyleDisplay()->mAppearance) {
1471 0 : return;
1472 : }
1473 :
1474 0 : nsIContent* content = aForFrame->GetContent();
1475 0 : if (!content || content->GetParent()) {
1476 0 : return;
1477 : }
1478 :
1479 0 : sc = aForFrame->GetStyleContext();
1480 : }
1481 :
1482 : PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
1483 : aDirtyRect, aBorderArea, sc,
1484 0 : *aForFrame->GetStyleBorder(), aFlags,
1485 0 : aBGClipRect);
1486 : }
1487 :
1488 : static bool
1489 0 : IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::css::Side aSide)
1490 : {
1491 0 : if (aBorder.GetActualBorder().Side(aSide) == 0)
1492 0 : return true;
1493 0 : switch (aBorder.GetBorderStyle(aSide)) {
1494 : case NS_STYLE_BORDER_STYLE_SOLID:
1495 : case NS_STYLE_BORDER_STYLE_GROOVE:
1496 : case NS_STYLE_BORDER_STYLE_RIDGE:
1497 : case NS_STYLE_BORDER_STYLE_INSET:
1498 : case NS_STYLE_BORDER_STYLE_OUTSET:
1499 : break;
1500 : default:
1501 0 : return false;
1502 : }
1503 :
1504 : // If we're using a border image, assume it's not fully opaque,
1505 : // because we may not even have the image loaded at this point, and
1506 : // even if we did, checking whether the relevant tile is fully
1507 : // opaque would be too much work.
1508 0 : if (aBorder.GetBorderImage())
1509 0 : return false;
1510 :
1511 : nscolor color;
1512 : bool isForeground;
1513 0 : aBorder.GetBorderColor(aSide, color, isForeground);
1514 :
1515 : // We don't know the foreground color here, so if it's being used
1516 : // we must assume it might be transparent.
1517 0 : if (isForeground)
1518 0 : return false;
1519 :
1520 0 : return NS_GET_A(color) == 255;
1521 : }
1522 :
1523 : /**
1524 : * Returns true if all border edges are either missing or opaque.
1525 : */
1526 : static bool
1527 0 : IsOpaqueBorder(const nsStyleBorder& aBorder)
1528 : {
1529 0 : if (aBorder.mBorderColors)
1530 0 : return false;
1531 0 : NS_FOR_CSS_SIDES(i) {
1532 0 : if (!IsOpaqueBorderEdge(aBorder, i))
1533 0 : return false;
1534 : }
1535 0 : return true;
1536 : }
1537 :
1538 : static inline void
1539 0 : SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect,
1540 : nscoord aAppUnitsPerPixel,
1541 : /* OUT: */
1542 : nsRect* aDirtyRect, gfxRect* aDirtyRectGfx)
1543 : {
1544 0 : aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect);
1545 :
1546 : // Compute the Thebes equivalent of the dirtyRect.
1547 0 : *aDirtyRectGfx = nsLayoutUtils::RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
1548 0 : NS_WARN_IF_FALSE(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(),
1549 : "converted dirty rect should not be empty");
1550 0 : NS_ABORT_IF_FALSE(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(),
1551 : "second should be empty if first is");
1552 0 : }
1553 :
1554 0 : struct BackgroundClipState {
1555 : nsRect mBGClipArea;
1556 : nsRect mDirtyRect;
1557 : gfxRect mDirtyRectGfx;
1558 :
1559 : gfxCornerSizes mClippedRadii;
1560 : bool mRadiiAreOuter;
1561 :
1562 : // Whether we are being asked to draw with a caller provided background
1563 : // clipping area. If this is true we also disable rounded corners.
1564 : bool mCustomClip;
1565 : };
1566 :
1567 : static void
1568 0 : GetBackgroundClip(gfxContext *aCtx, PRUint8 aBackgroundClip,
1569 : nsIFrame* aForFrame, const nsRect& aBorderArea,
1570 : const nsRect& aCallerDirtyRect, bool aHaveRoundedCorners,
1571 : const gfxCornerSizes& aBGRadii, nscoord aAppUnitsPerPixel,
1572 : /* out */ BackgroundClipState* aClipState)
1573 : {
1574 0 : aClipState->mBGClipArea = aBorderArea;
1575 0 : aClipState->mCustomClip = false;
1576 0 : aClipState->mRadiiAreOuter = true;
1577 0 : aClipState->mClippedRadii = aBGRadii;
1578 0 : if (aBackgroundClip != NS_STYLE_BG_CLIP_BORDER) {
1579 0 : nsMargin border = aForFrame->GetUsedBorder();
1580 0 : if (aBackgroundClip == NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING) {
1581 : // Reduce |border| by 1px (device pixels) on all sides, if
1582 : // possible, so that we don't get antialiasing seams between the
1583 : // background and border.
1584 0 : border.top = NS_MAX(0, border.top - aAppUnitsPerPixel);
1585 0 : border.right = NS_MAX(0, border.right - aAppUnitsPerPixel);
1586 0 : border.bottom = NS_MAX(0, border.bottom - aAppUnitsPerPixel);
1587 0 : border.left = NS_MAX(0, border.left - aAppUnitsPerPixel);
1588 0 : } else if (aBackgroundClip != NS_STYLE_BG_CLIP_PADDING) {
1589 0 : NS_ASSERTION(aBackgroundClip == NS_STYLE_BG_CLIP_CONTENT,
1590 : "unexpected background-clip");
1591 0 : border += aForFrame->GetUsedPadding();
1592 : }
1593 0 : aForFrame->ApplySkipSides(border);
1594 0 : aClipState->mBGClipArea.Deflate(border);
1595 :
1596 0 : if (aHaveRoundedCorners) {
1597 : gfxFloat borderSizes[4] = {
1598 : gfxFloat(border.top / aAppUnitsPerPixel),
1599 : gfxFloat(border.right / aAppUnitsPerPixel),
1600 : gfxFloat(border.bottom / aAppUnitsPerPixel),
1601 : gfxFloat(border.left / aAppUnitsPerPixel)
1602 0 : };
1603 : nsCSSBorderRenderer::ComputeInnerRadii(aBGRadii, borderSizes,
1604 0 : &aClipState->mClippedRadii);
1605 0 : aClipState->mRadiiAreOuter = false;
1606 : }
1607 : }
1608 :
1609 : SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, aAppUnitsPerPixel,
1610 0 : &aClipState->mDirtyRect, &aClipState->mDirtyRectGfx);
1611 0 : }
1612 :
1613 : static void
1614 0 : SetupBackgroundClip(BackgroundClipState& aClipState, gfxContext *aCtx,
1615 : bool aHaveRoundedCorners, nscoord aAppUnitsPerPixel,
1616 : gfxContextAutoSaveRestore* aAutoSR)
1617 : {
1618 0 : if (aClipState.mDirtyRectGfx.IsEmpty()) {
1619 : // Our caller won't draw anything under this condition, so no need
1620 : // to set more up.
1621 0 : return;
1622 : }
1623 :
1624 0 : if (aClipState.mCustomClip) {
1625 : // We don't support custom clips and rounded corners, arguably a bug, but
1626 : // table painting seems to depend on it.
1627 0 : return;
1628 : }
1629 :
1630 : // If we have rounded corners, clip all subsequent drawing to the
1631 : // rounded rectangle defined by bgArea and bgRadii (we don't know
1632 : // whether the rounded corners intrude on the dirtyRect or not).
1633 : // Do not do this if we have a caller-provided clip rect --
1634 : // as above with bgArea, arguably a bug, but table painting seems
1635 : // to depend on it.
1636 :
1637 0 : if (aHaveRoundedCorners) {
1638 : gfxRect bgAreaGfx =
1639 0 : nsLayoutUtils::RectToGfxRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
1640 0 : bgAreaGfx.Round();
1641 0 : bgAreaGfx.Condition();
1642 :
1643 0 : if (bgAreaGfx.IsEmpty()) {
1644 : // I think it's become possible to hit this since
1645 : // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1646 0 : NS_WARNING("converted background area should not be empty");
1647 : // Make our caller not do anything.
1648 0 : aClipState.mDirtyRectGfx.SizeTo(gfxSize(0.0, 0.0));
1649 0 : return;
1650 : }
1651 :
1652 0 : aAutoSR->Reset(aCtx);
1653 0 : aCtx->NewPath();
1654 0 : aCtx->RoundedRectangle(bgAreaGfx, aClipState.mClippedRadii, aClipState.mRadiiAreOuter);
1655 0 : aCtx->Clip();
1656 : }
1657 : }
1658 :
1659 : static void
1660 0 : DrawBackgroundColor(BackgroundClipState& aClipState, gfxContext *aCtx,
1661 : bool aHaveRoundedCorners, nscoord aAppUnitsPerPixel)
1662 : {
1663 0 : if (aClipState.mDirtyRectGfx.IsEmpty()) {
1664 : // Our caller won't draw anything under this condition, so no need
1665 : // to set more up.
1666 0 : return;
1667 : }
1668 :
1669 : // We don't support custom clips and rounded corners, arguably a bug, but
1670 : // table painting seems to depend on it.
1671 0 : if (!aHaveRoundedCorners || aClipState.mCustomClip) {
1672 0 : aCtx->NewPath();
1673 0 : aCtx->Rectangle(aClipState.mDirtyRectGfx, true);
1674 0 : aCtx->Fill();
1675 0 : return;
1676 : }
1677 :
1678 : gfxRect bgAreaGfx =
1679 0 : nsLayoutUtils::RectToGfxRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
1680 0 : bgAreaGfx.Round();
1681 0 : bgAreaGfx.Condition();
1682 :
1683 0 : if (bgAreaGfx.IsEmpty()) {
1684 : // I think it's become possible to hit this since
1685 : // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
1686 0 : NS_WARNING("converted background area should not be empty");
1687 : // Make our caller not do anything.
1688 0 : aClipState.mDirtyRectGfx.SizeTo(gfxSize(0.0, 0.0));
1689 0 : return;
1690 : }
1691 :
1692 0 : aCtx->Save();
1693 0 : gfxRect dirty = bgAreaGfx.Intersect(aClipState.mDirtyRectGfx);
1694 :
1695 0 : aCtx->NewPath();
1696 0 : aCtx->Rectangle(dirty, true);
1697 0 : aCtx->Clip();
1698 :
1699 0 : aCtx->NewPath();
1700 : aCtx->RoundedRectangle(bgAreaGfx, aClipState.mClippedRadii,
1701 0 : aClipState.mRadiiAreOuter);
1702 0 : aCtx->Fill();
1703 0 : aCtx->Restore();
1704 : }
1705 :
1706 : static nscolor
1707 0 : DetermineBackgroundColorInternal(nsPresContext* aPresContext,
1708 : nsStyleContext* aStyleContext,
1709 : nsIFrame* aFrame,
1710 : bool& aDrawBackgroundImage,
1711 : bool& aDrawBackgroundColor)
1712 : {
1713 0 : aDrawBackgroundImage = true;
1714 0 : aDrawBackgroundColor = true;
1715 :
1716 0 : if (aFrame->HonorPrintBackgroundSettings()) {
1717 0 : aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
1718 0 : aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
1719 : }
1720 :
1721 : nscolor bgColor;
1722 0 : if (aDrawBackgroundColor) {
1723 : bgColor =
1724 0 : aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);
1725 0 : if (NS_GET_A(bgColor) == 0)
1726 0 : aDrawBackgroundColor = false;
1727 : } else {
1728 : // If GetBackgroundColorDraw() is false, we are still expected to
1729 : // draw color in the background of any frame that's not completely
1730 : // transparent, but we are expected to use white instead of whatever
1731 : // color was specified.
1732 0 : bgColor = NS_RGB(255, 255, 255);
1733 0 : if (aDrawBackgroundImage ||
1734 0 : !aStyleContext->GetStyleBackground()->IsTransparent())
1735 0 : aDrawBackgroundColor = true;
1736 : else
1737 0 : bgColor = NS_RGBA(0,0,0,0);
1738 : }
1739 :
1740 0 : return bgColor;
1741 : }
1742 :
1743 : nscolor
1744 0 : nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
1745 : nsStyleContext* aStyleContext,
1746 : nsIFrame* aFrame)
1747 : {
1748 : bool drawBackgroundImage;
1749 : bool drawBackgroundColor;
1750 : return DetermineBackgroundColorInternal(aPresContext,
1751 : aStyleContext,
1752 : aFrame,
1753 : drawBackgroundImage,
1754 0 : drawBackgroundColor);
1755 : }
1756 :
1757 : static gfxFloat
1758 0 : ConvertGradientValueToPixels(const nsStyleCoord& aCoord,
1759 : gfxFloat aFillLength,
1760 : PRInt32 aAppUnitsPerPixel)
1761 : {
1762 0 : switch (aCoord.GetUnit()) {
1763 : case eStyleUnit_Percent:
1764 0 : return aCoord.GetPercentValue() * aFillLength;
1765 : case eStyleUnit_Coord:
1766 0 : return NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), aAppUnitsPerPixel);
1767 : case eStyleUnit_Calc: {
1768 0 : const nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
1769 : return calc->mPercent * aFillLength +
1770 0 : NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
1771 : }
1772 : default:
1773 0 : NS_WARNING("Unexpected coord unit");
1774 0 : return 0;
1775 : }
1776 : }
1777 :
1778 : // Given a box with size aBoxSize and origin (0,0), and an angle aAngle,
1779 : // and a starting point for the gradient line aStart, find the endpoint of
1780 : // the gradient line --- the intersection of the gradient line with a line
1781 : // perpendicular to aAngle that passes through the farthest corner in the
1782 : // direction aAngle.
1783 : static gfxPoint
1784 0 : ComputeGradientLineEndFromAngle(const gfxPoint& aStart,
1785 : double aAngle,
1786 : const gfxSize& aBoxSize)
1787 : {
1788 0 : double dx = cos(-aAngle);
1789 0 : double dy = sin(-aAngle);
1790 : gfxPoint farthestCorner(dx > 0 ? aBoxSize.width : 0,
1791 0 : dy > 0 ? aBoxSize.height : 0);
1792 0 : gfxPoint delta = farthestCorner - aStart;
1793 0 : double u = delta.x*dy - delta.y*dx;
1794 0 : return farthestCorner + gfxPoint(-u*dy, u*dx);
1795 : }
1796 :
1797 : // Compute the start and end points of the gradient line for a linear gradient.
1798 : static void
1799 0 : ComputeLinearGradientLine(nsPresContext* aPresContext,
1800 : nsStyleGradient* aGradient,
1801 : const gfxSize& aBoxSize,
1802 : gfxPoint* aLineStart,
1803 : gfxPoint* aLineEnd)
1804 : {
1805 0 : if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
1806 : double angle;
1807 0 : if (aGradient->mAngle.IsAngleValue()) {
1808 0 : angle = aGradient->mAngle.GetAngleValueInRadians();
1809 : } else {
1810 0 : angle = -M_PI_2; // defaults to vertical gradient starting from top
1811 : }
1812 0 : gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
1813 0 : *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
1814 0 : *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
1815 0 : } else if (aGradient->mToCorner) {
1816 0 : float xSign = aGradient->mBgPosX.GetPercentValue() * 2 - 1;
1817 0 : float ySign = 1 - aGradient->mBgPosY.GetPercentValue() * 2;
1818 0 : double angle = atan2(ySign * aBoxSize.width, xSign * aBoxSize.height);
1819 0 : gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
1820 0 : *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
1821 0 : *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
1822 : } else {
1823 0 : PRInt32 appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
1824 : *aLineStart = gfxPoint(
1825 : ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
1826 : appUnitsPerPixel),
1827 : ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
1828 0 : appUnitsPerPixel));
1829 0 : if (aGradient->mAngle.IsAngleValue()) {
1830 0 : double angle = aGradient->mAngle.GetAngleValueInRadians();
1831 0 : *aLineEnd = ComputeGradientLineEndFromAngle(*aLineStart, angle, aBoxSize);
1832 : } else {
1833 : // No angle, the line end is just the reflection of the start point
1834 : // through the center of the box
1835 0 : *aLineEnd = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineStart;
1836 : }
1837 : }
1838 0 : }
1839 :
1840 : // Compute the start and end points of the gradient line for a radial gradient.
1841 : // Also returns the horizontal and vertical radii defining the circle or
1842 : // ellipse to use.
1843 : static void
1844 0 : ComputeRadialGradientLine(nsPresContext* aPresContext,
1845 : nsStyleGradient* aGradient,
1846 : const gfxSize& aBoxSize,
1847 : gfxPoint* aLineStart,
1848 : gfxPoint* aLineEnd,
1849 : double* aRadiusX,
1850 : double* aRadiusY)
1851 : {
1852 0 : if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
1853 : // Default line start point is the center of the box
1854 0 : *aLineStart = gfxPoint(aBoxSize.width/2, aBoxSize.height/2);
1855 : } else {
1856 0 : PRInt32 appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
1857 : *aLineStart = gfxPoint(
1858 : ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
1859 : appUnitsPerPixel),
1860 : ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
1861 0 : appUnitsPerPixel));
1862 : }
1863 :
1864 : // Compute gradient shape: the x and y radii of an ellipse.
1865 : double radiusX, radiusY;
1866 0 : double leftDistance = NS_ABS(aLineStart->x);
1867 0 : double rightDistance = NS_ABS(aBoxSize.width - aLineStart->x);
1868 0 : double topDistance = NS_ABS(aLineStart->y);
1869 0 : double bottomDistance = NS_ABS(aBoxSize.height - aLineStart->y);
1870 0 : switch (aGradient->mSize) {
1871 : case NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE:
1872 0 : radiusX = NS_MIN(leftDistance, rightDistance);
1873 0 : radiusY = NS_MIN(topDistance, bottomDistance);
1874 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
1875 0 : radiusX = radiusY = NS_MIN(radiusX, radiusY);
1876 : }
1877 0 : break;
1878 : case NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER: {
1879 : // Compute x and y distances to nearest corner
1880 0 : double offsetX = NS_MIN(leftDistance, rightDistance);
1881 0 : double offsetY = NS_MIN(topDistance, bottomDistance);
1882 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
1883 0 : radiusX = radiusY = NS_hypot(offsetX, offsetY);
1884 : } else {
1885 : // maintain aspect ratio
1886 0 : radiusX = offsetX*M_SQRT2;
1887 0 : radiusY = offsetY*M_SQRT2;
1888 : }
1889 0 : break;
1890 : }
1891 : case NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE:
1892 0 : radiusX = NS_MAX(leftDistance, rightDistance);
1893 0 : radiusY = NS_MAX(topDistance, bottomDistance);
1894 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
1895 0 : radiusX = radiusY = NS_MAX(radiusX, radiusY);
1896 : }
1897 0 : break;
1898 : case NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER: {
1899 : // Compute x and y distances to nearest corner
1900 0 : double offsetX = NS_MAX(leftDistance, rightDistance);
1901 0 : double offsetY = NS_MAX(topDistance, bottomDistance);
1902 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
1903 0 : radiusX = radiusY = NS_hypot(offsetX, offsetY);
1904 : } else {
1905 : // maintain aspect ratio
1906 0 : radiusX = offsetX*M_SQRT2;
1907 0 : radiusY = offsetY*M_SQRT2;
1908 : }
1909 0 : break;
1910 : }
1911 : default:
1912 0 : NS_ABORT_IF_FALSE(false, "unknown radial gradient sizing method");
1913 : }
1914 0 : *aRadiusX = radiusX;
1915 0 : *aRadiusY = radiusY;
1916 :
1917 : double angle;
1918 0 : if (aGradient->mAngle.IsAngleValue()) {
1919 0 : angle = aGradient->mAngle.GetAngleValueInRadians();
1920 : } else {
1921 : // Default angle is 0deg
1922 0 : angle = 0.0;
1923 : }
1924 :
1925 : // The gradient line end point is where the gradient line intersects
1926 : // the ellipse.
1927 0 : *aLineEnd = *aLineStart + gfxPoint(radiusX*cos(-angle), radiusY*sin(-angle));
1928 0 : }
1929 :
1930 : // A resolved color stop --- with a specific position along the gradient line,
1931 : // and a Thebes color
1932 0 : struct ColorStop {
1933 0 : ColorStop(double aPosition, nscolor aColor) :
1934 0 : mPosition(aPosition), mColor(aColor) {}
1935 : double mPosition; // along the gradient line; 0=start, 1=end
1936 : gfxRGBA mColor;
1937 : };
1938 :
1939 : // Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
1940 : // in unpremultiplied space, which is what SVG gradients and cairo
1941 : // gradients expect.
1942 : static gfxRGBA
1943 0 : InterpolateColor(const gfxRGBA& aC1, const gfxRGBA& aC2, double aFrac)
1944 : {
1945 0 : double other = 1 - aFrac;
1946 : return gfxRGBA(aC2.r*aFrac + aC1.r*other,
1947 : aC2.g*aFrac + aC1.g*other,
1948 : aC2.b*aFrac + aC1.b*other,
1949 0 : aC2.a*aFrac + aC1.a*other);
1950 : }
1951 :
1952 : static nscoord
1953 0 : FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim)
1954 : {
1955 0 : NS_ASSERTION(aTileDim > 0, "Non-positive tile dimension");
1956 0 : double multiples = floor(double(aDirtyCoord - aTilePos)/aTileDim);
1957 0 : return NSToCoordRound(multiples*aTileDim + aTilePos);
1958 : }
1959 :
1960 : void
1961 0 : nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
1962 : nsRenderingContext& aRenderingContext,
1963 : nsStyleGradient* aGradient,
1964 : const nsRect& aDirtyRect,
1965 : const nsRect& aOneCellArea,
1966 : const nsRect& aFillArea)
1967 : {
1968 0 : if (aOneCellArea.IsEmpty())
1969 0 : return;
1970 :
1971 0 : gfxContext *ctx = aRenderingContext.ThebesContext();
1972 0 : nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
1973 : gfxRect oneCellArea =
1974 0 : nsLayoutUtils::RectToGfxRect(aOneCellArea, appUnitsPerPixel);
1975 :
1976 : // Compute "gradient line" start and end relative to oneCellArea
1977 0 : gfxPoint lineStart, lineEnd;
1978 0 : double radiusX = 0, radiusY = 0; // for radial gradients only
1979 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
1980 : ComputeLinearGradientLine(aPresContext, aGradient, oneCellArea.Size(),
1981 0 : &lineStart, &lineEnd);
1982 : } else {
1983 : ComputeRadialGradientLine(aPresContext, aGradient, oneCellArea.Size(),
1984 0 : &lineStart, &lineEnd, &radiusX, &radiusY);
1985 : }
1986 : gfxFloat lineLength = NS_hypot(lineEnd.x - lineStart.x,
1987 0 : lineEnd.y - lineStart.y);
1988 :
1989 0 : NS_ABORT_IF_FALSE(aGradient->mStops.Length() >= 2,
1990 : "The parser should reject gradients with less than two stops");
1991 :
1992 : // Build color stop array and compute stop positions
1993 0 : nsTArray<ColorStop> stops;
1994 : // If there is a run of stops before stop i that did not have specified
1995 : // positions, then this is the index of the first stop in that run, otherwise
1996 : // it's -1.
1997 0 : PRInt32 firstUnsetPosition = -1;
1998 0 : for (PRUint32 i = 0; i < aGradient->mStops.Length(); ++i) {
1999 0 : const nsStyleGradientStop& stop = aGradient->mStops[i];
2000 : double position;
2001 0 : switch (stop.mLocation.GetUnit()) {
2002 : case eStyleUnit_None:
2003 0 : if (i == 0) {
2004 : // First stop defaults to position 0.0
2005 0 : position = 0.0;
2006 0 : } else if (i == aGradient->mStops.Length() - 1) {
2007 : // Last stop defaults to position 1.0
2008 0 : position = 1.0;
2009 : } else {
2010 : // Other stops with no specified position get their position assigned
2011 : // later by interpolation, see below.
2012 : // Remeber where the run of stops with no specified position starts,
2013 : // if it starts here.
2014 0 : if (firstUnsetPosition < 0) {
2015 0 : firstUnsetPosition = i;
2016 : }
2017 0 : stops.AppendElement(ColorStop(0, stop.mColor));
2018 0 : continue;
2019 : }
2020 0 : break;
2021 : case eStyleUnit_Percent:
2022 0 : position = stop.mLocation.GetPercentValue();
2023 0 : break;
2024 : case eStyleUnit_Coord:
2025 : position = lineLength < 1e-6 ? 0.0 :
2026 0 : stop.mLocation.GetCoordValue() / appUnitsPerPixel / lineLength;
2027 0 : break;
2028 : default:
2029 0 : NS_ABORT_IF_FALSE(false, "Unknown stop position type");
2030 : }
2031 :
2032 0 : if (i > 0) {
2033 : // Prevent decreasing stop positions by advancing this position
2034 : // to the previous stop position, if necessary
2035 0 : position = NS_MAX(position, stops[i - 1].mPosition);
2036 : }
2037 0 : stops.AppendElement(ColorStop(position, stop.mColor));
2038 0 : if (firstUnsetPosition > 0) {
2039 : // Interpolate positions for all stops that didn't have a specified position
2040 0 : double p = stops[firstUnsetPosition - 1].mPosition;
2041 0 : double d = (stops[i].mPosition - p)/(i - firstUnsetPosition + 1);
2042 0 : for (PRUint32 j = firstUnsetPosition; j < i; ++j) {
2043 0 : p += d;
2044 0 : stops[j].mPosition = p;
2045 : }
2046 0 : firstUnsetPosition = -1;
2047 : }
2048 : }
2049 :
2050 : // Eliminate negative-position stops if the gradient is radial.
2051 0 : double firstStop = stops[0].mPosition;
2052 0 : if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && firstStop < 0.0) {
2053 0 : if (aGradient->mRepeating) {
2054 : // Choose an instance of the repeated pattern that gives us all positive
2055 : // stop-offsets.
2056 0 : double lastStop = stops[stops.Length() - 1].mPosition;
2057 0 : double stopDelta = lastStop - firstStop;
2058 : // If all the stops are in approximately the same place then logic below
2059 : // will kick in that makes us draw just the last stop color, so don't
2060 : // try to do anything in that case. We certainly need to avoid
2061 : // dividing by zero.
2062 0 : if (stopDelta >= 1e-6) {
2063 0 : double instanceCount = ceil(-firstStop/stopDelta);
2064 : // Advance stops by instanceCount multiples of the period of the
2065 : // repeating gradient.
2066 0 : double offset = instanceCount*stopDelta;
2067 0 : for (PRUint32 i = 0; i < stops.Length(); i++) {
2068 0 : stops[i].mPosition += offset;
2069 : }
2070 : }
2071 : } else {
2072 : // Move negative-position stops to position 0.0. We may also need
2073 : // to set the color of the stop to the color the gradient should have
2074 : // at the center of the ellipse.
2075 0 : for (PRUint32 i = 0; i < stops.Length(); i++) {
2076 0 : double pos = stops[i].mPosition;
2077 0 : if (pos < 0.0) {
2078 0 : stops[i].mPosition = 0.0;
2079 : // If this is the last stop, we don't need to adjust the color,
2080 : // it will fill the entire area.
2081 0 : if (i < stops.Length() - 1) {
2082 0 : double nextPos = stops[i + 1].mPosition;
2083 : // If nextPos is approximately equal to pos, then we don't
2084 : // need to adjust the color of this stop because it's
2085 : // not going to be displayed.
2086 : // If nextPos is negative, we don't need to adjust the color of
2087 : // this stop since it's not going to be displayed because
2088 : // nextPos will also be moved to 0.0.
2089 0 : if (nextPos >= 0.0 && nextPos - pos >= 1e-6) {
2090 : // Compute how far the new position 0.0 is along the interval
2091 : // between pos and nextPos.
2092 : // XXX Color interpolation (in cairo, too) should use the
2093 : // CSS 'color-interpolation' property!
2094 0 : double frac = (0.0 - pos)/(nextPos - pos);
2095 0 : stops[i].mColor =
2096 0 : InterpolateColor(stops[i].mColor, stops[i + 1].mColor, frac);
2097 : }
2098 : }
2099 : }
2100 : }
2101 : }
2102 0 : firstStop = stops[0].mPosition;
2103 0 : NS_ABORT_IF_FALSE(firstStop >= 0.0, "Failed to fix stop offsets");
2104 : }
2105 :
2106 0 : double lastStop = stops[stops.Length() - 1].mPosition;
2107 : // Cairo gradients must have stop positions in the range [0, 1]. So,
2108 : // stop positions will be normalized below by subtracting firstStop and then
2109 : // multiplying by stopScale.
2110 : double stopScale;
2111 0 : double stopDelta = lastStop - firstStop;
2112 : bool zeroRadius = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
2113 0 : (radiusX < 1e-6 || radiusY < 1e-6);
2114 0 : if (stopDelta < 1e-6 || lineLength < 1e-6 || zeroRadius) {
2115 : // Stops are all at the same place. Map all stops to 0.0.
2116 : // For repeating radial gradients, or for any radial gradients with
2117 : // a zero radius, we need to fill with the last stop color, so just set
2118 : // both radii to 0.
2119 0 : stopScale = 0.0;
2120 0 : if (aGradient->mRepeating || zeroRadius) {
2121 0 : radiusX = radiusY = 0.0;
2122 : }
2123 0 : lastStop = firstStop;
2124 : } else {
2125 0 : stopScale = 1.0/stopDelta;
2126 : }
2127 :
2128 : // Create the gradient pattern.
2129 0 : nsRefPtr<gfxPattern> gradientPattern;
2130 0 : if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
2131 : // Compute the actual gradient line ends we need to pass to cairo after
2132 : // stops have been normalized.
2133 0 : gfxPoint gradientStart = lineStart + (lineEnd - lineStart)*firstStop;
2134 0 : gfxPoint gradientEnd = lineStart + (lineEnd - lineStart)*lastStop;
2135 :
2136 0 : if (stopScale == 0.0) {
2137 : // Stops are all at the same place. For repeating gradients, this will
2138 : // just paint the last stop color. We don't need to do anything.
2139 : // For non-repeating gradients, this should render as two colors, one
2140 : // on each "side" of the gradient line segment, which is a point. All
2141 : // our stops will be at 0.0; we just need to set the direction vector
2142 : // correctly.
2143 0 : gradientEnd = gradientStart + (lineEnd - lineStart);
2144 : }
2145 :
2146 : gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
2147 0 : gradientEnd.x, gradientEnd.y);
2148 : } else {
2149 0 : NS_ASSERTION(firstStop >= 0.0,
2150 : "Negative stops not allowed for radial gradients");
2151 :
2152 : // To form an ellipse, we'll stretch a circle vertically, if necessary.
2153 : // So our radii are based on radiusX.
2154 0 : double innerRadius = radiusX*firstStop;
2155 0 : double outerRadius = radiusX*lastStop;
2156 0 : if (stopScale == 0.0) {
2157 : // Stops are all at the same place. See above (except we now have
2158 : // the inside vs. outside of an ellipse).
2159 0 : outerRadius = innerRadius + 1;
2160 : }
2161 : gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius,
2162 0 : lineStart.x, lineStart.y, outerRadius);
2163 0 : if (radiusX != radiusY) {
2164 : // Stretch the circles into ellipses vertically by setting a transform
2165 : // in the pattern.
2166 : // Recall that this is the transform from user space to pattern space.
2167 : // So to stretch the ellipse by factor of P vertically, we scale
2168 : // user coordinates by 1/P.
2169 0 : gfxMatrix matrix;
2170 0 : matrix.Translate(lineStart);
2171 0 : matrix.Scale(1.0, radiusX/radiusY);
2172 0 : matrix.Translate(-lineStart);
2173 0 : gradientPattern->SetMatrix(matrix);
2174 : }
2175 : }
2176 0 : if (gradientPattern->CairoStatus())
2177 : return;
2178 :
2179 : // Now set normalized color stops in pattern.
2180 0 : if (stopScale == 0.0) {
2181 : // Non-repeating gradient with all stops in same place -> just add
2182 : // first stop and last stop, both at position 0.
2183 : // Repeating gradient with all stops in the same place, or radial
2184 : // gradient with radius of 0 -> just paint the last stop color.
2185 0 : if (!aGradient->mRepeating && !zeroRadius) {
2186 0 : gradientPattern->AddColorStop(0.0, stops[0].mColor);
2187 : }
2188 0 : gradientPattern->AddColorStop(0.0, stops[stops.Length() - 1].mColor);
2189 : } else {
2190 : // Use all stops
2191 0 : for (PRUint32 i = 0; i < stops.Length(); i++) {
2192 0 : double pos = stopScale*(stops[i].mPosition - firstStop);
2193 0 : gradientPattern->AddColorStop(pos, stops[i].mColor);
2194 : }
2195 : }
2196 :
2197 : // Set repeat mode. Default cairo extend mode is PAD.
2198 0 : if (aGradient->mRepeating) {
2199 0 : gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
2200 : }
2201 :
2202 : // Paint gradient tiles. This isn't terribly efficient, but doing it this
2203 : // way is simple and sure to get pixel-snapping right. We could speed things
2204 : // up by drawing tiles into temporary surfaces and copying those to the
2205 : // destination, but after pixel-snapping tiles may not all be the same size.
2206 0 : nsRect dirty;
2207 0 : if (!dirty.IntersectRect(aDirtyRect, aFillArea))
2208 : return;
2209 :
2210 : gfxRect areaToFill =
2211 0 : nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerPixel);
2212 0 : gfxMatrix ctm = ctx->CurrentMatrix();
2213 :
2214 : // xStart/yStart are the top-left corner of the top-left tile.
2215 0 : nscoord xStart = FindTileStart(dirty.x, aOneCellArea.x, aOneCellArea.width);
2216 0 : nscoord yStart = FindTileStart(dirty.y, aOneCellArea.y, aOneCellArea.height);
2217 0 : nscoord xEnd = dirty.XMost();
2218 0 : nscoord yEnd = dirty.YMost();
2219 : // x and y are the top-left corner of the tile to draw
2220 0 : for (nscoord y = yStart; y < yEnd; y += aOneCellArea.height) {
2221 0 : for (nscoord x = xStart; x < xEnd; x += aOneCellArea.width) {
2222 : // The coordinates of the tile
2223 : gfxRect tileRect = nsLayoutUtils::RectToGfxRect(
2224 0 : nsRect(x, y, aOneCellArea.width, aOneCellArea.height),
2225 0 : appUnitsPerPixel);
2226 : // The actual area to fill with this tile is the intersection of this
2227 : // tile with the overall area we're supposed to be filling
2228 0 : gfxRect fillRect = tileRect.Intersect(areaToFill);
2229 0 : ctx->NewPath();
2230 0 : ctx->Translate(tileRect.TopLeft());
2231 0 : ctx->SetPattern(gradientPattern);
2232 0 : ctx->Rectangle(fillRect - tileRect.TopLeft(), true);
2233 0 : ctx->Fill();
2234 0 : ctx->SetMatrix(ctm);
2235 : }
2236 : }
2237 : }
2238 :
2239 : /**
2240 : * A struct representing all the information needed to paint a background
2241 : * image to some target, taking into account all CSS background-* properties.
2242 : * See PrepareBackgroundLayer.
2243 : */
2244 0 : struct BackgroundLayerState {
2245 : /**
2246 : * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags
2247 : */
2248 0 : BackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage, PRUint32 aFlags)
2249 0 : : mImageRenderer(aForFrame, aImage, aFlags) {}
2250 :
2251 : /**
2252 : * The ImageRenderer that will be used to draw the background.
2253 : */
2254 : ImageRenderer mImageRenderer;
2255 : /**
2256 : * A rectangle that one copy of the image tile is mapped onto. Same
2257 : * coordinate system as aBorderArea/aBGClipRect passed into
2258 : * PrepareBackgroundLayer.
2259 : */
2260 : nsRect mDestArea;
2261 : /**
2262 : * The actual rectangle that should be filled with (complete or partial)
2263 : * image tiles. Same coordinate system as aBorderArea/aBGClipRect passed into
2264 : * PrepareBackgroundLayer.
2265 : */
2266 : nsRect mFillArea;
2267 : /**
2268 : * The anchor point that should be snapped to a pixel corner. Same
2269 : * coordinate system as aBorderArea/aBGClipRect passed into
2270 : * PrepareBackgroundLayer.
2271 : */
2272 : nsPoint mAnchor;
2273 : };
2274 :
2275 : static BackgroundLayerState
2276 : PrepareBackgroundLayer(nsPresContext* aPresContext,
2277 : nsIFrame* aForFrame,
2278 : PRUint32 aFlags,
2279 : const nsRect& aBorderArea,
2280 : const nsRect& aBGClipRect,
2281 : const nsStyleBackground& aBackground,
2282 : const nsStyleBackground::Layer& aLayer);
2283 :
2284 : void
2285 0 : nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
2286 : nsRenderingContext& aRenderingContext,
2287 : nsIFrame* aForFrame,
2288 : const nsRect& aDirtyRect,
2289 : const nsRect& aBorderArea,
2290 : nsStyleContext* aBackgroundSC,
2291 : const nsStyleBorder& aBorder,
2292 : PRUint32 aFlags,
2293 : nsRect* aBGClipRect)
2294 : {
2295 0 : NS_PRECONDITION(aForFrame,
2296 : "Frame is expected to be provided to PaintBackground");
2297 :
2298 : // Check to see if we have an appearance defined. If so, we let the theme
2299 : // renderer draw the background and bail out.
2300 : // XXXzw this ignores aBGClipRect.
2301 0 : const nsStyleDisplay* displayData = aForFrame->GetStyleDisplay();
2302 0 : if (displayData->mAppearance) {
2303 0 : nsITheme *theme = aPresContext->GetTheme();
2304 0 : if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
2305 0 : displayData->mAppearance)) {
2306 0 : nsRect drawing(aBorderArea);
2307 : theme->GetWidgetOverflow(aPresContext->DeviceContext(),
2308 0 : aForFrame, displayData->mAppearance, &drawing);
2309 0 : drawing.IntersectRect(drawing, aDirtyRect);
2310 : theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
2311 : displayData->mAppearance, aBorderArea,
2312 0 : drawing);
2313 : return;
2314 : }
2315 : }
2316 :
2317 : // For canvas frames (in the CSS sense) we draw the background color using
2318 : // a solid color item that gets added in nsLayoutUtils::PaintFrame,
2319 : // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
2320 : // color may be moved into nsDisplayCanvasBackground by
2321 : // nsPresShell::AddCanvasBackgroundColorItem, and painted by
2322 : // nsDisplayCanvasBackground directly.) Either way we don't need to
2323 : // paint the background color here.
2324 0 : bool isCanvasFrame = IsCanvasFrame(aForFrame);
2325 :
2326 : // Determine whether we are drawing background images and/or
2327 : // background colors.
2328 : bool drawBackgroundImage;
2329 : bool drawBackgroundColor;
2330 :
2331 : nscolor bgColor = DetermineBackgroundColorInternal(aPresContext,
2332 : aBackgroundSC,
2333 : aForFrame,
2334 : drawBackgroundImage,
2335 0 : drawBackgroundColor);
2336 :
2337 : // At this point, drawBackgroundImage and drawBackgroundColor are
2338 : // true if and only if we are actually supposed to paint an image or
2339 : // color into aDirtyRect, respectively.
2340 0 : if (!drawBackgroundImage && !drawBackgroundColor)
2341 0 : return;
2342 :
2343 : // Compute the outermost boundary of the area that might be painted.
2344 0 : gfxContext *ctx = aRenderingContext.ThebesContext();
2345 0 : nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
2346 :
2347 : // Same coordinate space as aBorderArea & aBGClipRect
2348 0 : gfxCornerSizes bgRadii;
2349 : bool haveRoundedCorners;
2350 : {
2351 : nscoord radii[8];
2352 0 : nsSize frameSize = aForFrame->GetSize();
2353 0 : if (&aBorder == aForFrame->GetStyleBorder() &&
2354 0 : frameSize == aBorderArea.Size()) {
2355 0 : haveRoundedCorners = aForFrame->GetBorderRadii(radii);
2356 : } else {
2357 : haveRoundedCorners = nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius,
2358 0 : frameSize, aBorderArea.Size(),
2359 0 : aForFrame->GetSkipSides(), radii);
2360 : }
2361 0 : if (haveRoundedCorners)
2362 0 : ComputePixelRadii(radii, appUnitsPerPixel, &bgRadii);
2363 : }
2364 :
2365 : // The 'bgClipArea' (used only by the image tiling logic, far below)
2366 : // is the caller-provided aBGClipRect if any, or else the area
2367 : // determined by the value of 'background-clip' in
2368 : // SetupCurrentBackgroundClip. (Arguably it should be the
2369 : // intersection, but that breaks the table painter -- in particular,
2370 : // taking the intersection breaks reftests/bugs/403249-1[ab].)
2371 0 : const nsStyleBackground *bg = aBackgroundSC->GetStyleBackground();
2372 0 : BackgroundClipState clipState;
2373 : PRUint8 currentBackgroundClip;
2374 : bool isSolidBorder;
2375 0 : if (aBGClipRect) {
2376 0 : clipState.mBGClipArea = *aBGClipRect;
2377 0 : clipState.mCustomClip = true;
2378 : SetupDirtyRects(clipState.mBGClipArea, aDirtyRect, appUnitsPerPixel,
2379 0 : &clipState.mDirtyRect, &clipState.mDirtyRectGfx);
2380 : } else {
2381 : // The background is rendered over the 'background-clip' area,
2382 : // which is normally equal to the border area but may be reduced
2383 : // to the padding area by CSS. Also, if the border is solid, we
2384 : // don't need to draw outside the padding area. In either case,
2385 : // if the borders are rounded, make sure we use the same inner
2386 : // radii as the border code will.
2387 : // The background-color is drawn based on the bottom
2388 : // background-clip.
2389 0 : currentBackgroundClip = bg->BottomLayer().mClip;
2390 : isSolidBorder =
2391 0 : (aFlags & PAINTBG_WILL_PAINT_BORDER) && IsOpaqueBorder(aBorder);
2392 0 : if (isSolidBorder && currentBackgroundClip == NS_STYLE_BG_CLIP_BORDER) {
2393 : // If we have rounded corners, we need to inflate the background
2394 : // drawing area a bit to avoid seams between the border and
2395 : // background.
2396 : currentBackgroundClip = haveRoundedCorners ?
2397 0 : NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
2398 : }
2399 :
2400 : GetBackgroundClip(ctx, currentBackgroundClip, aForFrame, aBorderArea,
2401 : aDirtyRect, haveRoundedCorners, bgRadii, appUnitsPerPixel,
2402 0 : &clipState);
2403 : }
2404 :
2405 : // If we might be using a background color, go ahead and set it now.
2406 0 : if (drawBackgroundColor && !isCanvasFrame)
2407 0 : ctx->SetColor(gfxRGBA(bgColor));
2408 :
2409 0 : gfxContextAutoSaveRestore autoSR;
2410 :
2411 : // If there is no background image, draw a color. (If there is
2412 : // neither a background image nor a color, we wouldn't have gotten
2413 : // this far.)
2414 0 : if (!drawBackgroundImage) {
2415 0 : if (!isCanvasFrame) {
2416 0 : DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
2417 : }
2418 : return;
2419 : }
2420 :
2421 : // Ensure we get invalidated for loads of the image. We need to do
2422 : // this here because this might be the only code that knows about the
2423 : // association of the style data with the frame.
2424 0 : aPresContext->SetupBackgroundImageLoaders(aForFrame, bg);
2425 :
2426 : // We can skip painting the background color if a background image is opaque.
2427 0 : if (drawBackgroundColor &&
2428 0 : bg->BottomLayer().mRepeat.mXRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
2429 0 : bg->BottomLayer().mRepeat.mYRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
2430 0 : bg->BottomLayer().mImage.IsOpaque())
2431 0 : drawBackgroundColor = false;
2432 :
2433 : // The background color is rendered over the entire dirty area,
2434 : // even if the image isn't.
2435 0 : if (drawBackgroundColor && !isCanvasFrame) {
2436 0 : DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
2437 : }
2438 :
2439 0 : if (drawBackgroundImage) {
2440 0 : bool clipSet = false;
2441 0 : NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
2442 0 : const nsStyleBackground::Layer &layer = bg->mLayers[i];
2443 0 : if (!aBGClipRect) {
2444 0 : PRUint8 newBackgroundClip = layer.mClip;
2445 0 : if (isSolidBorder && newBackgroundClip == NS_STYLE_BG_CLIP_BORDER) {
2446 : newBackgroundClip = haveRoundedCorners ?
2447 0 : NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
2448 : }
2449 0 : if (currentBackgroundClip != newBackgroundClip || !clipSet) {
2450 0 : currentBackgroundClip = newBackgroundClip;
2451 : // If clipSet is false that means this is the bottom layer and we
2452 : // already called GetBackgroundClip above and it stored its results
2453 : // in clipState.
2454 0 : if (clipSet) {
2455 : GetBackgroundClip(ctx, currentBackgroundClip, aForFrame,
2456 : aBorderArea, aDirtyRect, haveRoundedCorners,
2457 0 : bgRadii, appUnitsPerPixel, &clipState);
2458 : }
2459 : SetupBackgroundClip(clipState, ctx, haveRoundedCorners,
2460 0 : appUnitsPerPixel, &autoSR);
2461 0 : clipSet = true;
2462 : }
2463 : }
2464 0 : if (!clipState.mDirtyRectGfx.IsEmpty()) {
2465 : BackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame,
2466 0 : aFlags, aBorderArea, clipState.mBGClipArea, *bg, layer);
2467 0 : if (!state.mFillArea.IsEmpty()) {
2468 : state.mImageRenderer.Draw(aPresContext, aRenderingContext,
2469 : state.mDestArea, state.mFillArea,
2470 : state.mAnchor + aBorderArea.TopLeft(),
2471 0 : clipState.mDirtyRect);
2472 : }
2473 : }
2474 : }
2475 : }
2476 : }
2477 :
2478 : static inline bool
2479 0 : IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame)
2480 : {
2481 0 : for (nsIFrame* f = aForFrame; f != aTopFrame; f = f->GetParent()) {
2482 0 : if (f->IsTransformed()) {
2483 0 : return true;
2484 : }
2485 : }
2486 0 : return false;
2487 : }
2488 :
2489 : static BackgroundLayerState
2490 0 : PrepareBackgroundLayer(nsPresContext* aPresContext,
2491 : nsIFrame* aForFrame,
2492 : PRUint32 aFlags,
2493 : const nsRect& aBorderArea,
2494 : const nsRect& aBGClipRect,
2495 : const nsStyleBackground& aBackground,
2496 : const nsStyleBackground::Layer& aLayer)
2497 : {
2498 : /*
2499 : * The background properties we need to keep in mind when drawing background
2500 : * layers are:
2501 : *
2502 : * background-image
2503 : * background-repeat
2504 : * background-attachment
2505 : * background-position
2506 : * background-clip
2507 : * background-origin
2508 : * background-size
2509 : * background-break (-moz-background-inline-policy)
2510 : *
2511 : * (background-color applies to the entire element and not to individual
2512 : * layers, so it is irrelevant to this method.)
2513 : *
2514 : * These properties have the following dependencies upon each other when
2515 : * determining rendering:
2516 : *
2517 : * background-image
2518 : * no dependencies
2519 : * background-repeat
2520 : * no dependencies
2521 : * background-attachment
2522 : * no dependencies
2523 : * background-position
2524 : * depends upon background-size (for the image's scaled size) and
2525 : * background-break (for the background positioning area)
2526 : * background-clip
2527 : * no dependencies
2528 : * background-origin
2529 : * depends upon background-attachment (only in the case where that value
2530 : * is 'fixed')
2531 : * background-size
2532 : * depends upon background-break (for the background positioning area for
2533 : * resolving percentages), background-image (for the image's intrinsic
2534 : * size), background-repeat (if that value is 'round'), and
2535 : * background-origin (for the background painting area, when
2536 : * background-repeat is 'round')
2537 : * background-break
2538 : * depends upon background-origin (specifying how the boxes making up the
2539 : * background positioning area are determined)
2540 : *
2541 : * As a result of only-if dependencies we don't strictly do a topological
2542 : * sort of the above properties when processing, but it's pretty close to one:
2543 : *
2544 : * background-clip (by caller)
2545 : * background-image
2546 : * background-break, background-origin
2547 : * background-attachment (postfix for background-{origin,break} if 'fixed')
2548 : * background-size
2549 : * background-position
2550 : * background-repeat
2551 : */
2552 :
2553 0 : PRUint32 irFlags = 0;
2554 0 : if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
2555 0 : irFlags |= ImageRenderer::FLAG_SYNC_DECODE_IMAGES;
2556 : }
2557 :
2558 0 : BackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
2559 0 : if (!state.mImageRenderer.PrepareImage()) {
2560 : // There's no image or it's not ready to be painted.
2561 0 : return state;
2562 : }
2563 :
2564 : // Compute background origin area relative to aBorderArea now as we may need
2565 : // it to compute the effective image size for a CSS gradient.
2566 0 : nsRect bgPositioningArea(0, 0, 0, 0);
2567 :
2568 0 : nsIAtom* frameType = aForFrame->GetType();
2569 0 : nsIFrame* geometryFrame = aForFrame;
2570 0 : if (frameType == nsGkAtoms::inlineFrame) {
2571 : // XXXjwalden Strictly speaking this is not quite faithful to how
2572 : // background-break is supposed to interact with background-origin values,
2573 : // but it's a non-trivial amount of work to make it fully conformant, and
2574 : // until the specification is more finalized (and assuming background-break
2575 : // even makes the cut) it doesn't make sense to hammer out exact behavior.
2576 0 : switch (aBackground.mBackgroundInlinePolicy) {
2577 : case NS_STYLE_BG_INLINE_POLICY_EACH_BOX:
2578 0 : bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size());
2579 0 : break;
2580 : case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:
2581 0 : bgPositioningArea = gInlineBGData->GetBoundingRect(aForFrame);
2582 0 : break;
2583 : default:
2584 : NS_ERROR("Unknown background-inline-policy value! "
2585 0 : "Please, teach me what to do.");
2586 : case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS:
2587 0 : bgPositioningArea = gInlineBGData->GetContinuousRect(aForFrame);
2588 0 : break;
2589 : }
2590 0 : } else if (frameType == nsGkAtoms::canvasFrame) {
2591 0 : geometryFrame = aForFrame->GetFirstPrincipalChild();
2592 : // geometryFrame might be null if this canvas is a page created
2593 : // as an overflow container (e.g. the in-flow content has already
2594 : // finished and this page only displays the continuations of
2595 : // absolutely positioned content).
2596 0 : if (geometryFrame) {
2597 0 : bgPositioningArea = geometryFrame->GetRect();
2598 : }
2599 : } else {
2600 0 : bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size());
2601 : }
2602 :
2603 : // Background images are tiled over the 'background-clip' area
2604 : // but the origin of the tiling is based on the 'background-origin' area
2605 0 : if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_BORDER && geometryFrame) {
2606 0 : nsMargin border = geometryFrame->GetUsedBorder();
2607 0 : if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_PADDING) {
2608 0 : border += geometryFrame->GetUsedPadding();
2609 0 : NS_ASSERTION(aLayer.mOrigin == NS_STYLE_BG_ORIGIN_CONTENT,
2610 : "unknown background-origin value");
2611 : }
2612 0 : geometryFrame->ApplySkipSides(border);
2613 0 : bgPositioningArea.Deflate(border);
2614 : }
2615 :
2616 : // For background-attachment:fixed backgrounds, we'll limit the area
2617 : // where the background can be drawn to the viewport.
2618 0 : nsRect bgClipRect = aBGClipRect;
2619 :
2620 : // Compute the anchor point.
2621 : //
2622 : // relative to aBorderArea.TopLeft() (which is where the top-left
2623 : // of aForFrame's border-box will be rendered)
2624 0 : nsPoint imageTopLeft;
2625 0 : if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) {
2626 0 : aPresContext->SetHasFixedBackgroundFrame();
2627 :
2628 : // If it's a fixed background attachment, then the image is placed
2629 : // relative to the viewport, which is the area of the root frame
2630 : // in a screen context or the page content frame in a print context.
2631 : nsIFrame* topFrame =
2632 0 : aPresContext->PresShell()->FrameManager()->GetRootFrame();
2633 0 : NS_ASSERTION(topFrame, "no root frame");
2634 0 : nsIFrame* pageContentFrame = nsnull;
2635 0 : if (aPresContext->IsPaginated()) {
2636 : pageContentFrame =
2637 0 : nsLayoutUtils::GetClosestFrameOfType(aForFrame, nsGkAtoms::pageContentFrame);
2638 0 : if (pageContentFrame) {
2639 0 : topFrame = pageContentFrame;
2640 : }
2641 : // else this is an embedded shell and its root frame is what we want
2642 : }
2643 :
2644 : // Set the background positioning area to the viewport's area
2645 : // (relative to aForFrame)
2646 0 : bgPositioningArea = nsRect(-aForFrame->GetOffsetTo(topFrame), topFrame->GetSize());
2647 :
2648 0 : if (!pageContentFrame) {
2649 : // Subtract the size of scrollbars.
2650 : nsIScrollableFrame* scrollableFrame =
2651 0 : aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
2652 0 : if (scrollableFrame) {
2653 0 : nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes();
2654 0 : bgPositioningArea.Deflate(scrollbars);
2655 : }
2656 : }
2657 :
2658 0 : if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW &&
2659 0 : !IsTransformed(aForFrame, topFrame)) {
2660 : // Clip background-attachment:fixed backgrounds to the viewport, if we're
2661 : // painting to the screen and not transformed. This avoids triggering
2662 : // tiling in common cases, without affecting output since drawing is
2663 : // always clipped to the viewport when we draw to the screen. (But it's
2664 : // not a pure optimization since it can affect the values of pixels at the
2665 : // edge of the viewport --- whether they're sampled from a putative "next
2666 : // tile" or not.)
2667 0 : bgClipRect.IntersectRect(bgClipRect, bgPositioningArea + aBorderArea.TopLeft());
2668 : }
2669 : }
2670 :
2671 : // Scale the image as specified for background-size and as required for
2672 : // proper background positioning when background-position is defined with
2673 : // percentages.
2674 0 : nsSize imageSize = state.mImageRenderer.ComputeSize(aLayer.mSize, bgPositioningArea.Size());
2675 0 : if (imageSize.width <= 0 || imageSize.height <= 0)
2676 : return state;
2677 :
2678 : // Compute the position of the background now that the background's size is
2679 : // determined.
2680 : ComputeBackgroundAnchorPoint(aLayer, bgPositioningArea.Size(), imageSize,
2681 0 : &imageTopLeft, &state.mAnchor);
2682 0 : imageTopLeft += bgPositioningArea.TopLeft();
2683 0 : state.mAnchor += bgPositioningArea.TopLeft();
2684 :
2685 0 : state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
2686 0 : state.mFillArea = state.mDestArea;
2687 0 : PRIntn repeatX = aLayer.mRepeat.mXRepeat;
2688 0 : PRIntn repeatY = aLayer.mRepeat.mYRepeat;
2689 0 : if (repeatX == NS_STYLE_BG_REPEAT_REPEAT) {
2690 0 : state.mFillArea.x = bgClipRect.x;
2691 0 : state.mFillArea.width = bgClipRect.width;
2692 : }
2693 0 : if (repeatY == NS_STYLE_BG_REPEAT_REPEAT) {
2694 0 : state.mFillArea.y = bgClipRect.y;
2695 0 : state.mFillArea.height = bgClipRect.height;
2696 : }
2697 0 : state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
2698 : return state;
2699 : }
2700 :
2701 : nsRect
2702 0 : nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
2703 : nsIFrame* aForFrame,
2704 : const nsRect& aBorderArea,
2705 : const nsStyleBackground& aBackground,
2706 : const nsStyleBackground::Layer& aLayer)
2707 : {
2708 : BackgroundLayerState state =
2709 : PrepareBackgroundLayer(aPresContext, aForFrame, 0, aBorderArea,
2710 0 : aBorderArea, aBackground, aLayer);
2711 0 : return state.mFillArea;
2712 : }
2713 :
2714 : static void
2715 0 : DrawBorderImage(nsPresContext* aPresContext,
2716 : nsRenderingContext& aRenderingContext,
2717 : nsIFrame* aForFrame,
2718 : const nsRect& aBorderArea,
2719 : const nsStyleBorder& aStyleBorder,
2720 : const nsRect& aDirtyRect)
2721 : {
2722 0 : NS_PRECONDITION(aStyleBorder.IsBorderImageLoaded(),
2723 : "drawing border image that isn't successfully loaded");
2724 :
2725 0 : if (aDirtyRect.IsEmpty())
2726 0 : return;
2727 :
2728 : // Ensure we get invalidated for loads and animations of the image.
2729 : // We need to do this here because this might be the only code that
2730 : // knows about the association of the style data with the frame.
2731 : // XXX We shouldn't really... since if anybody is passing in a
2732 : // different style, they'll potentially have the wrong size for the
2733 : // border too.
2734 0 : aPresContext->SetupBorderImageLoaders(aForFrame, &aStyleBorder);
2735 :
2736 0 : imgIRequest *req = aStyleBorder.GetBorderImage();
2737 :
2738 : // Get the actual image, and determine where the split points are.
2739 : // Note that mBorderImageSplit is in image pixels, not necessarily
2740 : // CSS pixels.
2741 :
2742 0 : nsCOMPtr<imgIContainer> imgContainer;
2743 0 : req->GetImage(getter_AddRefs(imgContainer));
2744 0 : NS_ASSERTION(imgContainer, "no image to draw");
2745 :
2746 0 : nsIntSize imageSize;
2747 0 : if (NS_FAILED(imgContainer->GetWidth(&imageSize.width))) {
2748 : imageSize.width =
2749 0 : nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.width);
2750 : }
2751 0 : if (NS_FAILED(imgContainer->GetHeight(&imageSize.height))) {
2752 : imageSize.height =
2753 0 : nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.height);
2754 : }
2755 :
2756 : // Convert percentages and clamp values to the image size.
2757 0 : nsIntMargin split;
2758 0 : NS_FOR_CSS_SIDES(s) {
2759 0 : nsStyleCoord coord = aStyleBorder.mBorderImageSplit.Get(s);
2760 : PRInt32 imgDimension = ((s == NS_SIDE_TOP || s == NS_SIDE_BOTTOM)
2761 : ? imageSize.height
2762 0 : : imageSize.width);
2763 : double value;
2764 0 : switch (coord.GetUnit()) {
2765 : case eStyleUnit_Percent:
2766 0 : value = coord.GetPercentValue() * imgDimension;
2767 0 : break;
2768 : case eStyleUnit_Factor:
2769 0 : value = coord.GetFactorValue();
2770 0 : break;
2771 : default:
2772 0 : NS_ASSERTION(coord.GetUnit() == eStyleUnit_Null,
2773 : "unexpected CSS unit for image split");
2774 0 : value = 0;
2775 0 : break;
2776 : }
2777 0 : if (value < 0)
2778 0 : value = 0;
2779 0 : if (value > imgDimension)
2780 0 : value = imgDimension;
2781 0 : split.Side(s) = NS_lround(value);
2782 : }
2783 :
2784 0 : nsMargin border(aStyleBorder.GetActualBorder());
2785 :
2786 : // These helper tables recharacterize the 'split' and 'border' margins
2787 : // in a more convenient form: they are the x/y/width/height coords
2788 : // required for various bands of the border, and they have been transformed
2789 : // to be relative to the image (for 'split') or the page (for 'border').
2790 : enum {
2791 : LEFT, MIDDLE, RIGHT,
2792 : TOP = LEFT, BOTTOM = RIGHT
2793 : };
2794 : const nscoord borderX[3] = {
2795 : aBorderArea.x + 0,
2796 : aBorderArea.x + border.left,
2797 : aBorderArea.x + aBorderArea.width - border.right,
2798 0 : };
2799 : const nscoord borderY[3] = {
2800 : aBorderArea.y + 0,
2801 : aBorderArea.y + border.top,
2802 : aBorderArea.y + aBorderArea.height - border.bottom,
2803 0 : };
2804 : const nscoord borderWidth[3] = {
2805 : border.left,
2806 : aBorderArea.width - border.left - border.right,
2807 : border.right,
2808 0 : };
2809 : const nscoord borderHeight[3] = {
2810 : border.top,
2811 : aBorderArea.height - border.top - border.bottom,
2812 : border.bottom,
2813 0 : };
2814 :
2815 : const PRInt32 splitX[3] = {
2816 : 0,
2817 : split.left,
2818 : imageSize.width - split.right,
2819 0 : };
2820 : const PRInt32 splitY[3] = {
2821 : 0,
2822 : split.top,
2823 : imageSize.height - split.bottom,
2824 0 : };
2825 : const PRInt32 splitWidth[3] = {
2826 : split.left,
2827 : imageSize.width - split.left - split.right,
2828 : split.right,
2829 0 : };
2830 : const PRInt32 splitHeight[3] = {
2831 : split.top,
2832 : imageSize.height - split.top - split.bottom,
2833 : split.bottom,
2834 0 : };
2835 :
2836 : // In all the 'factor' calculations below, 'border' measurements are
2837 : // in app units but 'split' measurements are in image/CSS pixels, so
2838 : // the factor corresponding to no additional scaling is
2839 : // CSSPixelsToAppUnits(1), not simply 1.
2840 0 : for (int i = LEFT; i <= RIGHT; i++) {
2841 0 : for (int j = TOP; j <= BOTTOM; j++) {
2842 0 : nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
2843 0 : nsIntRect subArea(splitX[i], splitY[j], splitWidth[i], splitHeight[j]);
2844 :
2845 : PRUint8 fillStyleH, fillStyleV;
2846 0 : nsSize unitSize;
2847 :
2848 0 : if (i == MIDDLE && j == MIDDLE) {
2849 : // css-background:
2850 : // The middle image's width is scaled by the same factor as the
2851 : // top image unless that factor is zero or infinity, in which
2852 : // case the scaling factor of the bottom is substituted, and
2853 : // failing that, the width is not scaled. The height of the
2854 : // middle image is scaled by the same factor as the left image
2855 : // unless that factor is zero or infinity, in which case the
2856 : // scaling factor of the right image is substituted, and failing
2857 : // that, the height is not scaled.
2858 : gfxFloat hFactor, vFactor;
2859 :
2860 0 : if (0 < border.left && 0 < split.left)
2861 0 : vFactor = gfxFloat(border.left)/split.left;
2862 0 : else if (0 < border.right && 0 < split.right)
2863 0 : vFactor = gfxFloat(border.right)/split.right;
2864 : else
2865 0 : vFactor = nsPresContext::CSSPixelsToAppUnits(1);
2866 :
2867 0 : if (0 < border.top && 0 < split.top)
2868 0 : hFactor = gfxFloat(border.top)/split.top;
2869 0 : else if (0 < border.bottom && 0 < split.bottom)
2870 0 : hFactor = gfxFloat(border.bottom)/split.bottom;
2871 : else
2872 0 : hFactor = nsPresContext::CSSPixelsToAppUnits(1);
2873 :
2874 0 : unitSize.width = splitWidth[i]*hFactor;
2875 0 : unitSize.height = splitHeight[j]*vFactor;
2876 0 : fillStyleH = aStyleBorder.mBorderImageHFill;
2877 0 : fillStyleV = aStyleBorder.mBorderImageVFill;
2878 :
2879 0 : } else if (i == MIDDLE) { // top, bottom
2880 : // Sides are always stretched to the thickness of their border,
2881 : // and stretched proportionately on the other axis.
2882 : gfxFloat factor;
2883 0 : if (0 < borderHeight[j] && 0 < splitHeight[j])
2884 0 : factor = gfxFloat(borderHeight[j])/splitHeight[j];
2885 : else
2886 0 : factor = nsPresContext::CSSPixelsToAppUnits(1);
2887 :
2888 0 : unitSize.width = splitWidth[i]*factor;
2889 0 : unitSize.height = borderHeight[j];
2890 0 : fillStyleH = aStyleBorder.mBorderImageHFill;
2891 0 : fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH;
2892 :
2893 0 : } else if (j == MIDDLE) { // left, right
2894 : gfxFloat factor;
2895 0 : if (0 < borderWidth[i] && 0 < splitWidth[i])
2896 0 : factor = gfxFloat(borderWidth[i])/splitWidth[i];
2897 : else
2898 0 : factor = nsPresContext::CSSPixelsToAppUnits(1);
2899 :
2900 0 : unitSize.width = borderWidth[i];
2901 0 : unitSize.height = splitHeight[j]*factor;
2902 0 : fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH;
2903 0 : fillStyleV = aStyleBorder.mBorderImageVFill;
2904 :
2905 : } else {
2906 : // Corners are always stretched to fit the corner.
2907 0 : unitSize.width = borderWidth[i];
2908 0 : unitSize.height = borderHeight[j];
2909 0 : fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH;
2910 0 : fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH;
2911 : }
2912 :
2913 : DrawBorderImageComponent(aRenderingContext, aForFrame,
2914 : imgContainer, aDirtyRect,
2915 : destArea, subArea,
2916 : fillStyleH, fillStyleV,
2917 0 : unitSize, aStyleBorder, i * (RIGHT + 1) + j);
2918 : }
2919 : }
2920 : }
2921 :
2922 : static void
2923 0 : DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
2924 : nsIFrame* aForFrame,
2925 : imgIContainer* aImage,
2926 : const nsRect& aDirtyRect,
2927 : const nsRect& aFill,
2928 : const nsIntRect& aSrc,
2929 : PRUint8 aHFill,
2930 : PRUint8 aVFill,
2931 : const nsSize& aUnitSize,
2932 : const nsStyleBorder& aStyleBorder,
2933 : PRUint8 aIndex)
2934 : {
2935 0 : if (aFill.IsEmpty() || aSrc.IsEmpty())
2936 0 : return;
2937 :
2938 : // Don't bother trying to cache sub images if the border image is animated
2939 : // We can only sucessfully call GetAnimated() if we are fully decoded, so default to true
2940 0 : bool animated = true;
2941 0 : aImage->GetAnimated(&animated);
2942 :
2943 0 : nsCOMPtr<imgIContainer> subImage;
2944 0 : if (animated || (subImage = aStyleBorder.GetSubImage(aIndex)) == 0) {
2945 0 : if (NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT, aSrc,
2946 : imgIContainer::FLAG_SYNC_DECODE,
2947 : getter_AddRefs(subImage))))
2948 : return;
2949 :
2950 0 : if (!animated)
2951 0 : aStyleBorder.SetSubImage(aIndex, subImage);
2952 : }
2953 :
2954 : gfxPattern::GraphicsFilter graphicsFilter =
2955 0 : nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame);
2956 :
2957 : // If we have no tiling in either direction, we can skip the intermediate
2958 : // scaling step.
2959 0 : if ((aHFill == NS_STYLE_BORDER_IMAGE_STRETCH &&
2960 : aVFill == NS_STYLE_BORDER_IMAGE_STRETCH) ||
2961 : (aUnitSize.width == aFill.width &&
2962 : aUnitSize.height == aFill.height)) {
2963 : nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
2964 : graphicsFilter,
2965 0 : aFill, aDirtyRect, imgIContainer::FLAG_NONE);
2966 : return;
2967 : }
2968 :
2969 : // Compute the scale and position of the master copy of the image.
2970 0 : nsRect tile;
2971 0 : switch (aHFill) {
2972 : case NS_STYLE_BORDER_IMAGE_STRETCH:
2973 0 : tile.x = aFill.x;
2974 0 : tile.width = aFill.width;
2975 0 : break;
2976 : case NS_STYLE_BORDER_IMAGE_REPEAT:
2977 0 : tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
2978 0 : tile.width = aUnitSize.width;
2979 0 : break;
2980 :
2981 : case NS_STYLE_BORDER_IMAGE_ROUND:
2982 0 : tile.x = aFill.x;
2983 0 : tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width);
2984 0 : break;
2985 :
2986 : default:
2987 0 : NS_NOTREACHED("unrecognized border-image fill style");
2988 : }
2989 :
2990 0 : switch (aVFill) {
2991 : case NS_STYLE_BORDER_IMAGE_STRETCH:
2992 0 : tile.y = aFill.y;
2993 0 : tile.height = aFill.height;
2994 0 : break;
2995 : case NS_STYLE_BORDER_IMAGE_REPEAT:
2996 0 : tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
2997 0 : tile.height = aUnitSize.height;
2998 0 : break;
2999 :
3000 : case NS_STYLE_BORDER_IMAGE_ROUND:
3001 0 : tile.y = aFill.y;
3002 0 : tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
3003 0 : break;
3004 :
3005 : default:
3006 0 : NS_NOTREACHED("unrecognized border-image fill style");
3007 : }
3008 :
3009 : nsLayoutUtils::DrawImage(&aRenderingContext, subImage, graphicsFilter,
3010 : tile, aFill, tile.TopLeft(), aDirtyRect,
3011 0 : imgIContainer::FLAG_NONE);
3012 : }
3013 :
3014 : // Begin table border-collapsing section
3015 : // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
3016 : // At some point, all functions should be unified to include the additional functionality that these provide
3017 :
3018 : static nscoord
3019 0 : RoundIntToPixel(nscoord aValue,
3020 : nscoord aTwipsPerPixel,
3021 : bool aRoundDown = false)
3022 : {
3023 0 : if (aTwipsPerPixel <= 0)
3024 : // We must be rendering to a device that has a resolution greater than Twips!
3025 : // In that case, aValue is as accurate as it's going to get.
3026 0 : return aValue;
3027 :
3028 0 : nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
3029 0 : nscoord extra = aValue % aTwipsPerPixel;
3030 0 : nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
3031 0 : return finalValue;
3032 : }
3033 :
3034 : static nscoord
3035 0 : RoundFloatToPixel(float aValue,
3036 : nscoord aTwipsPerPixel,
3037 : bool aRoundDown = false)
3038 : {
3039 0 : return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
3040 : }
3041 :
3042 : static void
3043 0 : SetPoly(const nsRect& aRect,
3044 : nsPoint* poly)
3045 : {
3046 0 : poly[0].x = aRect.x;
3047 0 : poly[0].y = aRect.y;
3048 0 : poly[1].x = aRect.x + aRect.width;
3049 0 : poly[1].y = aRect.y;
3050 0 : poly[2].x = aRect.x + aRect.width;
3051 0 : poly[2].y = aRect.y + aRect.height;
3052 0 : poly[3].x = aRect.x;
3053 0 : poly[3].y = aRect.y + aRect.height;
3054 0 : poly[4].x = aRect.x;
3055 0 : poly[4].y = aRect.y;
3056 0 : }
3057 :
3058 : static void
3059 0 : DrawSolidBorderSegment(nsRenderingContext& aContext,
3060 : nsRect aRect,
3061 : nscoord aTwipsPerPixel,
3062 : PRUint8 aStartBevelSide = 0,
3063 : nscoord aStartBevelOffset = 0,
3064 : PRUint8 aEndBevelSide = 0,
3065 : nscoord aEndBevelOffset = 0)
3066 : {
3067 :
3068 0 : if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) ||
3069 : ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
3070 : // simple line or rectangle
3071 0 : if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) {
3072 0 : if (1 == aRect.height)
3073 0 : aContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
3074 : else
3075 0 : aContext.FillRect(aRect);
3076 : }
3077 : else {
3078 0 : if (1 == aRect.width)
3079 0 : aContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
3080 : else
3081 0 : aContext.FillRect(aRect);
3082 : }
3083 : }
3084 : else {
3085 : // polygon with beveling
3086 0 : nsPoint poly[5];
3087 0 : SetPoly(aRect, poly);
3088 0 : switch(aStartBevelSide) {
3089 : case NS_SIDE_TOP:
3090 0 : poly[0].x += aStartBevelOffset;
3091 0 : poly[4].x = poly[0].x;
3092 0 : break;
3093 : case NS_SIDE_BOTTOM:
3094 0 : poly[3].x += aStartBevelOffset;
3095 0 : break;
3096 : case NS_SIDE_RIGHT:
3097 0 : poly[1].y += aStartBevelOffset;
3098 0 : break;
3099 : case NS_SIDE_LEFT:
3100 0 : poly[0].y += aStartBevelOffset;
3101 0 : poly[4].y = poly[0].y;
3102 : }
3103 :
3104 0 : switch(aEndBevelSide) {
3105 : case NS_SIDE_TOP:
3106 0 : poly[1].x -= aEndBevelOffset;
3107 0 : break;
3108 : case NS_SIDE_BOTTOM:
3109 0 : poly[2].x -= aEndBevelOffset;
3110 0 : break;
3111 : case NS_SIDE_RIGHT:
3112 0 : poly[2].y -= aEndBevelOffset;
3113 0 : break;
3114 : case NS_SIDE_LEFT:
3115 0 : poly[3].y -= aEndBevelOffset;
3116 : }
3117 :
3118 0 : aContext.FillPolygon(poly, 5);
3119 : }
3120 :
3121 :
3122 0 : }
3123 :
3124 : static void
3125 0 : GetDashInfo(nscoord aBorderLength,
3126 : nscoord aDashLength,
3127 : nscoord aTwipsPerPixel,
3128 : PRInt32& aNumDashSpaces,
3129 : nscoord& aStartDashLength,
3130 : nscoord& aEndDashLength)
3131 : {
3132 0 : aNumDashSpaces = 0;
3133 0 : if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
3134 0 : aStartDashLength = aBorderLength;
3135 0 : aEndDashLength = 0;
3136 : }
3137 : else {
3138 0 : aNumDashSpaces = (aBorderLength - aDashLength)/ (2 * aDashLength); // round down
3139 0 : nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
3140 0 : if (extra > 0) {
3141 0 : nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
3142 0 : aStartDashLength += half;
3143 0 : aEndDashLength += (extra - half);
3144 : }
3145 : }
3146 0 : }
3147 :
3148 : void
3149 0 : nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext,
3150 : PRUint8 aBorderStyle,
3151 : nscolor aBorderColor,
3152 : const nsStyleBackground* aBGColor,
3153 : const nsRect& aBorder,
3154 : PRInt32 aAppUnitsPerCSSPixel,
3155 : PRUint8 aStartBevelSide,
3156 : nscoord aStartBevelOffset,
3157 : PRUint8 aEndBevelSide,
3158 : nscoord aEndBevelOffset)
3159 : {
3160 0 : aContext.SetColor (aBorderColor);
3161 :
3162 0 : bool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide));
3163 0 : nscoord twipsPerPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel);
3164 0 : PRUint8 ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
3165 :
3166 0 : if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) ||
3167 : (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
3168 : // no beveling for 1 pixel border, dash or dot
3169 0 : aStartBevelOffset = 0;
3170 0 : aEndBevelOffset = 0;
3171 : }
3172 :
3173 0 : gfxContext *ctx = aContext.ThebesContext();
3174 0 : gfxContext::AntialiasMode oldMode = ctx->CurrentAntialiasMode();
3175 0 : ctx->SetAntialiasMode(gfxContext::MODE_ALIASED);
3176 :
3177 0 : switch (aBorderStyle) {
3178 : case NS_STYLE_BORDER_STYLE_NONE:
3179 : case NS_STYLE_BORDER_STYLE_HIDDEN:
3180 : //NS_ASSERTION(false, "style of none or hidden");
3181 0 : break;
3182 : case NS_STYLE_BORDER_STYLE_DOTTED:
3183 : case NS_STYLE_BORDER_STYLE_DASHED:
3184 : {
3185 0 : nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
3186 : // make the dash length proportional to the border thickness
3187 0 : dashLength *= (horizontal) ? aBorder.height : aBorder.width;
3188 : // make the min dash length for the ends 1/2 the dash length
3189 : nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle)
3190 0 : ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength;
3191 0 : minDashLength = NS_MAX(minDashLength, twipsPerPixel);
3192 0 : nscoord numDashSpaces = 0;
3193 0 : nscoord startDashLength = minDashLength;
3194 0 : nscoord endDashLength = minDashLength;
3195 0 : if (horizontal) {
3196 0 : GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
3197 0 : nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
3198 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3199 0 : for (PRInt32 spaceX = 0; spaceX < numDashSpaces; spaceX++) {
3200 0 : rect.x += rect.width + dashLength;
3201 0 : rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength;
3202 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3203 : }
3204 : }
3205 : else {
3206 0 : GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
3207 0 : nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
3208 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3209 0 : for (PRInt32 spaceY = 0; spaceY < numDashSpaces; spaceY++) {
3210 0 : rect.y += rect.height + dashLength;
3211 0 : rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength;
3212 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3213 : }
3214 : }
3215 : }
3216 0 : break;
3217 : case NS_STYLE_BORDER_STYLE_GROOVE:
3218 0 : ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
3219 : case NS_STYLE_BORDER_STYLE_RIDGE:
3220 0 : if ((horizontal && (twipsPerPixel >= aBorder.height)) ||
3221 0 : (!horizontal && (twipsPerPixel >= aBorder.width))) {
3222 : // a one pixel border
3223 : DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset,
3224 0 : aEndBevelSide, aEndBevelOffset);
3225 : }
3226 : else {
3227 : nscoord startBevel = (aStartBevelOffset > 0)
3228 0 : ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, true) : 0;
3229 : nscoord endBevel = (aEndBevelOffset > 0)
3230 0 : ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, true) : 0;
3231 0 : mozilla::css::Side ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT;
3232 : // FIXME: In theory, this should use the visited-dependent
3233 : // background color, but I don't care.
3234 : aContext.SetColor (
3235 0 : MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
3236 0 : nsRect rect(aBorder);
3237 : nscoord half;
3238 0 : if (horizontal) { // top, bottom
3239 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
3240 0 : rect.height = half;
3241 0 : if (NS_SIDE_TOP == aStartBevelSide) {
3242 0 : rect.x += startBevel;
3243 0 : rect.width -= startBevel;
3244 : }
3245 0 : if (NS_SIDE_TOP == aEndBevelSide) {
3246 0 : rect.width -= endBevel;
3247 : }
3248 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3249 0 : startBevel, aEndBevelSide, endBevel);
3250 : }
3251 : else { // left, right
3252 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel);
3253 0 : rect.width = half;
3254 0 : if (NS_SIDE_LEFT == aStartBevelSide) {
3255 0 : rect.y += startBevel;
3256 0 : rect.height -= startBevel;
3257 : }
3258 0 : if (NS_SIDE_LEFT == aEndBevelSide) {
3259 0 : rect.height -= endBevel;
3260 : }
3261 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3262 0 : startBevel, aEndBevelSide, endBevel);
3263 : }
3264 :
3265 0 : rect = aBorder;
3266 0 : ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT;
3267 : // FIXME: In theory, this should use the visited-dependent
3268 : // background color, but I don't care.
3269 : aContext.SetColor (
3270 0 : MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
3271 0 : if (horizontal) {
3272 0 : rect.y = rect.y + half;
3273 0 : rect.height = aBorder.height - half;
3274 0 : if (NS_SIDE_BOTTOM == aStartBevelSide) {
3275 0 : rect.x += startBevel;
3276 0 : rect.width -= startBevel;
3277 : }
3278 0 : if (NS_SIDE_BOTTOM == aEndBevelSide) {
3279 0 : rect.width -= endBevel;
3280 : }
3281 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3282 0 : startBevel, aEndBevelSide, endBevel);
3283 : }
3284 : else {
3285 0 : rect.x = rect.x + half;
3286 0 : rect.width = aBorder.width - half;
3287 0 : if (NS_SIDE_RIGHT == aStartBevelSide) {
3288 0 : rect.y += aStartBevelOffset - startBevel;
3289 0 : rect.height -= startBevel;
3290 : }
3291 0 : if (NS_SIDE_RIGHT == aEndBevelSide) {
3292 0 : rect.height -= endBevel;
3293 : }
3294 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3295 0 : startBevel, aEndBevelSide, endBevel);
3296 : }
3297 : }
3298 0 : break;
3299 : case NS_STYLE_BORDER_STYLE_DOUBLE:
3300 : // We can only do "double" borders if the thickness of the border
3301 : // is more than 2px. Otherwise, we fall through to painting a
3302 : // solid border.
3303 0 : if ((aBorder.width > 2*twipsPerPixel || horizontal) &&
3304 0 : (aBorder.height > 2*twipsPerPixel || !horizontal)) {
3305 : nscoord startBevel = (aStartBevelOffset > 0)
3306 0 : ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0;
3307 : nscoord endBevel = (aEndBevelOffset > 0)
3308 0 : ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0;
3309 0 : if (horizontal) { // top, bottom
3310 0 : nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel);
3311 :
3312 : // draw the top line or rect
3313 0 : nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
3314 0 : if (NS_SIDE_TOP == aStartBevelSide) {
3315 0 : topRect.x += aStartBevelOffset - startBevel;
3316 0 : topRect.width -= aStartBevelOffset - startBevel;
3317 : }
3318 0 : if (NS_SIDE_TOP == aEndBevelSide) {
3319 0 : topRect.width -= aEndBevelOffset - endBevel;
3320 : }
3321 : DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide,
3322 0 : startBevel, aEndBevelSide, endBevel);
3323 :
3324 : // draw the botom line or rect
3325 0 : nscoord heightOffset = aBorder.height - thirdHeight;
3326 0 : nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
3327 0 : if (NS_SIDE_BOTTOM == aStartBevelSide) {
3328 0 : bottomRect.x += aStartBevelOffset - startBevel;
3329 0 : bottomRect.width -= aStartBevelOffset - startBevel;
3330 : }
3331 0 : if (NS_SIDE_BOTTOM == aEndBevelSide) {
3332 0 : bottomRect.width -= aEndBevelOffset - endBevel;
3333 : }
3334 : DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide,
3335 0 : startBevel, aEndBevelSide, endBevel);
3336 : }
3337 : else { // left, right
3338 0 : nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel);
3339 :
3340 0 : nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
3341 0 : if (NS_SIDE_LEFT == aStartBevelSide) {
3342 0 : leftRect.y += aStartBevelOffset - startBevel;
3343 0 : leftRect.height -= aStartBevelOffset - startBevel;
3344 : }
3345 0 : if (NS_SIDE_LEFT == aEndBevelSide) {
3346 0 : leftRect.height -= aEndBevelOffset - endBevel;
3347 : }
3348 : DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide,
3349 0 : startBevel, aEndBevelSide, endBevel);
3350 :
3351 0 : nscoord widthOffset = aBorder.width - thirdWidth;
3352 0 : nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
3353 0 : if (NS_SIDE_RIGHT == aStartBevelSide) {
3354 0 : rightRect.y += aStartBevelOffset - startBevel;
3355 0 : rightRect.height -= aStartBevelOffset - startBevel;
3356 : }
3357 0 : if (NS_SIDE_RIGHT == aEndBevelSide) {
3358 0 : rightRect.height -= aEndBevelOffset - endBevel;
3359 : }
3360 : DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide,
3361 0 : startBevel, aEndBevelSide, endBevel);
3362 : }
3363 0 : break;
3364 : }
3365 : // else fall through to solid
3366 : case NS_STYLE_BORDER_STYLE_SOLID:
3367 : DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide,
3368 0 : aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
3369 0 : break;
3370 : case NS_STYLE_BORDER_STYLE_OUTSET:
3371 : case NS_STYLE_BORDER_STYLE_INSET:
3372 0 : NS_ASSERTION(false, "inset, outset should have been converted to groove, ridge");
3373 0 : break;
3374 : case NS_STYLE_BORDER_STYLE_AUTO:
3375 0 : NS_ASSERTION(false, "Unexpected 'auto' table border");
3376 0 : break;
3377 : }
3378 :
3379 0 : ctx->SetAntialiasMode(oldMode);
3380 0 : }
3381 :
3382 : // End table border-collapsing section
3383 :
3384 : void
3385 0 : nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
3386 : const gfxRect& aDirtyRect,
3387 : const nscolor aColor,
3388 : const gfxPoint& aPt,
3389 : const gfxSize& aLineSize,
3390 : const gfxFloat aAscent,
3391 : const gfxFloat aOffset,
3392 : const PRUint8 aDecoration,
3393 : const PRUint8 aStyle,
3394 : const gfxFloat aDescentLimit)
3395 : {
3396 0 : NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
3397 :
3398 : gfxRect rect =
3399 : GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
3400 0 : aDecoration, aStyle, aDescentLimit);
3401 0 : if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
3402 0 : return;
3403 : }
3404 :
3405 0 : if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
3406 : aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
3407 : aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
3408 0 : NS_ERROR("Invalid decoration value!");
3409 0 : return;
3410 : }
3411 :
3412 0 : gfxFloat lineHeight = NS_MAX(NS_round(aLineSize.height), 1.0);
3413 0 : bool contextIsSaved = false;
3414 :
3415 : gfxFloat oldLineWidth;
3416 0 : nsRefPtr<gfxPattern> oldPattern;
3417 :
3418 0 : switch (aStyle) {
3419 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3420 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
3421 0 : oldLineWidth = aGfxContext->CurrentLineWidth();
3422 0 : oldPattern = aGfxContext->GetPattern();
3423 0 : break;
3424 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
3425 0 : aGfxContext->Save();
3426 0 : contextIsSaved = true;
3427 0 : aGfxContext->Clip(rect);
3428 0 : gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
3429 0 : gfxFloat dash[2] = { dashWidth, dashWidth };
3430 0 : aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
3431 0 : aGfxContext->SetDash(dash, 2, 0.0);
3432 : // We should continue to draw the last dash even if it is not in the rect.
3433 0 : rect.width += dashWidth;
3434 0 : break;
3435 : }
3436 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: {
3437 0 : aGfxContext->Save();
3438 0 : contextIsSaved = true;
3439 0 : aGfxContext->Clip(rect);
3440 0 : gfxFloat dashWidth = lineHeight * DOT_LENGTH;
3441 : gfxFloat dash[2];
3442 0 : if (lineHeight > 2.0) {
3443 0 : dash[0] = 0.0;
3444 0 : dash[1] = dashWidth * 2.0;
3445 0 : aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
3446 : } else {
3447 0 : dash[0] = dashWidth;
3448 0 : dash[1] = dashWidth;
3449 : }
3450 0 : aGfxContext->SetDash(dash, 2, 0.0);
3451 : // We should continue to draw the last dot even if it is not in the rect.
3452 0 : rect.width += dashWidth;
3453 0 : break;
3454 : }
3455 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
3456 0 : aGfxContext->Save();
3457 0 : contextIsSaved = true;
3458 0 : aGfxContext->Clip(rect);
3459 0 : if (lineHeight > 2.0) {
3460 0 : aGfxContext->SetAntialiasMode(gfxContext::MODE_COVERAGE);
3461 : } else {
3462 : // Don't use anti-aliasing here. Because looks like lighter color wavy
3463 : // line at this case. And probably, users don't think the
3464 : // non-anti-aliased wavy line is not pretty.
3465 0 : aGfxContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
3466 : }
3467 0 : break;
3468 : default:
3469 0 : NS_ERROR("Invalid style value!");
3470 : return;
3471 : }
3472 :
3473 : // The y position should be set to the middle of the line.
3474 0 : rect.y += lineHeight / 2;
3475 :
3476 0 : aGfxContext->SetColor(gfxRGBA(aColor));
3477 0 : aGfxContext->SetLineWidth(lineHeight);
3478 0 : switch (aStyle) {
3479 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3480 0 : aGfxContext->NewPath();
3481 0 : aGfxContext->MoveTo(rect.TopLeft());
3482 0 : aGfxContext->LineTo(rect.TopRight());
3483 0 : aGfxContext->Stroke();
3484 0 : break;
3485 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
3486 : /**
3487 : * We are drawing double line as:
3488 : *
3489 : * +-------------------------------------------+
3490 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3491 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3492 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3493 : * | |
3494 : * | |
3495 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3496 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3497 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3498 : * +-------------------------------------------+
3499 : */
3500 0 : aGfxContext->NewPath();
3501 0 : aGfxContext->MoveTo(rect.TopLeft());
3502 0 : aGfxContext->LineTo(rect.TopRight());
3503 0 : rect.height -= lineHeight;
3504 0 : aGfxContext->MoveTo(rect.BottomLeft());
3505 0 : aGfxContext->LineTo(rect.BottomRight());
3506 0 : aGfxContext->Stroke();
3507 0 : break;
3508 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
3509 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
3510 0 : aGfxContext->NewPath();
3511 0 : aGfxContext->MoveTo(rect.TopLeft());
3512 0 : aGfxContext->LineTo(rect.TopRight());
3513 0 : aGfxContext->Stroke();
3514 0 : break;
3515 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
3516 : /**
3517 : * We are drawing wavy line as:
3518 : *
3519 : * P: Path, X: Painted pixel
3520 : *
3521 : * +---------------------------------------+
3522 : * XX|X XXXXXX XXXXXX |
3523 : * PP|PX XPPPPPPX XPPPPPPX | ^
3524 : * XX|XPX XPXXXXXXPX XPXXXXXXPX| |
3525 : * | XPX XPX XPX XPX XP|X |adv
3526 : * | XPXXXXXXPX XPXXXXXXPX X|PX |
3527 : * | XPPPPPPX XPPPPPPX |XPX v
3528 : * | XXXXXX XXXXXX | XX
3529 : * +---------------------------------------+
3530 : * <---><---> ^
3531 : * adv flatLengthAtVertex rightMost
3532 : *
3533 : * 1. Always starts from top-left of the drawing area, however, we need
3534 : * to draw the line from outside of the rect. Because the start
3535 : * point of the line is not good style if we draw from inside it.
3536 : * 2. First, draw horizontal line from outside the rect to top-left of
3537 : * the rect;
3538 : * 3. Goes down to bottom of the area at 45 degrees.
3539 : * 4. Slides to right horizontaly, see |flatLengthAtVertex|.
3540 : * 5. Goes up to top of the area at 45 degrees.
3541 : * 6. Slides to right horizontaly.
3542 : * 7. Repeat from 2 until reached to right-most edge of the area.
3543 : */
3544 :
3545 0 : gfxFloat adv = rect.Height() - lineHeight;
3546 0 : gfxFloat flatLengthAtVertex = NS_MAX((lineHeight - 1.0) * 2.0, 1.0);
3547 :
3548 : // figure out if we can trim whole cycles from the left and right edges
3549 : // of the line, to try and avoid creating an unnecessarily long and
3550 : // complex path
3551 0 : gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
3552 0 : PRInt32 skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
3553 0 : if (skipCycles > 0) {
3554 0 : rect.x += skipCycles * cycleLength;
3555 0 : rect.width -= skipCycles * cycleLength;
3556 : }
3557 :
3558 0 : rect.x += lineHeight / 2.0;
3559 0 : gfxPoint pt(rect.TopLeft());
3560 0 : gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
3561 :
3562 0 : skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
3563 0 : if (skipCycles > 0) {
3564 0 : rightMost -= skipCycles * cycleLength;
3565 : }
3566 :
3567 0 : aGfxContext->NewPath();
3568 :
3569 0 : pt.x -= lineHeight;
3570 0 : aGfxContext->MoveTo(pt); // 1
3571 :
3572 0 : pt.x = rect.X();
3573 0 : aGfxContext->LineTo(pt); // 2
3574 :
3575 0 : bool goDown = true;
3576 0 : PRUint32 iter = 0;
3577 0 : while (pt.x < rightMost) {
3578 0 : if (++iter > 1000) {
3579 : // stroke the current path and start again, to avoid pathological
3580 : // behavior in cairo with huge numbers of path segments
3581 0 : aGfxContext->Stroke();
3582 0 : aGfxContext->NewPath();
3583 0 : aGfxContext->MoveTo(pt);
3584 0 : iter = 0;
3585 : }
3586 0 : pt.x += adv;
3587 0 : pt.y += goDown ? adv : -adv;
3588 :
3589 0 : aGfxContext->LineTo(pt); // 3 and 5
3590 :
3591 0 : pt.x += flatLengthAtVertex;
3592 0 : aGfxContext->LineTo(pt); // 4 and 6
3593 :
3594 0 : goDown = !goDown;
3595 : }
3596 0 : aGfxContext->Stroke();
3597 0 : break;
3598 : }
3599 : default:
3600 0 : NS_ERROR("Invalid style value!");
3601 0 : break;
3602 : }
3603 :
3604 0 : if (contextIsSaved) {
3605 0 : aGfxContext->Restore();
3606 : } else {
3607 0 : aGfxContext->SetPattern(oldPattern);
3608 0 : aGfxContext->SetLineWidth(oldLineWidth);
3609 : }
3610 : }
3611 :
3612 : nsRect
3613 0 : nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
3614 : const gfxSize& aLineSize,
3615 : const gfxFloat aAscent,
3616 : const gfxFloat aOffset,
3617 : const PRUint8 aDecoration,
3618 : const PRUint8 aStyle,
3619 : const gfxFloat aDescentLimit)
3620 : {
3621 0 : NS_ASSERTION(aPresContext, "aPresContext is null");
3622 0 : NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
3623 :
3624 : gfxRect rect =
3625 : GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
3626 0 : aDecoration, aStyle, aDescentLimit);
3627 : // The rect values are already rounded to nearest device pixels.
3628 0 : nsRect r;
3629 0 : r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
3630 0 : r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
3631 0 : r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
3632 0 : r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
3633 : return r;
3634 : }
3635 :
3636 : gfxRect
3637 0 : nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
3638 : const gfxSize& aLineSize,
3639 : const gfxFloat aAscent,
3640 : const gfxFloat aOffset,
3641 : const PRUint8 aDecoration,
3642 : const PRUint8 aStyle,
3643 : const gfxFloat aDescentLimit)
3644 : {
3645 0 : NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
3646 : "Invalid aStyle value");
3647 :
3648 0 : if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
3649 0 : return gfxRect(0, 0, 0, 0);
3650 :
3651 0 : bool canLiftUnderline = aDescentLimit >= 0.0;
3652 :
3653 0 : const gfxFloat left = floor(aPt.x + 0.5),
3654 0 : right = floor(aPt.x + aLineSize.width + 0.5);
3655 0 : gfxRect r(left, 0, right - left, 0);
3656 :
3657 0 : gfxFloat lineHeight = NS_round(aLineSize.height);
3658 0 : lineHeight = NS_MAX(lineHeight, 1.0);
3659 :
3660 0 : gfxFloat ascent = NS_round(aAscent);
3661 0 : gfxFloat descentLimit = floor(aDescentLimit);
3662 :
3663 0 : gfxFloat suggestedMaxRectHeight = NS_MAX(NS_MIN(ascent, descentLimit), 1.0);
3664 0 : r.height = lineHeight;
3665 0 : if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
3666 : /**
3667 : * We will draw double line as:
3668 : *
3669 : * +-------------------------------------------+
3670 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3671 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3672 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3673 : * | | ^
3674 : * | | | gap
3675 : * | | v
3676 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3677 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3678 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3679 : * +-------------------------------------------+
3680 : */
3681 0 : gfxFloat gap = NS_round(lineHeight / 2.0);
3682 0 : gap = NS_MAX(gap, 1.0);
3683 0 : r.height = lineHeight * 2.0 + gap;
3684 0 : if (canLiftUnderline) {
3685 0 : if (r.Height() > suggestedMaxRectHeight) {
3686 : // Don't shrink the line height, because the thickness has some meaning.
3687 : // We can just shrink the gap at this time.
3688 0 : r.height = NS_MAX(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
3689 : }
3690 : }
3691 0 : } else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
3692 : /**
3693 : * We will draw wavy line as:
3694 : *
3695 : * +-------------------------------------------+
3696 : * |XXXXX XXXXXX XXXXXX | ^
3697 : * |XXXXXX XXXXXXXX XXXXXXXX | | lineHeight
3698 : * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
3699 : * | XXX XXX XXX XXX XX|
3700 : * | XXXXXXXXXX XXXXXXXXXX X|
3701 : * | XXXXXXXX XXXXXXXX |
3702 : * | XXXXXX XXXXXX |
3703 : * +-------------------------------------------+
3704 : */
3705 0 : r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
3706 0 : if (canLiftUnderline) {
3707 0 : if (r.Height() > suggestedMaxRectHeight) {
3708 : // Don't shrink the line height even if there is not enough space,
3709 : // because the thickness has some meaning. E.g., the 1px wavy line and
3710 : // 2px wavy line can be used for different meaning in IME selections
3711 : // at same time.
3712 0 : r.height = NS_MAX(suggestedMaxRectHeight, lineHeight * 2.0);
3713 : }
3714 : }
3715 : }
3716 :
3717 0 : gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
3718 0 : gfxFloat offset = 0.0;
3719 0 : switch (aDecoration) {
3720 : case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
3721 0 : offset = aOffset;
3722 0 : if (canLiftUnderline) {
3723 0 : if (descentLimit < -offset + r.Height()) {
3724 : // If we can ignore the offset and the decoration line is overflowing,
3725 : // we should align the bottom edge of the decoration line rect if it's
3726 : // possible. Otherwise, we should lift up the top edge of the rect as
3727 : // far as possible.
3728 0 : gfxFloat offsetBottomAligned = -descentLimit + r.Height();
3729 0 : gfxFloat offsetTopAligned = 0.0;
3730 0 : offset = NS_MIN(offsetBottomAligned, offsetTopAligned);
3731 : }
3732 : }
3733 0 : break;
3734 : case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
3735 0 : offset = aOffset - lineHeight + r.Height();
3736 0 : break;
3737 : case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
3738 0 : gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
3739 0 : extra = NS_MAX(extra, lineHeight);
3740 0 : offset = aOffset - lineHeight + extra;
3741 0 : break;
3742 : }
3743 : default:
3744 0 : NS_ERROR("Invalid decoration value!");
3745 : }
3746 0 : r.y = baseline - floor(offset + 0.5);
3747 0 : return r;
3748 : }
3749 :
3750 : // ------------------
3751 : // ImageRenderer
3752 : // ------------------
3753 0 : ImageRenderer::ImageRenderer(nsIFrame* aForFrame,
3754 : const nsStyleImage* aImage,
3755 : PRUint32 aFlags)
3756 : : mForFrame(aForFrame)
3757 : , mImage(aImage)
3758 0 : , mType(aImage->GetType())
3759 : , mImageContainer(nsnull)
3760 : , mGradientData(nsnull)
3761 : , mPaintServerFrame(nsnull)
3762 : , mIsReady(false)
3763 : , mSize(0, 0)
3764 0 : , mFlags(aFlags)
3765 : {
3766 0 : }
3767 :
3768 0 : ImageRenderer::~ImageRenderer()
3769 : {
3770 0 : }
3771 :
3772 : bool
3773 0 : ImageRenderer::PrepareImage()
3774 : {
3775 0 : if (mImage->IsEmpty() || !mImage->IsComplete()) {
3776 : // Make sure the image is actually decoding
3777 0 : mImage->RequestDecode();
3778 :
3779 : // We can not prepare the image for rendering if it is not fully loaded.
3780 : //
3781 : // Special case: If we requested a sync decode and we have an image, push
3782 : // on through
3783 0 : nsCOMPtr<imgIContainer> img;
3784 0 : if (!((mFlags & FLAG_SYNC_DECODE_IMAGES) &&
3785 : (mType == eStyleImageType_Image) &&
3786 0 : (NS_SUCCEEDED(mImage->GetImageData()->GetImage(getter_AddRefs(img))) && img)))
3787 0 : return false;
3788 : }
3789 :
3790 0 : switch (mType) {
3791 : case eStyleImageType_Image:
3792 : {
3793 0 : nsCOMPtr<imgIContainer> srcImage;
3794 0 : mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
3795 0 : NS_ABORT_IF_FALSE(srcImage, "If srcImage is null, mImage->IsComplete() "
3796 : "should have returned false");
3797 :
3798 0 : if (!mImage->GetCropRect()) {
3799 0 : mImageContainer.swap(srcImage);
3800 : } else {
3801 0 : nsIntRect actualCropRect;
3802 : bool isEntireImage;
3803 : bool success =
3804 0 : mImage->ComputeActualCropRect(actualCropRect, &isEntireImage);
3805 0 : NS_ASSERTION(success, "ComputeActualCropRect() should not fail here");
3806 0 : if (!success || actualCropRect.IsEmpty()) {
3807 : // The cropped image has zero size
3808 0 : return false;
3809 : }
3810 0 : if (isEntireImage) {
3811 : // The cropped image is identical to the source image
3812 0 : mImageContainer.swap(srcImage);
3813 : } else {
3814 0 : nsCOMPtr<imgIContainer> subImage;
3815 : PRUint32 aExtractFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
3816 : ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
3817 0 : : (PRUint32) imgIContainer::FLAG_NONE;
3818 0 : nsresult rv = srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,
3819 : actualCropRect, aExtractFlags,
3820 0 : getter_AddRefs(subImage));
3821 0 : if (NS_FAILED(rv)) {
3822 : NS_WARNING("The cropped image contains no pixels to draw; "
3823 0 : "maybe the crop rect is outside the image frame rect");
3824 0 : return false;
3825 : }
3826 0 : mImageContainer.swap(subImage);
3827 : }
3828 : }
3829 0 : mIsReady = true;
3830 0 : break;
3831 : }
3832 : case eStyleImageType_Gradient:
3833 0 : mGradientData = mImage->GetGradientData();
3834 0 : mIsReady = true;
3835 0 : break;
3836 : case eStyleImageType_Element:
3837 : {
3838 : nsAutoString elementId =
3839 0 : NS_LITERAL_STRING("#") + nsDependentString(mImage->GetElementId());
3840 0 : nsCOMPtr<nsIURI> targetURI;
3841 0 : nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
3842 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), elementId,
3843 0 : mForFrame->GetContent()->GetCurrentDoc(), base);
3844 : nsSVGPaintingProperty* property = nsSVGEffects::GetPaintingPropertyForURI(
3845 0 : targetURI, mForFrame->GetFirstContinuation(),
3846 0 : nsSVGEffects::BackgroundImageProperty());
3847 0 : if (!property)
3848 0 : return false;
3849 0 : mPaintServerFrame = property->GetReferencedFrame();
3850 :
3851 : // If the referenced element doesn't have a frame we might still be able
3852 : // to paint it if it's an <img>, <canvas>, or <video> element.
3853 0 : if (!mPaintServerFrame) {
3854 : mImageElementSurface =
3855 0 : nsLayoutUtils::SurfaceFromElement(property->GetReferencedElement());
3856 0 : if (!mImageElementSurface.mSurface)
3857 0 : return false;
3858 : }
3859 0 : mIsReady = true;
3860 0 : break;
3861 : }
3862 : case eStyleImageType_Null:
3863 : default:
3864 0 : break;
3865 : }
3866 :
3867 0 : return mIsReady;
3868 : }
3869 :
3870 : enum FitType { CONTAIN, COVER };
3871 :
3872 : static nsSize
3873 0 : ComputeContainCoverSizeFromRatio(const nsSize& aBgPositioningArea,
3874 : const nsSize& aRatio, FitType fitType)
3875 : {
3876 0 : NS_ABORT_IF_FALSE(aRatio.width > 0, "width division by zero");
3877 0 : NS_ABORT_IF_FALSE(aRatio.height > 0, "height division by zero");
3878 :
3879 0 : float scaleX = double(aBgPositioningArea.width) / aRatio.width;
3880 0 : float scaleY = double(aBgPositioningArea.height) / aRatio.height;
3881 0 : nsSize size;
3882 0 : if ((fitType == CONTAIN) == (scaleX < scaleY)) {
3883 0 : size.width = aBgPositioningArea.width;
3884 0 : size.height = NSCoordSaturatingNonnegativeMultiply(aRatio.height, scaleX);
3885 : } else {
3886 0 : size.width = NSCoordSaturatingNonnegativeMultiply(aRatio.width, scaleY);
3887 0 : size.height = aBgPositioningArea.height;
3888 : }
3889 : return size;
3890 : }
3891 :
3892 : void
3893 0 : ImageRenderer::ComputeUnscaledDimensions(const nsSize& aBgPositioningArea,
3894 : nscoord& aUnscaledWidth, bool& aHaveWidth,
3895 : nscoord& aUnscaledHeight, bool& aHaveHeight,
3896 : nsSize& aRatio)
3897 : {
3898 0 : NS_ASSERTION(mIsReady, "Ensure PrepareImage() has returned true "
3899 : "before calling me");
3900 :
3901 0 : switch (mType) {
3902 : case eStyleImageType_Image:
3903 : {
3904 0 : nsIntSize imageIntSize;
3905 : nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, imageIntSize,
3906 0 : aRatio, aHaveWidth, aHaveHeight);
3907 0 : if (aHaveWidth) {
3908 0 : aUnscaledWidth = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);
3909 : }
3910 0 : if (aHaveHeight) {
3911 0 : aUnscaledHeight = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);
3912 : }
3913 0 : return;
3914 : }
3915 : case eStyleImageType_Gradient:
3916 : // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
3917 : // intrinsic dimensions.
3918 0 : aHaveWidth = aHaveHeight = false;
3919 0 : aRatio = nsSize(0, 0);
3920 0 : return;
3921 : case eStyleImageType_Element:
3922 : {
3923 : // XXX element() should have the width/height of the referenced element,
3924 : // and that element's ratio, if it matches. If it doesn't match, it
3925 : // should have no width/height or ratio. See element() in CSS3:
3926 : // <http://dev.w3.org/csswg/css3-images/#element-reference>.
3927 : // Make sure to change nsStyleBackground::Size::DependsOnFrameSize
3928 : // when fixing this!
3929 0 : aHaveWidth = aHaveHeight = true;
3930 0 : nsSize size;
3931 0 : if (mPaintServerFrame) {
3932 0 : if (mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)) {
3933 0 : size = aBgPositioningArea;
3934 : } else {
3935 : // The intrinsic image size for a generic nsIFrame paint server is
3936 : // the frame's bbox size rounded to device pixels.
3937 : PRInt32 appUnitsPerDevPixel =
3938 0 : mForFrame->PresContext()->AppUnitsPerDevPixel();
3939 : nsRect rect =
3940 0 : nsSVGIntegrationUtils::GetNonSVGUserSpace(mPaintServerFrame);
3941 0 : nsRect rectSize = rect - rect.TopLeft();
3942 0 : nsIntRect rounded = rectSize.ToNearestPixels(appUnitsPerDevPixel);
3943 0 : size = rounded.ToAppUnits(appUnitsPerDevPixel).Size();
3944 : }
3945 : } else {
3946 0 : NS_ASSERTION(mImageElementSurface.mSurface, "Surface should be ready.");
3947 0 : gfxIntSize surfaceSize = mImageElementSurface.mSize;
3948 0 : size.width = nsPresContext::CSSPixelsToAppUnits(surfaceSize.width);
3949 0 : size.height = nsPresContext::CSSPixelsToAppUnits(surfaceSize.height);
3950 : }
3951 0 : aRatio = size;
3952 0 : aUnscaledWidth = size.width;
3953 0 : aUnscaledHeight = size.height;
3954 0 : return;
3955 : }
3956 : case eStyleImageType_Null:
3957 : default:
3958 0 : aHaveWidth = aHaveHeight = true;
3959 0 : aUnscaledWidth = aUnscaledHeight = 0;
3960 0 : aRatio = nsSize(0, 0);
3961 0 : return;
3962 : }
3963 : }
3964 :
3965 : nsSize
3966 0 : ImageRenderer::ComputeDrawnSize(const nsStyleBackground::Size& aLayerSize,
3967 : const nsSize& aBgPositioningArea,
3968 : nscoord aUnscaledWidth, bool aHaveWidth,
3969 : nscoord aUnscaledHeight, bool aHaveHeight,
3970 : const nsSize& aIntrinsicRatio)
3971 : {
3972 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.width >= 0,
3973 : "image ratio with nonsense width");
3974 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.height >= 0,
3975 : "image ratio with nonsense height");
3976 :
3977 : // Bail early if the image is empty.
3978 0 : if ((aHaveWidth && aUnscaledWidth <= 0) ||
3979 : (aHaveHeight && aUnscaledHeight <= 0)) {
3980 0 : return nsSize(0, 0);
3981 : }
3982 :
3983 : // If the image has an intrinsic ratio but either component of it is zero,
3984 : // then the image would eventually scale to nothingness, so again we can bail.
3985 0 : bool haveRatio = aIntrinsicRatio != nsSize(0, 0);
3986 0 : if (haveRatio &&
3987 : (aIntrinsicRatio.width == 0 || aIntrinsicRatio.height == 0)) {
3988 0 : return nsSize(0, 0);
3989 : }
3990 :
3991 : // Easiest case: background-size completely specifies the size.
3992 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eLengthPercentage &&
3993 : aLayerSize.mHeightType == nsStyleBackground::Size::eLengthPercentage) {
3994 : return nsSize(aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea),
3995 0 : aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
3996 : }
3997 :
3998 : // The harder cases: contain/cover.
3999 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eContain ||
4000 : aLayerSize.mWidthType == nsStyleBackground::Size::eCover) {
4001 : FitType fitType = aLayerSize.mWidthType == nsStyleBackground::Size::eCover
4002 : ? COVER
4003 0 : : CONTAIN;
4004 0 : if (!haveRatio) {
4005 : // If we don't have an intrinsic ratio, then proportionally scaling to
4006 : // either largest-fitting or smallest-covering size means scaling to the
4007 : // background positioning area's size.
4008 0 : return aBgPositioningArea;
4009 : }
4010 :
4011 : return ComputeContainCoverSizeFromRatio(aBgPositioningArea, aIntrinsicRatio,
4012 0 : fitType);
4013 : }
4014 :
4015 : // Harder case: all-auto.
4016 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eAuto &&
4017 : aLayerSize.mHeightType == nsStyleBackground::Size::eAuto) {
4018 : // If the image has all its dimensions, we're done.
4019 0 : if (aHaveWidth && aHaveHeight)
4020 0 : return nsSize(aUnscaledWidth, aUnscaledHeight);
4021 :
4022 : // If the image has no dimensions, treat it as if for contain.
4023 0 : if (!aHaveWidth && !aHaveHeight) {
4024 0 : if (!haveRatio) {
4025 : // As above, max-contain without a ratio means the whole area.
4026 0 : return aBgPositioningArea;
4027 : }
4028 :
4029 : // Otherwise determine size using the intrinsic ratio.
4030 : return ComputeContainCoverSizeFromRatio(aBgPositioningArea,
4031 0 : aIntrinsicRatio, CONTAIN);
4032 : }
4033 :
4034 0 : NS_ABORT_IF_FALSE(aHaveWidth != aHaveHeight, "logic error");
4035 :
4036 0 : if (haveRatio) {
4037 : // Resolve missing dimensions using the intrinsic ratio.
4038 0 : nsSize size;
4039 0 : if (aHaveWidth) {
4040 0 : size.width = aUnscaledWidth;
4041 : size.height =
4042 : NSCoordSaturatingNonnegativeMultiply(size.width,
4043 : double(aIntrinsicRatio.height) /
4044 0 : aIntrinsicRatio.width);
4045 : } else {
4046 0 : size.height = aUnscaledHeight;
4047 : size.width =
4048 : NSCoordSaturatingNonnegativeMultiply(size.height,
4049 : double(aIntrinsicRatio.width) /
4050 0 : aIntrinsicRatio.height);
4051 : }
4052 :
4053 0 : return size;
4054 : }
4055 :
4056 : // Without a ratio we must fall back to the relevant dimension of the
4057 : // area to determine the missing dimension.
4058 : return aHaveWidth ? nsSize(aUnscaledWidth, aBgPositioningArea.height)
4059 0 : : nsSize(aBgPositioningArea.width, aUnscaledHeight);
4060 : }
4061 :
4062 : // Hardest case: only one auto. Prepare to negotiate amongst intrinsic
4063 : // dimensions, intrinsic ratio, *and* a specific background-size!
4064 0 : NS_ABORT_IF_FALSE((aLayerSize.mWidthType == nsStyleBackground::Size::eAuto) !=
4065 : (aLayerSize.mHeightType == nsStyleBackground::Size::eAuto),
4066 : "logic error");
4067 :
4068 0 : bool isAutoWidth = aLayerSize.mWidthType == nsStyleBackground::Size::eAuto;
4069 :
4070 0 : if (haveRatio) {
4071 : // Use the specified dimension, and compute the other from the ratio.
4072 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.width > 0,
4073 : "ratio width out of sync with width?");
4074 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.height > 0,
4075 : "ratio height out of sync with width?");
4076 0 : nsSize size;
4077 0 : if (isAutoWidth) {
4078 0 : size.height = aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea);
4079 : size.width =
4080 : NSCoordSaturatingNonnegativeMultiply(size.height,
4081 : double(aIntrinsicRatio.width) /
4082 0 : aIntrinsicRatio.height);
4083 : } else {
4084 0 : size.width = aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea);
4085 : size.height =
4086 : NSCoordSaturatingNonnegativeMultiply(size.width,
4087 : double(aIntrinsicRatio.height) /
4088 0 : aIntrinsicRatio.width);
4089 : }
4090 :
4091 0 : return size;
4092 : }
4093 :
4094 0 : NS_ABORT_IF_FALSE(!(aHaveWidth && aHaveHeight),
4095 : "if we have width and height, we must have had a ratio");
4096 :
4097 : // We have a specified dimension and an auto dimension, with no ratio to
4098 : // preserve. A specified dimension trumps all, so use that. For the other
4099 : // dimension, resolve auto to the intrinsic dimension (if present) or to 100%.
4100 0 : nsSize size;
4101 0 : if (isAutoWidth) {
4102 0 : size.width = aHaveWidth ? aUnscaledWidth : aBgPositioningArea.width;
4103 0 : size.height = aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea);
4104 : } else {
4105 0 : size.width = aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea);
4106 0 : size.height = aHaveHeight ? aUnscaledHeight : aBgPositioningArea.height;
4107 : }
4108 :
4109 0 : return size;
4110 : }
4111 :
4112 : /*
4113 : * The size returned by this method differs from the value of mSize, which this
4114 : * method also computes, in that mSize is the image's "preferred" size for this
4115 : * particular rendering, while the size returned here is the actual rendered
4116 : * size after accounting for background-size. The preferred size is most often
4117 : * the image's intrinsic dimensions. But for images with incomplete intrinsic
4118 : * dimensions, the preferred size varies, depending on the background
4119 : * positioning area, the specified background-size, and the intrinsic ratio and
4120 : * dimensions of the image (if it has them).
4121 : *
4122 : * This distinction is necessary because the components of a vector image are
4123 : * specified with respect to its preferred size for a rendering situation, not
4124 : * to its actual rendered size after background-size is applied. For example,
4125 : * consider a 4px wide vector image with no height which contains a left-aligned
4126 : * 2px wide black rectangle with height 100%. If the background-size width is
4127 : * auto (or 4px), the vector image will render 4px wide, and the black rectangle
4128 : * will be 2px wide. If the background-size width is 8px, the vector image will
4129 : * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
4130 : * In both cases mSize.width will be 4px; but in the first case the returned
4131 : * width will be 4px, while in the second case the returned width will be 8px.
4132 : */
4133 : nsSize
4134 0 : ImageRenderer::ComputeSize(const nsStyleBackground::Size& aLayerSize,
4135 : const nsSize& aBgPositioningArea)
4136 : {
4137 : bool haveWidth, haveHeight;
4138 0 : nsSize ratio;
4139 : nscoord unscaledWidth, unscaledHeight;
4140 : ComputeUnscaledDimensions(aBgPositioningArea,
4141 : unscaledWidth, haveWidth,
4142 : unscaledHeight, haveHeight,
4143 0 : ratio);
4144 : nsSize drawnSize = ComputeDrawnSize(aLayerSize, aBgPositioningArea,
4145 : unscaledWidth, haveWidth,
4146 : unscaledHeight, haveHeight,
4147 0 : ratio);
4148 0 : mSize.width = haveWidth ? unscaledWidth : drawnSize.width;
4149 0 : mSize.height = haveHeight ? unscaledHeight : drawnSize.height;
4150 : return drawnSize;
4151 : }
4152 :
4153 : void
4154 0 : ImageRenderer::Draw(nsPresContext* aPresContext,
4155 : nsRenderingContext& aRenderingContext,
4156 : const nsRect& aDest,
4157 : const nsRect& aFill,
4158 : const nsPoint& aAnchor,
4159 : const nsRect& aDirty)
4160 : {
4161 0 : if (!mIsReady) {
4162 0 : NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
4163 0 : return;
4164 : }
4165 :
4166 0 : if (aDest.IsEmpty() || aFill.IsEmpty() ||
4167 : mSize.width <= 0 || mSize.height <= 0)
4168 0 : return;
4169 :
4170 : gfxPattern::GraphicsFilter graphicsFilter =
4171 0 : nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
4172 :
4173 0 : switch (mType) {
4174 : case eStyleImageType_Image:
4175 : {
4176 : PRUint32 drawFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
4177 : ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
4178 0 : : (PRUint32) imgIContainer::FLAG_NONE;
4179 : nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer,
4180 : nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
4181 : nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
4182 : graphicsFilter,
4183 0 : aDest, aFill, aAnchor, aDirty, drawFlags);
4184 0 : break;
4185 : }
4186 : case eStyleImageType_Gradient:
4187 : nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
4188 0 : mGradientData, aDirty, aDest, aFill);
4189 0 : break;
4190 : case eStyleImageType_Element:
4191 0 : if (mPaintServerFrame) {
4192 : nsSVGIntegrationUtils::DrawPaintServer(
4193 : &aRenderingContext, mForFrame, mPaintServerFrame, graphicsFilter,
4194 0 : aDest, aFill, aAnchor, aDirty, mSize);
4195 : } else {
4196 0 : NS_ASSERTION(mImageElementSurface.mSurface, "Surface should be ready.");
4197 : nsRefPtr<gfxDrawable> surfaceDrawable =
4198 : new gfxSurfaceDrawable(mImageElementSurface.mSurface,
4199 0 : mImageElementSurface.mSize);
4200 : nsLayoutUtils::DrawPixelSnapped(
4201 : &aRenderingContext, surfaceDrawable, graphicsFilter,
4202 0 : aDest, aFill, aAnchor, aDirty);
4203 : }
4204 0 : break;
4205 : case eStyleImageType_Null:
4206 : default:
4207 0 : break;
4208 : }
4209 : }
4210 :
4211 : #define MAX_BLUR_RADIUS 300
4212 : #define MAX_SPREAD_RADIUS 50
4213 :
4214 : static inline gfxIntSize
4215 0 : ComputeBlurRadius(nscoord aBlurRadius, PRInt32 aAppUnitsPerDevPixel)
4216 : {
4217 : // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
4218 : // standard deviation of the blur should be half the given blur value.
4219 : gfxFloat blurStdDev =
4220 : NS_MIN(gfxFloat(aBlurRadius) / gfxFloat(aAppUnitsPerDevPixel),
4221 0 : gfxFloat(MAX_BLUR_RADIUS))
4222 0 : / 2.0;
4223 : return
4224 0 : gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(blurStdDev, blurStdDev));
4225 : }
4226 :
4227 : // -----
4228 : // nsContextBoxBlur
4229 : // -----
4230 : gfxContext*
4231 0 : nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
4232 : nscoord aBlurRadius,
4233 : PRInt32 aAppUnitsPerDevPixel,
4234 : gfxContext* aDestinationCtx,
4235 : const nsRect& aDirtyRect,
4236 : const gfxRect* aSkipRect,
4237 : PRUint32 aFlags)
4238 : {
4239 0 : if (aRect.IsEmpty()) {
4240 0 : mContext = nsnull;
4241 0 : return nsnull;
4242 : }
4243 :
4244 0 : gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
4245 : PRInt32 spreadRadius = NS_MIN(PRInt32(aSpreadRadius / aAppUnitsPerDevPixel),
4246 0 : PRInt32(MAX_SPREAD_RADIUS));
4247 0 : mDestinationCtx = aDestinationCtx;
4248 :
4249 : // If not blurring, draw directly onto the destination device
4250 0 : if (blurRadius.width <= 0 && blurRadius.height <= 0 && spreadRadius <= 0 &&
4251 0 : !(aFlags & FORCE_MASK)) {
4252 0 : mContext = aDestinationCtx;
4253 0 : return mContext;
4254 : }
4255 :
4256 : // Convert from app units to device pixels
4257 0 : gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
4258 :
4259 : gfxRect dirtyRect =
4260 0 : nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
4261 0 : dirtyRect.RoundOut();
4262 :
4263 : // Create the temporary surface for blurring
4264 : mContext = blur.Init(rect, gfxIntSize(spreadRadius, spreadRadius),
4265 0 : blurRadius, &dirtyRect, aSkipRect);
4266 0 : return mContext;
4267 : }
4268 :
4269 : void
4270 0 : nsContextBoxBlur::DoPaint()
4271 : {
4272 0 : if (mContext == mDestinationCtx)
4273 0 : return;
4274 :
4275 0 : blur.Paint(mDestinationCtx);
4276 : }
4277 :
4278 : gfxContext*
4279 0 : nsContextBoxBlur::GetContext()
4280 : {
4281 0 : return mContext;
4282 : }
4283 :
4284 : /* static */ nsMargin
4285 0 : nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
4286 : PRInt32 aAppUnitsPerDevPixel)
4287 : {
4288 0 : gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
4289 :
4290 0 : nsMargin result;
4291 0 : result.top = blurRadius.height * aAppUnitsPerDevPixel;
4292 0 : result.right = blurRadius.width * aAppUnitsPerDevPixel;
4293 0 : result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
4294 0 : result.left = blurRadius.width * aAppUnitsPerDevPixel;
4295 : return result;
4296 : }
|