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 1365 : InlineBackgroundData()
171 1365 : : mFrame(nsnull), mBlockFrame(nsnull)
172 : {
173 1365 : }
174 :
175 1364 : ~InlineBackgroundData()
176 1364 : {
177 1364 : }
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 1365 : void nsCSSRendering::Init()
412 : {
413 1365 : NS_ASSERTION(!gInlineBGData, "Init called twice");
414 1365 : gInlineBGData = new InlineBackgroundData();
415 1365 : }
416 :
417 : // Clean up any global variables used by nsCSSRendering.
418 1364 : void nsCSSRendering::Shutdown()
419 : {
420 1364 : delete gInlineBGData;
421 1364 : gInlineBGData = nsnull;
422 1364 : }
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.
2739 :
2740 0 : nsCOMPtr<imgIContainer> imgContainer;
2741 0 : req->GetImage(getter_AddRefs(imgContainer));
2742 0 : NS_ASSERTION(imgContainer, "no image to draw");
2743 :
2744 0 : nsIntSize imageSize;
2745 0 : if (NS_FAILED(imgContainer->GetWidth(&imageSize.width))) {
2746 : imageSize.width =
2747 0 : nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.width);
2748 : }
2749 0 : if (NS_FAILED(imgContainer->GetHeight(&imageSize.height))) {
2750 : imageSize.height =
2751 0 : nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.height);
2752 : }
2753 :
2754 : // Determine the border image area, which by default corresponds to the
2755 : // border box but can be modified by 'border-image-outset'.
2756 0 : nsRect borderImgArea(aBorderArea);
2757 0 : borderImgArea.Inflate(aStyleBorder.GetImageOutset());
2758 :
2759 : // Compute the used values of 'border-image-slice' and 'border-image-width';
2760 : // we do them together because the latter can depend on the former.
2761 0 : nsIntMargin slice;
2762 0 : nsMargin border;
2763 0 : NS_FOR_CSS_SIDES(s) {
2764 0 : nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s);
2765 : PRInt32 imgDimension = NS_SIDE_IS_VERTICAL(s)
2766 0 : ? imageSize.width : imageSize.height;
2767 : nscoord borderDimension = NS_SIDE_IS_VERTICAL(s)
2768 0 : ? borderImgArea.width : borderImgArea.height;
2769 : double value;
2770 0 : switch (coord.GetUnit()) {
2771 : case eStyleUnit_Percent:
2772 0 : value = coord.GetPercentValue() * imgDimension;
2773 0 : break;
2774 : case eStyleUnit_Factor:
2775 0 : value = coord.GetFactorValue();
2776 0 : break;
2777 : default:
2778 0 : NS_NOTREACHED("unexpected CSS unit for image slice");
2779 0 : value = 0;
2780 0 : break;
2781 : }
2782 0 : if (value < 0)
2783 0 : value = 0;
2784 0 : if (value > imgDimension)
2785 0 : value = imgDimension;
2786 0 : slice.Side(s) = NS_lround(value);
2787 :
2788 0 : nsMargin borderWidths(aStyleBorder.GetActualBorder());
2789 0 : coord = aStyleBorder.mBorderImageWidth.Get(s);
2790 0 : switch (coord.GetUnit()) {
2791 : case eStyleUnit_Coord: // absolute dimension
2792 0 : value = coord.GetCoordValue();
2793 0 : break;
2794 : case eStyleUnit_Percent:
2795 0 : value = coord.GetPercentValue() * borderDimension;
2796 0 : break;
2797 : case eStyleUnit_Factor:
2798 0 : value = coord.GetFactorValue() * borderWidths.Side(s);
2799 0 : break;
2800 : case eStyleUnit_Auto: // same as the slice value, in CSS pixels
2801 0 : value = nsPresContext::CSSPixelsToAppUnits(slice.Side(s));
2802 0 : break;
2803 : default:
2804 0 : NS_NOTREACHED("unexpected CSS unit for border image area division");
2805 0 : value = 0;
2806 0 : break;
2807 : }
2808 0 : border.Side(s) = NS_lround(value);
2809 : }
2810 :
2811 : // "If two opposite border-image-width offsets are large enough that they
2812 : // overlap, their used values are proportionately reduced until they no
2813 : // longer overlap."
2814 : double scaleX = border.left + border.right > borderImgArea.width
2815 : ? borderImgArea.width / double(border.left + border.right)
2816 0 : : 1.0;
2817 : double scaleY = border.top + border.bottom > borderImgArea.height
2818 : ? borderImgArea.height / double(border.top + border.bottom)
2819 0 : : 1.0;
2820 0 : double scale = NS_MIN(scaleX, scaleY);
2821 0 : if (scale < 1.0) {
2822 0 : border.left *= scale;
2823 0 : border.right *= scale;
2824 0 : border.top *= scale;
2825 0 : border.bottom *= scale;
2826 0 : NS_ASSERTION(border.left + border.right <= borderImgArea.width &&
2827 : border.top + border.bottom <= borderImgArea.height,
2828 : "rounding error in width reduction???");
2829 : }
2830 :
2831 : // These helper tables recharacterize the 'slice' and 'width' margins
2832 : // in a more convenient form: they are the x/y/width/height coords
2833 : // required for various bands of the border, and they have been transformed
2834 : // to be relative to the innerRect (for 'slice') or the page (for 'border').
2835 : enum {
2836 : LEFT, MIDDLE, RIGHT,
2837 : TOP = LEFT, BOTTOM = RIGHT
2838 : };
2839 : const nscoord borderX[3] = {
2840 : borderImgArea.x + 0,
2841 : borderImgArea.x + border.left,
2842 : borderImgArea.x + borderImgArea.width - border.right,
2843 0 : };
2844 : const nscoord borderY[3] = {
2845 : borderImgArea.y + 0,
2846 : borderImgArea.y + border.top,
2847 : borderImgArea.y + borderImgArea.height - border.bottom,
2848 0 : };
2849 : const nscoord borderWidth[3] = {
2850 : border.left,
2851 : borderImgArea.width - border.left - border.right,
2852 : border.right,
2853 0 : };
2854 : const nscoord borderHeight[3] = {
2855 : border.top,
2856 : borderImgArea.height - border.top - border.bottom,
2857 : border.bottom,
2858 0 : };
2859 : const PRInt32 sliceX[3] = {
2860 : 0,
2861 : slice.left,
2862 : imageSize.width - slice.right,
2863 0 : };
2864 : const PRInt32 sliceY[3] = {
2865 : 0,
2866 : slice.top,
2867 : imageSize.height - slice.bottom,
2868 0 : };
2869 : const PRInt32 sliceWidth[3] = {
2870 : slice.left,
2871 : PR_MAX(imageSize.width - slice.left - slice.right, 0),
2872 : slice.right,
2873 0 : };
2874 : const PRInt32 sliceHeight[3] = {
2875 : slice.top,
2876 : PR_MAX(imageSize.height - slice.top - slice.bottom, 0),
2877 : slice.bottom,
2878 0 : };
2879 :
2880 : // In all the 'factor' calculations below, 'border' measurements are
2881 : // in app units but 'slice' measurements are in image/CSS pixels, so
2882 : // the factor corresponding to no additional scaling is
2883 : // CSSPixelsToAppUnits(1), not simply 1.
2884 0 : for (int i = LEFT; i <= RIGHT; i++) {
2885 0 : for (int j = TOP; j <= BOTTOM; j++) {
2886 0 : nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
2887 0 : nsIntRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
2888 :
2889 : PRUint8 fillStyleH, fillStyleV;
2890 0 : nsSize unitSize;
2891 :
2892 0 : if (i == MIDDLE && j == MIDDLE) {
2893 : // Discard the middle portion unless set to fill.
2894 0 : if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL ==
2895 : aStyleBorder.mBorderImageFill) {
2896 0 : continue;
2897 : }
2898 :
2899 0 : NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL ==
2900 : aStyleBorder.mBorderImageFill,
2901 : "Unexpected border image fill");
2902 :
2903 : // css-background:
2904 : // The middle image's width is scaled by the same factor as the
2905 : // top image unless that factor is zero or infinity, in which
2906 : // case the scaling factor of the bottom is substituted, and
2907 : // failing that, the width is not scaled. The height of the
2908 : // middle image is scaled by the same factor as the left image
2909 : // unless that factor is zero or infinity, in which case the
2910 : // scaling factor of the right image is substituted, and failing
2911 : // that, the height is not scaled.
2912 : gfxFloat hFactor, vFactor;
2913 :
2914 0 : if (0 < border.left && 0 < slice.left)
2915 0 : vFactor = gfxFloat(border.left)/slice.left;
2916 0 : else if (0 < border.right && 0 < slice.right)
2917 0 : vFactor = gfxFloat(border.right)/slice.right;
2918 : else
2919 0 : vFactor = nsPresContext::CSSPixelsToAppUnits(1);
2920 :
2921 0 : if (0 < border.top && 0 < slice.top)
2922 0 : hFactor = gfxFloat(border.top)/slice.top;
2923 0 : else if (0 < border.bottom && 0 < slice.bottom)
2924 0 : hFactor = gfxFloat(border.bottom)/slice.bottom;
2925 : else
2926 0 : hFactor = nsPresContext::CSSPixelsToAppUnits(1);
2927 :
2928 0 : unitSize.width = sliceWidth[i]*hFactor;
2929 0 : unitSize.height = sliceHeight[j]*vFactor;
2930 0 : fillStyleH = aStyleBorder.mBorderImageRepeatH;
2931 0 : fillStyleV = aStyleBorder.mBorderImageRepeatV;
2932 :
2933 0 : } else if (i == MIDDLE) { // top, bottom
2934 : // Sides are always stretched to the thickness of their border,
2935 : // and stretched proportionately on the other axis.
2936 : gfxFloat factor;
2937 0 : if (0 < borderHeight[j] && 0 < sliceHeight[j])
2938 0 : factor = gfxFloat(borderHeight[j])/sliceHeight[j];
2939 : else
2940 0 : factor = nsPresContext::CSSPixelsToAppUnits(1);
2941 :
2942 0 : unitSize.width = sliceWidth[i]*factor;
2943 0 : unitSize.height = borderHeight[j];
2944 0 : fillStyleH = aStyleBorder.mBorderImageRepeatH;
2945 0 : fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
2946 :
2947 0 : } else if (j == MIDDLE) { // left, right
2948 : gfxFloat factor;
2949 0 : if (0 < borderWidth[i] && 0 < sliceWidth[i])
2950 0 : factor = gfxFloat(borderWidth[i])/sliceWidth[i];
2951 : else
2952 0 : factor = nsPresContext::CSSPixelsToAppUnits(1);
2953 :
2954 0 : unitSize.width = borderWidth[i];
2955 0 : unitSize.height = sliceHeight[j]*factor;
2956 0 : fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
2957 0 : fillStyleV = aStyleBorder.mBorderImageRepeatV;
2958 :
2959 : } else {
2960 : // Corners are always stretched to fit the corner.
2961 0 : unitSize.width = borderWidth[i];
2962 0 : unitSize.height = borderHeight[j];
2963 0 : fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
2964 0 : fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
2965 : }
2966 :
2967 : DrawBorderImageComponent(aRenderingContext, aForFrame,
2968 : imgContainer, aDirtyRect,
2969 : destArea, subArea,
2970 : fillStyleH, fillStyleV,
2971 0 : unitSize, aStyleBorder, i * (RIGHT + 1) + j);
2972 : }
2973 : }
2974 : }
2975 :
2976 : static void
2977 0 : DrawBorderImageComponent(nsRenderingContext& aRenderingContext,
2978 : nsIFrame* aForFrame,
2979 : imgIContainer* aImage,
2980 : const nsRect& aDirtyRect,
2981 : const nsRect& aFill,
2982 : const nsIntRect& aSrc,
2983 : PRUint8 aHFill,
2984 : PRUint8 aVFill,
2985 : const nsSize& aUnitSize,
2986 : const nsStyleBorder& aStyleBorder,
2987 : PRUint8 aIndex)
2988 : {
2989 0 : if (aFill.IsEmpty() || aSrc.IsEmpty())
2990 0 : return;
2991 :
2992 : // Don't bother trying to cache sub images if the border image is animated
2993 : // We can only sucessfully call GetAnimated() if we are fully decoded, so default to true
2994 0 : bool animated = true;
2995 0 : aImage->GetAnimated(&animated);
2996 :
2997 0 : nsCOMPtr<imgIContainer> subImage;
2998 0 : if (animated || (subImage = aStyleBorder.GetSubImage(aIndex)) == 0) {
2999 0 : if (NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT, aSrc,
3000 : imgIContainer::FLAG_SYNC_DECODE,
3001 : getter_AddRefs(subImage))))
3002 : return;
3003 :
3004 0 : if (!animated)
3005 0 : aStyleBorder.SetSubImage(aIndex, subImage);
3006 : }
3007 :
3008 : gfxPattern::GraphicsFilter graphicsFilter =
3009 0 : nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame);
3010 :
3011 : // If we have no tiling in either direction, we can skip the intermediate
3012 : // scaling step.
3013 0 : if ((aHFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH &&
3014 : aVFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) ||
3015 : (aUnitSize.width == aFill.width &&
3016 : aUnitSize.height == aFill.height)) {
3017 : nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
3018 : graphicsFilter,
3019 0 : aFill, aDirtyRect, imgIContainer::FLAG_NONE);
3020 : return;
3021 : }
3022 :
3023 : // Compute the scale and position of the master copy of the image.
3024 0 : nsRect tile;
3025 0 : switch (aHFill) {
3026 : case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
3027 0 : tile.x = aFill.x;
3028 0 : tile.width = aFill.width;
3029 0 : break;
3030 : case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
3031 0 : tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
3032 0 : tile.width = aUnitSize.width;
3033 0 : break;
3034 : case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
3035 0 : tile.x = aFill.x;
3036 0 : tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width);
3037 0 : break;
3038 : default:
3039 0 : NS_NOTREACHED("unrecognized border-image fill style");
3040 : }
3041 :
3042 0 : switch (aVFill) {
3043 : case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
3044 0 : tile.y = aFill.y;
3045 0 : tile.height = aFill.height;
3046 0 : break;
3047 : case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
3048 0 : tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
3049 0 : tile.height = aUnitSize.height;
3050 0 : break;
3051 : case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
3052 0 : tile.y = aFill.y;
3053 0 : tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
3054 0 : break;
3055 : default:
3056 0 : NS_NOTREACHED("unrecognized border-image fill style");
3057 : }
3058 :
3059 : nsLayoutUtils::DrawImage(&aRenderingContext, subImage, graphicsFilter,
3060 : tile, aFill, tile.TopLeft(), aDirtyRect,
3061 0 : imgIContainer::FLAG_NONE);
3062 : }
3063 :
3064 : // Begin table border-collapsing section
3065 : // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
3066 : // At some point, all functions should be unified to include the additional functionality that these provide
3067 :
3068 : static nscoord
3069 0 : RoundIntToPixel(nscoord aValue,
3070 : nscoord aTwipsPerPixel,
3071 : bool aRoundDown = false)
3072 : {
3073 0 : if (aTwipsPerPixel <= 0)
3074 : // We must be rendering to a device that has a resolution greater than Twips!
3075 : // In that case, aValue is as accurate as it's going to get.
3076 0 : return aValue;
3077 :
3078 0 : nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
3079 0 : nscoord extra = aValue % aTwipsPerPixel;
3080 0 : nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
3081 0 : return finalValue;
3082 : }
3083 :
3084 : static nscoord
3085 0 : RoundFloatToPixel(float aValue,
3086 : nscoord aTwipsPerPixel,
3087 : bool aRoundDown = false)
3088 : {
3089 0 : return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
3090 : }
3091 :
3092 : static void
3093 0 : SetPoly(const nsRect& aRect,
3094 : nsPoint* poly)
3095 : {
3096 0 : poly[0].x = aRect.x;
3097 0 : poly[0].y = aRect.y;
3098 0 : poly[1].x = aRect.x + aRect.width;
3099 0 : poly[1].y = aRect.y;
3100 0 : poly[2].x = aRect.x + aRect.width;
3101 0 : poly[2].y = aRect.y + aRect.height;
3102 0 : poly[3].x = aRect.x;
3103 0 : poly[3].y = aRect.y + aRect.height;
3104 0 : poly[4].x = aRect.x;
3105 0 : poly[4].y = aRect.y;
3106 0 : }
3107 :
3108 : static void
3109 0 : DrawSolidBorderSegment(nsRenderingContext& aContext,
3110 : nsRect aRect,
3111 : nscoord aTwipsPerPixel,
3112 : PRUint8 aStartBevelSide = 0,
3113 : nscoord aStartBevelOffset = 0,
3114 : PRUint8 aEndBevelSide = 0,
3115 : nscoord aEndBevelOffset = 0)
3116 : {
3117 :
3118 0 : if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) ||
3119 : ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
3120 : // simple line or rectangle
3121 0 : if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) {
3122 0 : if (1 == aRect.height)
3123 0 : aContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
3124 : else
3125 0 : aContext.FillRect(aRect);
3126 : }
3127 : else {
3128 0 : if (1 == aRect.width)
3129 0 : aContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
3130 : else
3131 0 : aContext.FillRect(aRect);
3132 : }
3133 : }
3134 : else {
3135 : // polygon with beveling
3136 0 : nsPoint poly[5];
3137 0 : SetPoly(aRect, poly);
3138 0 : switch(aStartBevelSide) {
3139 : case NS_SIDE_TOP:
3140 0 : poly[0].x += aStartBevelOffset;
3141 0 : poly[4].x = poly[0].x;
3142 0 : break;
3143 : case NS_SIDE_BOTTOM:
3144 0 : poly[3].x += aStartBevelOffset;
3145 0 : break;
3146 : case NS_SIDE_RIGHT:
3147 0 : poly[1].y += aStartBevelOffset;
3148 0 : break;
3149 : case NS_SIDE_LEFT:
3150 0 : poly[0].y += aStartBevelOffset;
3151 0 : poly[4].y = poly[0].y;
3152 : }
3153 :
3154 0 : switch(aEndBevelSide) {
3155 : case NS_SIDE_TOP:
3156 0 : poly[1].x -= aEndBevelOffset;
3157 0 : break;
3158 : case NS_SIDE_BOTTOM:
3159 0 : poly[2].x -= aEndBevelOffset;
3160 0 : break;
3161 : case NS_SIDE_RIGHT:
3162 0 : poly[2].y -= aEndBevelOffset;
3163 0 : break;
3164 : case NS_SIDE_LEFT:
3165 0 : poly[3].y -= aEndBevelOffset;
3166 : }
3167 :
3168 0 : aContext.FillPolygon(poly, 5);
3169 : }
3170 :
3171 :
3172 0 : }
3173 :
3174 : static void
3175 0 : GetDashInfo(nscoord aBorderLength,
3176 : nscoord aDashLength,
3177 : nscoord aTwipsPerPixel,
3178 : PRInt32& aNumDashSpaces,
3179 : nscoord& aStartDashLength,
3180 : nscoord& aEndDashLength)
3181 : {
3182 0 : aNumDashSpaces = 0;
3183 0 : if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
3184 0 : aStartDashLength = aBorderLength;
3185 0 : aEndDashLength = 0;
3186 : }
3187 : else {
3188 0 : aNumDashSpaces = (aBorderLength - aDashLength)/ (2 * aDashLength); // round down
3189 0 : nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
3190 0 : if (extra > 0) {
3191 0 : nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
3192 0 : aStartDashLength += half;
3193 0 : aEndDashLength += (extra - half);
3194 : }
3195 : }
3196 0 : }
3197 :
3198 : void
3199 0 : nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext,
3200 : PRUint8 aBorderStyle,
3201 : nscolor aBorderColor,
3202 : const nsStyleBackground* aBGColor,
3203 : const nsRect& aBorder,
3204 : PRInt32 aAppUnitsPerCSSPixel,
3205 : PRUint8 aStartBevelSide,
3206 : nscoord aStartBevelOffset,
3207 : PRUint8 aEndBevelSide,
3208 : nscoord aEndBevelOffset)
3209 : {
3210 0 : aContext.SetColor (aBorderColor);
3211 :
3212 0 : bool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide));
3213 0 : nscoord twipsPerPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel);
3214 0 : PRUint8 ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
3215 :
3216 0 : if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) ||
3217 : (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
3218 : // no beveling for 1 pixel border, dash or dot
3219 0 : aStartBevelOffset = 0;
3220 0 : aEndBevelOffset = 0;
3221 : }
3222 :
3223 0 : gfxContext *ctx = aContext.ThebesContext();
3224 0 : gfxContext::AntialiasMode oldMode = ctx->CurrentAntialiasMode();
3225 0 : ctx->SetAntialiasMode(gfxContext::MODE_ALIASED);
3226 :
3227 0 : switch (aBorderStyle) {
3228 : case NS_STYLE_BORDER_STYLE_NONE:
3229 : case NS_STYLE_BORDER_STYLE_HIDDEN:
3230 : //NS_ASSERTION(false, "style of none or hidden");
3231 0 : break;
3232 : case NS_STYLE_BORDER_STYLE_DOTTED:
3233 : case NS_STYLE_BORDER_STYLE_DASHED:
3234 : {
3235 0 : nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
3236 : // make the dash length proportional to the border thickness
3237 0 : dashLength *= (horizontal) ? aBorder.height : aBorder.width;
3238 : // make the min dash length for the ends 1/2 the dash length
3239 : nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle)
3240 0 : ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength;
3241 0 : minDashLength = NS_MAX(minDashLength, twipsPerPixel);
3242 0 : nscoord numDashSpaces = 0;
3243 0 : nscoord startDashLength = minDashLength;
3244 0 : nscoord endDashLength = minDashLength;
3245 0 : if (horizontal) {
3246 0 : GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
3247 0 : nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
3248 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3249 0 : for (PRInt32 spaceX = 0; spaceX < numDashSpaces; spaceX++) {
3250 0 : rect.x += rect.width + dashLength;
3251 0 : rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength;
3252 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3253 : }
3254 : }
3255 : else {
3256 0 : GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
3257 0 : nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
3258 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3259 0 : for (PRInt32 spaceY = 0; spaceY < numDashSpaces; spaceY++) {
3260 0 : rect.y += rect.height + dashLength;
3261 0 : rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength;
3262 0 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
3263 : }
3264 : }
3265 : }
3266 0 : break;
3267 : case NS_STYLE_BORDER_STYLE_GROOVE:
3268 0 : ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
3269 : case NS_STYLE_BORDER_STYLE_RIDGE:
3270 0 : if ((horizontal && (twipsPerPixel >= aBorder.height)) ||
3271 0 : (!horizontal && (twipsPerPixel >= aBorder.width))) {
3272 : // a one pixel border
3273 : DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset,
3274 0 : aEndBevelSide, aEndBevelOffset);
3275 : }
3276 : else {
3277 : nscoord startBevel = (aStartBevelOffset > 0)
3278 0 : ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, true) : 0;
3279 : nscoord endBevel = (aEndBevelOffset > 0)
3280 0 : ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, true) : 0;
3281 0 : mozilla::css::Side ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT;
3282 : // FIXME: In theory, this should use the visited-dependent
3283 : // background color, but I don't care.
3284 : aContext.SetColor (
3285 0 : MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
3286 0 : nsRect rect(aBorder);
3287 : nscoord half;
3288 0 : if (horizontal) { // top, bottom
3289 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
3290 0 : rect.height = half;
3291 0 : if (NS_SIDE_TOP == aStartBevelSide) {
3292 0 : rect.x += startBevel;
3293 0 : rect.width -= startBevel;
3294 : }
3295 0 : if (NS_SIDE_TOP == aEndBevelSide) {
3296 0 : rect.width -= endBevel;
3297 : }
3298 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3299 0 : startBevel, aEndBevelSide, endBevel);
3300 : }
3301 : else { // left, right
3302 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel);
3303 0 : rect.width = half;
3304 0 : if (NS_SIDE_LEFT == aStartBevelSide) {
3305 0 : rect.y += startBevel;
3306 0 : rect.height -= startBevel;
3307 : }
3308 0 : if (NS_SIDE_LEFT == aEndBevelSide) {
3309 0 : rect.height -= endBevel;
3310 : }
3311 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3312 0 : startBevel, aEndBevelSide, endBevel);
3313 : }
3314 :
3315 0 : rect = aBorder;
3316 0 : ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT;
3317 : // FIXME: In theory, this should use the visited-dependent
3318 : // background color, but I don't care.
3319 : aContext.SetColor (
3320 0 : MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
3321 0 : if (horizontal) {
3322 0 : rect.y = rect.y + half;
3323 0 : rect.height = aBorder.height - half;
3324 0 : if (NS_SIDE_BOTTOM == aStartBevelSide) {
3325 0 : rect.x += startBevel;
3326 0 : rect.width -= startBevel;
3327 : }
3328 0 : if (NS_SIDE_BOTTOM == aEndBevelSide) {
3329 0 : rect.width -= endBevel;
3330 : }
3331 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3332 0 : startBevel, aEndBevelSide, endBevel);
3333 : }
3334 : else {
3335 0 : rect.x = rect.x + half;
3336 0 : rect.width = aBorder.width - half;
3337 0 : if (NS_SIDE_RIGHT == aStartBevelSide) {
3338 0 : rect.y += aStartBevelOffset - startBevel;
3339 0 : rect.height -= startBevel;
3340 : }
3341 0 : if (NS_SIDE_RIGHT == aEndBevelSide) {
3342 0 : rect.height -= endBevel;
3343 : }
3344 : DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
3345 0 : startBevel, aEndBevelSide, endBevel);
3346 : }
3347 : }
3348 0 : break;
3349 : case NS_STYLE_BORDER_STYLE_DOUBLE:
3350 : // We can only do "double" borders if the thickness of the border
3351 : // is more than 2px. Otherwise, we fall through to painting a
3352 : // solid border.
3353 0 : if ((aBorder.width > 2*twipsPerPixel || horizontal) &&
3354 0 : (aBorder.height > 2*twipsPerPixel || !horizontal)) {
3355 : nscoord startBevel = (aStartBevelOffset > 0)
3356 0 : ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0;
3357 : nscoord endBevel = (aEndBevelOffset > 0)
3358 0 : ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0;
3359 0 : if (horizontal) { // top, bottom
3360 0 : nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel);
3361 :
3362 : // draw the top line or rect
3363 0 : nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
3364 0 : if (NS_SIDE_TOP == aStartBevelSide) {
3365 0 : topRect.x += aStartBevelOffset - startBevel;
3366 0 : topRect.width -= aStartBevelOffset - startBevel;
3367 : }
3368 0 : if (NS_SIDE_TOP == aEndBevelSide) {
3369 0 : topRect.width -= aEndBevelOffset - endBevel;
3370 : }
3371 : DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide,
3372 0 : startBevel, aEndBevelSide, endBevel);
3373 :
3374 : // draw the botom line or rect
3375 0 : nscoord heightOffset = aBorder.height - thirdHeight;
3376 0 : nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
3377 0 : if (NS_SIDE_BOTTOM == aStartBevelSide) {
3378 0 : bottomRect.x += aStartBevelOffset - startBevel;
3379 0 : bottomRect.width -= aStartBevelOffset - startBevel;
3380 : }
3381 0 : if (NS_SIDE_BOTTOM == aEndBevelSide) {
3382 0 : bottomRect.width -= aEndBevelOffset - endBevel;
3383 : }
3384 : DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide,
3385 0 : startBevel, aEndBevelSide, endBevel);
3386 : }
3387 : else { // left, right
3388 0 : nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel);
3389 :
3390 0 : nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
3391 0 : if (NS_SIDE_LEFT == aStartBevelSide) {
3392 0 : leftRect.y += aStartBevelOffset - startBevel;
3393 0 : leftRect.height -= aStartBevelOffset - startBevel;
3394 : }
3395 0 : if (NS_SIDE_LEFT == aEndBevelSide) {
3396 0 : leftRect.height -= aEndBevelOffset - endBevel;
3397 : }
3398 : DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide,
3399 0 : startBevel, aEndBevelSide, endBevel);
3400 :
3401 0 : nscoord widthOffset = aBorder.width - thirdWidth;
3402 0 : nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
3403 0 : if (NS_SIDE_RIGHT == aStartBevelSide) {
3404 0 : rightRect.y += aStartBevelOffset - startBevel;
3405 0 : rightRect.height -= aStartBevelOffset - startBevel;
3406 : }
3407 0 : if (NS_SIDE_RIGHT == aEndBevelSide) {
3408 0 : rightRect.height -= aEndBevelOffset - endBevel;
3409 : }
3410 : DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide,
3411 0 : startBevel, aEndBevelSide, endBevel);
3412 : }
3413 0 : break;
3414 : }
3415 : // else fall through to solid
3416 : case NS_STYLE_BORDER_STYLE_SOLID:
3417 : DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide,
3418 0 : aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
3419 0 : break;
3420 : case NS_STYLE_BORDER_STYLE_OUTSET:
3421 : case NS_STYLE_BORDER_STYLE_INSET:
3422 0 : NS_ASSERTION(false, "inset, outset should have been converted to groove, ridge");
3423 0 : break;
3424 : case NS_STYLE_BORDER_STYLE_AUTO:
3425 0 : NS_ASSERTION(false, "Unexpected 'auto' table border");
3426 0 : break;
3427 : }
3428 :
3429 0 : ctx->SetAntialiasMode(oldMode);
3430 0 : }
3431 :
3432 : // End table border-collapsing section
3433 :
3434 : void
3435 0 : nsCSSRendering::PaintDecorationLine(gfxContext* aGfxContext,
3436 : const gfxRect& aDirtyRect,
3437 : const nscolor aColor,
3438 : const gfxPoint& aPt,
3439 : const gfxSize& aLineSize,
3440 : const gfxFloat aAscent,
3441 : const gfxFloat aOffset,
3442 : const PRUint8 aDecoration,
3443 : const PRUint8 aStyle,
3444 : const gfxFloat aDescentLimit)
3445 : {
3446 0 : NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
3447 :
3448 : gfxRect rect =
3449 : GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
3450 0 : aDecoration, aStyle, aDescentLimit);
3451 0 : if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
3452 0 : return;
3453 : }
3454 :
3455 0 : if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
3456 : aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
3457 : aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
3458 0 : NS_ERROR("Invalid decoration value!");
3459 0 : return;
3460 : }
3461 :
3462 0 : gfxFloat lineHeight = NS_MAX(NS_round(aLineSize.height), 1.0);
3463 0 : bool contextIsSaved = false;
3464 :
3465 : gfxFloat oldLineWidth;
3466 0 : nsRefPtr<gfxPattern> oldPattern;
3467 :
3468 0 : switch (aStyle) {
3469 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3470 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
3471 0 : oldLineWidth = aGfxContext->CurrentLineWidth();
3472 0 : oldPattern = aGfxContext->GetPattern();
3473 0 : break;
3474 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
3475 0 : aGfxContext->Save();
3476 0 : contextIsSaved = true;
3477 0 : aGfxContext->Clip(rect);
3478 0 : gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
3479 0 : gfxFloat dash[2] = { dashWidth, dashWidth };
3480 0 : aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
3481 0 : aGfxContext->SetDash(dash, 2, 0.0);
3482 : // We should continue to draw the last dash even if it is not in the rect.
3483 0 : rect.width += dashWidth;
3484 0 : break;
3485 : }
3486 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: {
3487 0 : aGfxContext->Save();
3488 0 : contextIsSaved = true;
3489 0 : aGfxContext->Clip(rect);
3490 0 : gfxFloat dashWidth = lineHeight * DOT_LENGTH;
3491 : gfxFloat dash[2];
3492 0 : if (lineHeight > 2.0) {
3493 0 : dash[0] = 0.0;
3494 0 : dash[1] = dashWidth * 2.0;
3495 0 : aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
3496 : } else {
3497 0 : dash[0] = dashWidth;
3498 0 : dash[1] = dashWidth;
3499 : }
3500 0 : aGfxContext->SetDash(dash, 2, 0.0);
3501 : // We should continue to draw the last dot even if it is not in the rect.
3502 0 : rect.width += dashWidth;
3503 0 : break;
3504 : }
3505 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
3506 0 : aGfxContext->Save();
3507 0 : contextIsSaved = true;
3508 0 : aGfxContext->Clip(rect);
3509 0 : if (lineHeight > 2.0) {
3510 0 : aGfxContext->SetAntialiasMode(gfxContext::MODE_COVERAGE);
3511 : } else {
3512 : // Don't use anti-aliasing here. Because looks like lighter color wavy
3513 : // line at this case. And probably, users don't think the
3514 : // non-anti-aliased wavy line is not pretty.
3515 0 : aGfxContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
3516 : }
3517 0 : break;
3518 : default:
3519 0 : NS_ERROR("Invalid style value!");
3520 : return;
3521 : }
3522 :
3523 : // The y position should be set to the middle of the line.
3524 0 : rect.y += lineHeight / 2;
3525 :
3526 0 : aGfxContext->SetColor(gfxRGBA(aColor));
3527 0 : aGfxContext->SetLineWidth(lineHeight);
3528 0 : switch (aStyle) {
3529 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3530 0 : aGfxContext->NewPath();
3531 0 : aGfxContext->MoveTo(rect.TopLeft());
3532 0 : aGfxContext->LineTo(rect.TopRight());
3533 0 : aGfxContext->Stroke();
3534 0 : break;
3535 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
3536 : /**
3537 : * We are drawing double line as:
3538 : *
3539 : * +-------------------------------------------+
3540 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3541 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3542 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3543 : * | |
3544 : * | |
3545 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3546 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3547 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3548 : * +-------------------------------------------+
3549 : */
3550 0 : aGfxContext->NewPath();
3551 0 : aGfxContext->MoveTo(rect.TopLeft());
3552 0 : aGfxContext->LineTo(rect.TopRight());
3553 0 : rect.height -= lineHeight;
3554 0 : aGfxContext->MoveTo(rect.BottomLeft());
3555 0 : aGfxContext->LineTo(rect.BottomRight());
3556 0 : aGfxContext->Stroke();
3557 0 : break;
3558 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
3559 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
3560 0 : aGfxContext->NewPath();
3561 0 : aGfxContext->MoveTo(rect.TopLeft());
3562 0 : aGfxContext->LineTo(rect.TopRight());
3563 0 : aGfxContext->Stroke();
3564 0 : break;
3565 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
3566 : /**
3567 : * We are drawing wavy line as:
3568 : *
3569 : * P: Path, X: Painted pixel
3570 : *
3571 : * +---------------------------------------+
3572 : * XX|X XXXXXX XXXXXX |
3573 : * PP|PX XPPPPPPX XPPPPPPX | ^
3574 : * XX|XPX XPXXXXXXPX XPXXXXXXPX| |
3575 : * | XPX XPX XPX XPX XP|X |adv
3576 : * | XPXXXXXXPX XPXXXXXXPX X|PX |
3577 : * | XPPPPPPX XPPPPPPX |XPX v
3578 : * | XXXXXX XXXXXX | XX
3579 : * +---------------------------------------+
3580 : * <---><---> ^
3581 : * adv flatLengthAtVertex rightMost
3582 : *
3583 : * 1. Always starts from top-left of the drawing area, however, we need
3584 : * to draw the line from outside of the rect. Because the start
3585 : * point of the line is not good style if we draw from inside it.
3586 : * 2. First, draw horizontal line from outside the rect to top-left of
3587 : * the rect;
3588 : * 3. Goes down to bottom of the area at 45 degrees.
3589 : * 4. Slides to right horizontaly, see |flatLengthAtVertex|.
3590 : * 5. Goes up to top of the area at 45 degrees.
3591 : * 6. Slides to right horizontaly.
3592 : * 7. Repeat from 2 until reached to right-most edge of the area.
3593 : */
3594 :
3595 0 : gfxFloat adv = rect.Height() - lineHeight;
3596 0 : gfxFloat flatLengthAtVertex = NS_MAX((lineHeight - 1.0) * 2.0, 1.0);
3597 :
3598 : // figure out if we can trim whole cycles from the left and right edges
3599 : // of the line, to try and avoid creating an unnecessarily long and
3600 : // complex path
3601 0 : gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
3602 0 : PRInt32 skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
3603 0 : if (skipCycles > 0) {
3604 0 : rect.x += skipCycles * cycleLength;
3605 0 : rect.width -= skipCycles * cycleLength;
3606 : }
3607 :
3608 0 : rect.x += lineHeight / 2.0;
3609 0 : gfxPoint pt(rect.TopLeft());
3610 0 : gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
3611 :
3612 0 : skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
3613 0 : if (skipCycles > 0) {
3614 0 : rightMost -= skipCycles * cycleLength;
3615 : }
3616 :
3617 0 : aGfxContext->NewPath();
3618 :
3619 0 : pt.x -= lineHeight;
3620 0 : aGfxContext->MoveTo(pt); // 1
3621 :
3622 0 : pt.x = rect.X();
3623 0 : aGfxContext->LineTo(pt); // 2
3624 :
3625 0 : bool goDown = true;
3626 0 : PRUint32 iter = 0;
3627 0 : while (pt.x < rightMost) {
3628 0 : if (++iter > 1000) {
3629 : // stroke the current path and start again, to avoid pathological
3630 : // behavior in cairo with huge numbers of path segments
3631 0 : aGfxContext->Stroke();
3632 0 : aGfxContext->NewPath();
3633 0 : aGfxContext->MoveTo(pt);
3634 0 : iter = 0;
3635 : }
3636 0 : pt.x += adv;
3637 0 : pt.y += goDown ? adv : -adv;
3638 :
3639 0 : aGfxContext->LineTo(pt); // 3 and 5
3640 :
3641 0 : pt.x += flatLengthAtVertex;
3642 0 : aGfxContext->LineTo(pt); // 4 and 6
3643 :
3644 0 : goDown = !goDown;
3645 : }
3646 0 : aGfxContext->Stroke();
3647 0 : break;
3648 : }
3649 : default:
3650 0 : NS_ERROR("Invalid style value!");
3651 0 : break;
3652 : }
3653 :
3654 0 : if (contextIsSaved) {
3655 0 : aGfxContext->Restore();
3656 : } else {
3657 0 : aGfxContext->SetPattern(oldPattern);
3658 0 : aGfxContext->SetLineWidth(oldLineWidth);
3659 : }
3660 : }
3661 :
3662 : nsRect
3663 0 : nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
3664 : const gfxSize& aLineSize,
3665 : const gfxFloat aAscent,
3666 : const gfxFloat aOffset,
3667 : const PRUint8 aDecoration,
3668 : const PRUint8 aStyle,
3669 : const gfxFloat aDescentLimit)
3670 : {
3671 0 : NS_ASSERTION(aPresContext, "aPresContext is null");
3672 0 : NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
3673 :
3674 : gfxRect rect =
3675 : GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
3676 0 : aDecoration, aStyle, aDescentLimit);
3677 : // The rect values are already rounded to nearest device pixels.
3678 0 : nsRect r;
3679 0 : r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
3680 0 : r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
3681 0 : r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
3682 0 : r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
3683 : return r;
3684 : }
3685 :
3686 : gfxRect
3687 0 : nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
3688 : const gfxSize& aLineSize,
3689 : const gfxFloat aAscent,
3690 : const gfxFloat aOffset,
3691 : const PRUint8 aDecoration,
3692 : const PRUint8 aStyle,
3693 : const gfxFloat aDescentLimit)
3694 : {
3695 0 : NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
3696 : "Invalid aStyle value");
3697 :
3698 0 : if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
3699 0 : return gfxRect(0, 0, 0, 0);
3700 :
3701 0 : bool canLiftUnderline = aDescentLimit >= 0.0;
3702 :
3703 0 : const gfxFloat left = floor(aPt.x + 0.5),
3704 0 : right = floor(aPt.x + aLineSize.width + 0.5);
3705 0 : gfxRect r(left, 0, right - left, 0);
3706 :
3707 0 : gfxFloat lineHeight = NS_round(aLineSize.height);
3708 0 : lineHeight = NS_MAX(lineHeight, 1.0);
3709 :
3710 0 : gfxFloat ascent = NS_round(aAscent);
3711 0 : gfxFloat descentLimit = floor(aDescentLimit);
3712 :
3713 0 : gfxFloat suggestedMaxRectHeight = NS_MAX(NS_MIN(ascent, descentLimit), 1.0);
3714 0 : r.height = lineHeight;
3715 0 : if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
3716 : /**
3717 : * We will draw double line as:
3718 : *
3719 : * +-------------------------------------------+
3720 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3721 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3722 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3723 : * | | ^
3724 : * | | | gap
3725 : * | | v
3726 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3727 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
3728 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3729 : * +-------------------------------------------+
3730 : */
3731 0 : gfxFloat gap = NS_round(lineHeight / 2.0);
3732 0 : gap = NS_MAX(gap, 1.0);
3733 0 : r.height = lineHeight * 2.0 + gap;
3734 0 : if (canLiftUnderline) {
3735 0 : if (r.Height() > suggestedMaxRectHeight) {
3736 : // Don't shrink the line height, because the thickness has some meaning.
3737 : // We can just shrink the gap at this time.
3738 0 : r.height = NS_MAX(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
3739 : }
3740 : }
3741 0 : } else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
3742 : /**
3743 : * We will draw wavy line as:
3744 : *
3745 : * +-------------------------------------------+
3746 : * |XXXXX XXXXXX XXXXXX | ^
3747 : * |XXXXXX XXXXXXXX XXXXXXXX | | lineHeight
3748 : * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
3749 : * | XXX XXX XXX XXX XX|
3750 : * | XXXXXXXXXX XXXXXXXXXX X|
3751 : * | XXXXXXXX XXXXXXXX |
3752 : * | XXXXXX XXXXXX |
3753 : * +-------------------------------------------+
3754 : */
3755 0 : r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
3756 0 : if (canLiftUnderline) {
3757 0 : if (r.Height() > suggestedMaxRectHeight) {
3758 : // Don't shrink the line height even if there is not enough space,
3759 : // because the thickness has some meaning. E.g., the 1px wavy line and
3760 : // 2px wavy line can be used for different meaning in IME selections
3761 : // at same time.
3762 0 : r.height = NS_MAX(suggestedMaxRectHeight, lineHeight * 2.0);
3763 : }
3764 : }
3765 : }
3766 :
3767 0 : gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
3768 0 : gfxFloat offset = 0.0;
3769 0 : switch (aDecoration) {
3770 : case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
3771 0 : offset = aOffset;
3772 0 : if (canLiftUnderline) {
3773 0 : if (descentLimit < -offset + r.Height()) {
3774 : // If we can ignore the offset and the decoration line is overflowing,
3775 : // we should align the bottom edge of the decoration line rect if it's
3776 : // possible. Otherwise, we should lift up the top edge of the rect as
3777 : // far as possible.
3778 0 : gfxFloat offsetBottomAligned = -descentLimit + r.Height();
3779 0 : gfxFloat offsetTopAligned = 0.0;
3780 0 : offset = NS_MIN(offsetBottomAligned, offsetTopAligned);
3781 : }
3782 : }
3783 0 : break;
3784 : case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
3785 0 : offset = aOffset - lineHeight + r.Height();
3786 0 : break;
3787 : case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
3788 0 : gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
3789 0 : extra = NS_MAX(extra, lineHeight);
3790 0 : offset = aOffset - lineHeight + extra;
3791 0 : break;
3792 : }
3793 : default:
3794 0 : NS_ERROR("Invalid decoration value!");
3795 : }
3796 0 : r.y = baseline - floor(offset + 0.5);
3797 0 : return r;
3798 : }
3799 :
3800 : // ------------------
3801 : // ImageRenderer
3802 : // ------------------
3803 0 : ImageRenderer::ImageRenderer(nsIFrame* aForFrame,
3804 : const nsStyleImage* aImage,
3805 : PRUint32 aFlags)
3806 : : mForFrame(aForFrame)
3807 : , mImage(aImage)
3808 0 : , mType(aImage->GetType())
3809 : , mImageContainer(nsnull)
3810 : , mGradientData(nsnull)
3811 : , mPaintServerFrame(nsnull)
3812 : , mIsReady(false)
3813 : , mSize(0, 0)
3814 0 : , mFlags(aFlags)
3815 : {
3816 0 : }
3817 :
3818 0 : ImageRenderer::~ImageRenderer()
3819 : {
3820 0 : }
3821 :
3822 : bool
3823 0 : ImageRenderer::PrepareImage()
3824 : {
3825 0 : if (mImage->IsEmpty() || !mImage->IsComplete()) {
3826 : // Make sure the image is actually decoding
3827 0 : mImage->RequestDecode();
3828 :
3829 : // We can not prepare the image for rendering if it is not fully loaded.
3830 : //
3831 : // Special case: If we requested a sync decode and we have an image, push
3832 : // on through
3833 0 : nsCOMPtr<imgIContainer> img;
3834 0 : if (!((mFlags & FLAG_SYNC_DECODE_IMAGES) &&
3835 : (mType == eStyleImageType_Image) &&
3836 0 : (NS_SUCCEEDED(mImage->GetImageData()->GetImage(getter_AddRefs(img))) && img)))
3837 0 : return false;
3838 : }
3839 :
3840 0 : switch (mType) {
3841 : case eStyleImageType_Image:
3842 : {
3843 0 : nsCOMPtr<imgIContainer> srcImage;
3844 0 : mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
3845 0 : NS_ABORT_IF_FALSE(srcImage, "If srcImage is null, mImage->IsComplete() "
3846 : "should have returned false");
3847 :
3848 0 : if (!mImage->GetCropRect()) {
3849 0 : mImageContainer.swap(srcImage);
3850 : } else {
3851 0 : nsIntRect actualCropRect;
3852 : bool isEntireImage;
3853 : bool success =
3854 0 : mImage->ComputeActualCropRect(actualCropRect, &isEntireImage);
3855 0 : NS_ASSERTION(success, "ComputeActualCropRect() should not fail here");
3856 0 : if (!success || actualCropRect.IsEmpty()) {
3857 : // The cropped image has zero size
3858 0 : return false;
3859 : }
3860 0 : if (isEntireImage) {
3861 : // The cropped image is identical to the source image
3862 0 : mImageContainer.swap(srcImage);
3863 : } else {
3864 0 : nsCOMPtr<imgIContainer> subImage;
3865 : PRUint32 aExtractFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
3866 : ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
3867 0 : : (PRUint32) imgIContainer::FLAG_NONE;
3868 0 : nsresult rv = srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,
3869 : actualCropRect, aExtractFlags,
3870 0 : getter_AddRefs(subImage));
3871 0 : if (NS_FAILED(rv)) {
3872 : NS_WARNING("The cropped image contains no pixels to draw; "
3873 0 : "maybe the crop rect is outside the image frame rect");
3874 0 : return false;
3875 : }
3876 0 : mImageContainer.swap(subImage);
3877 : }
3878 : }
3879 0 : mIsReady = true;
3880 0 : break;
3881 : }
3882 : case eStyleImageType_Gradient:
3883 0 : mGradientData = mImage->GetGradientData();
3884 0 : mIsReady = true;
3885 0 : break;
3886 : case eStyleImageType_Element:
3887 : {
3888 : nsAutoString elementId =
3889 0 : NS_LITERAL_STRING("#") + nsDependentString(mImage->GetElementId());
3890 0 : nsCOMPtr<nsIURI> targetURI;
3891 0 : nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
3892 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), elementId,
3893 0 : mForFrame->GetContent()->GetCurrentDoc(), base);
3894 : nsSVGPaintingProperty* property = nsSVGEffects::GetPaintingPropertyForURI(
3895 0 : targetURI, mForFrame->GetFirstContinuation(),
3896 0 : nsSVGEffects::BackgroundImageProperty());
3897 0 : if (!property)
3898 0 : return false;
3899 0 : mPaintServerFrame = property->GetReferencedFrame();
3900 :
3901 : // If the referenced element doesn't have a frame we might still be able
3902 : // to paint it if it's an <img>, <canvas>, or <video> element.
3903 0 : if (!mPaintServerFrame) {
3904 : mImageElementSurface =
3905 0 : nsLayoutUtils::SurfaceFromElement(property->GetReferencedElement());
3906 0 : if (!mImageElementSurface.mSurface)
3907 0 : return false;
3908 : }
3909 0 : mIsReady = true;
3910 0 : break;
3911 : }
3912 : case eStyleImageType_Null:
3913 : default:
3914 0 : break;
3915 : }
3916 :
3917 0 : return mIsReady;
3918 : }
3919 :
3920 : enum FitType { CONTAIN, COVER };
3921 :
3922 : static nsSize
3923 0 : ComputeContainCoverSizeFromRatio(const nsSize& aBgPositioningArea,
3924 : const nsSize& aRatio, FitType fitType)
3925 : {
3926 0 : NS_ABORT_IF_FALSE(aRatio.width > 0, "width division by zero");
3927 0 : NS_ABORT_IF_FALSE(aRatio.height > 0, "height division by zero");
3928 :
3929 0 : float scaleX = double(aBgPositioningArea.width) / aRatio.width;
3930 0 : float scaleY = double(aBgPositioningArea.height) / aRatio.height;
3931 0 : nsSize size;
3932 0 : if ((fitType == CONTAIN) == (scaleX < scaleY)) {
3933 0 : size.width = aBgPositioningArea.width;
3934 0 : size.height = NSCoordSaturatingNonnegativeMultiply(aRatio.height, scaleX);
3935 : } else {
3936 0 : size.width = NSCoordSaturatingNonnegativeMultiply(aRatio.width, scaleY);
3937 0 : size.height = aBgPositioningArea.height;
3938 : }
3939 : return size;
3940 : }
3941 :
3942 : void
3943 0 : ImageRenderer::ComputeUnscaledDimensions(const nsSize& aBgPositioningArea,
3944 : nscoord& aUnscaledWidth, bool& aHaveWidth,
3945 : nscoord& aUnscaledHeight, bool& aHaveHeight,
3946 : nsSize& aRatio)
3947 : {
3948 0 : NS_ASSERTION(mIsReady, "Ensure PrepareImage() has returned true "
3949 : "before calling me");
3950 :
3951 0 : switch (mType) {
3952 : case eStyleImageType_Image:
3953 : {
3954 0 : nsIntSize imageIntSize;
3955 : nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, imageIntSize,
3956 0 : aRatio, aHaveWidth, aHaveHeight);
3957 0 : if (aHaveWidth) {
3958 0 : aUnscaledWidth = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width);
3959 : }
3960 0 : if (aHaveHeight) {
3961 0 : aUnscaledHeight = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height);
3962 : }
3963 0 : return;
3964 : }
3965 : case eStyleImageType_Gradient:
3966 : // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
3967 : // intrinsic dimensions.
3968 0 : aHaveWidth = aHaveHeight = false;
3969 0 : aRatio = nsSize(0, 0);
3970 0 : return;
3971 : case eStyleImageType_Element:
3972 : {
3973 : // XXX element() should have the width/height of the referenced element,
3974 : // and that element's ratio, if it matches. If it doesn't match, it
3975 : // should have no width/height or ratio. See element() in CSS3:
3976 : // <http://dev.w3.org/csswg/css3-images/#element-reference>.
3977 : // Make sure to change nsStyleBackground::Size::DependsOnFrameSize
3978 : // when fixing this!
3979 0 : aHaveWidth = aHaveHeight = true;
3980 0 : nsSize size;
3981 0 : if (mPaintServerFrame) {
3982 0 : if (mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)) {
3983 0 : size = aBgPositioningArea;
3984 : } else {
3985 : // The intrinsic image size for a generic nsIFrame paint server is
3986 : // the frame's bbox size rounded to device pixels.
3987 : PRInt32 appUnitsPerDevPixel =
3988 0 : mForFrame->PresContext()->AppUnitsPerDevPixel();
3989 : nsRect rect =
3990 0 : nsSVGIntegrationUtils::GetNonSVGUserSpace(mPaintServerFrame);
3991 0 : nsRect rectSize = rect - rect.TopLeft();
3992 0 : nsIntRect rounded = rectSize.ToNearestPixels(appUnitsPerDevPixel);
3993 0 : size = rounded.ToAppUnits(appUnitsPerDevPixel).Size();
3994 : }
3995 : } else {
3996 0 : NS_ASSERTION(mImageElementSurface.mSurface, "Surface should be ready.");
3997 0 : gfxIntSize surfaceSize = mImageElementSurface.mSize;
3998 0 : size.width = nsPresContext::CSSPixelsToAppUnits(surfaceSize.width);
3999 0 : size.height = nsPresContext::CSSPixelsToAppUnits(surfaceSize.height);
4000 : }
4001 0 : aRatio = size;
4002 0 : aUnscaledWidth = size.width;
4003 0 : aUnscaledHeight = size.height;
4004 0 : return;
4005 : }
4006 : case eStyleImageType_Null:
4007 : default:
4008 0 : aHaveWidth = aHaveHeight = true;
4009 0 : aUnscaledWidth = aUnscaledHeight = 0;
4010 0 : aRatio = nsSize(0, 0);
4011 0 : return;
4012 : }
4013 : }
4014 :
4015 : nsSize
4016 0 : ImageRenderer::ComputeDrawnSize(const nsStyleBackground::Size& aLayerSize,
4017 : const nsSize& aBgPositioningArea,
4018 : nscoord aUnscaledWidth, bool aHaveWidth,
4019 : nscoord aUnscaledHeight, bool aHaveHeight,
4020 : const nsSize& aIntrinsicRatio)
4021 : {
4022 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.width >= 0,
4023 : "image ratio with nonsense width");
4024 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.height >= 0,
4025 : "image ratio with nonsense height");
4026 :
4027 : // Bail early if the image is empty.
4028 0 : if ((aHaveWidth && aUnscaledWidth <= 0) ||
4029 : (aHaveHeight && aUnscaledHeight <= 0)) {
4030 0 : return nsSize(0, 0);
4031 : }
4032 :
4033 : // If the image has an intrinsic ratio but either component of it is zero,
4034 : // then the image would eventually scale to nothingness, so again we can bail.
4035 0 : bool haveRatio = aIntrinsicRatio != nsSize(0, 0);
4036 0 : if (haveRatio &&
4037 : (aIntrinsicRatio.width == 0 || aIntrinsicRatio.height == 0)) {
4038 0 : return nsSize(0, 0);
4039 : }
4040 :
4041 : // Easiest case: background-size completely specifies the size.
4042 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eLengthPercentage &&
4043 : aLayerSize.mHeightType == nsStyleBackground::Size::eLengthPercentage) {
4044 : return nsSize(aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea),
4045 0 : aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
4046 : }
4047 :
4048 : // The harder cases: contain/cover.
4049 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eContain ||
4050 : aLayerSize.mWidthType == nsStyleBackground::Size::eCover) {
4051 : FitType fitType = aLayerSize.mWidthType == nsStyleBackground::Size::eCover
4052 : ? COVER
4053 0 : : CONTAIN;
4054 0 : if (!haveRatio) {
4055 : // If we don't have an intrinsic ratio, then proportionally scaling to
4056 : // either largest-fitting or smallest-covering size means scaling to the
4057 : // background positioning area's size.
4058 0 : return aBgPositioningArea;
4059 : }
4060 :
4061 : return ComputeContainCoverSizeFromRatio(aBgPositioningArea, aIntrinsicRatio,
4062 0 : fitType);
4063 : }
4064 :
4065 : // Harder case: all-auto.
4066 0 : if (aLayerSize.mWidthType == nsStyleBackground::Size::eAuto &&
4067 : aLayerSize.mHeightType == nsStyleBackground::Size::eAuto) {
4068 : // If the image has all its dimensions, we're done.
4069 0 : if (aHaveWidth && aHaveHeight)
4070 0 : return nsSize(aUnscaledWidth, aUnscaledHeight);
4071 :
4072 : // If the image has no dimensions, treat it as if for contain.
4073 0 : if (!aHaveWidth && !aHaveHeight) {
4074 0 : if (!haveRatio) {
4075 : // As above, max-contain without a ratio means the whole area.
4076 0 : return aBgPositioningArea;
4077 : }
4078 :
4079 : // Otherwise determine size using the intrinsic ratio.
4080 : return ComputeContainCoverSizeFromRatio(aBgPositioningArea,
4081 0 : aIntrinsicRatio, CONTAIN);
4082 : }
4083 :
4084 0 : NS_ABORT_IF_FALSE(aHaveWidth != aHaveHeight, "logic error");
4085 :
4086 0 : if (haveRatio) {
4087 : // Resolve missing dimensions using the intrinsic ratio.
4088 0 : nsSize size;
4089 0 : if (aHaveWidth) {
4090 0 : size.width = aUnscaledWidth;
4091 : size.height =
4092 : NSCoordSaturatingNonnegativeMultiply(size.width,
4093 : double(aIntrinsicRatio.height) /
4094 0 : aIntrinsicRatio.width);
4095 : } else {
4096 0 : size.height = aUnscaledHeight;
4097 : size.width =
4098 : NSCoordSaturatingNonnegativeMultiply(size.height,
4099 : double(aIntrinsicRatio.width) /
4100 0 : aIntrinsicRatio.height);
4101 : }
4102 :
4103 0 : return size;
4104 : }
4105 :
4106 : // Without a ratio we must fall back to the relevant dimension of the
4107 : // area to determine the missing dimension.
4108 : return aHaveWidth ? nsSize(aUnscaledWidth, aBgPositioningArea.height)
4109 0 : : nsSize(aBgPositioningArea.width, aUnscaledHeight);
4110 : }
4111 :
4112 : // Hardest case: only one auto. Prepare to negotiate amongst intrinsic
4113 : // dimensions, intrinsic ratio, *and* a specific background-size!
4114 0 : NS_ABORT_IF_FALSE((aLayerSize.mWidthType == nsStyleBackground::Size::eAuto) !=
4115 : (aLayerSize.mHeightType == nsStyleBackground::Size::eAuto),
4116 : "logic error");
4117 :
4118 0 : bool isAutoWidth = aLayerSize.mWidthType == nsStyleBackground::Size::eAuto;
4119 :
4120 0 : if (haveRatio) {
4121 : // Use the specified dimension, and compute the other from the ratio.
4122 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.width > 0,
4123 : "ratio width out of sync with width?");
4124 0 : NS_ABORT_IF_FALSE(aIntrinsicRatio.height > 0,
4125 : "ratio height out of sync with width?");
4126 0 : nsSize size;
4127 0 : if (isAutoWidth) {
4128 0 : size.height = aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea);
4129 : size.width =
4130 : NSCoordSaturatingNonnegativeMultiply(size.height,
4131 : double(aIntrinsicRatio.width) /
4132 0 : aIntrinsicRatio.height);
4133 : } else {
4134 0 : size.width = aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea);
4135 : size.height =
4136 : NSCoordSaturatingNonnegativeMultiply(size.width,
4137 : double(aIntrinsicRatio.height) /
4138 0 : aIntrinsicRatio.width);
4139 : }
4140 :
4141 0 : return size;
4142 : }
4143 :
4144 0 : NS_ABORT_IF_FALSE(!(aHaveWidth && aHaveHeight),
4145 : "if we have width and height, we must have had a ratio");
4146 :
4147 : // We have a specified dimension and an auto dimension, with no ratio to
4148 : // preserve. A specified dimension trumps all, so use that. For the other
4149 : // dimension, resolve auto to the intrinsic dimension (if present) or to 100%.
4150 0 : nsSize size;
4151 0 : if (isAutoWidth) {
4152 0 : size.width = aHaveWidth ? aUnscaledWidth : aBgPositioningArea.width;
4153 0 : size.height = aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea);
4154 : } else {
4155 0 : size.width = aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea);
4156 0 : size.height = aHaveHeight ? aUnscaledHeight : aBgPositioningArea.height;
4157 : }
4158 :
4159 0 : return size;
4160 : }
4161 :
4162 : /*
4163 : * The size returned by this method differs from the value of mSize, which this
4164 : * method also computes, in that mSize is the image's "preferred" size for this
4165 : * particular rendering, while the size returned here is the actual rendered
4166 : * size after accounting for background-size. The preferred size is most often
4167 : * the image's intrinsic dimensions. But for images with incomplete intrinsic
4168 : * dimensions, the preferred size varies, depending on the background
4169 : * positioning area, the specified background-size, and the intrinsic ratio and
4170 : * dimensions of the image (if it has them).
4171 : *
4172 : * This distinction is necessary because the components of a vector image are
4173 : * specified with respect to its preferred size for a rendering situation, not
4174 : * to its actual rendered size after background-size is applied. For example,
4175 : * consider a 4px wide vector image with no height which contains a left-aligned
4176 : * 2px wide black rectangle with height 100%. If the background-size width is
4177 : * auto (or 4px), the vector image will render 4px wide, and the black rectangle
4178 : * will be 2px wide. If the background-size width is 8px, the vector image will
4179 : * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
4180 : * In both cases mSize.width will be 4px; but in the first case the returned
4181 : * width will be 4px, while in the second case the returned width will be 8px.
4182 : */
4183 : nsSize
4184 0 : ImageRenderer::ComputeSize(const nsStyleBackground::Size& aLayerSize,
4185 : const nsSize& aBgPositioningArea)
4186 : {
4187 : bool haveWidth, haveHeight;
4188 0 : nsSize ratio;
4189 : nscoord unscaledWidth, unscaledHeight;
4190 : ComputeUnscaledDimensions(aBgPositioningArea,
4191 : unscaledWidth, haveWidth,
4192 : unscaledHeight, haveHeight,
4193 0 : ratio);
4194 : nsSize drawnSize = ComputeDrawnSize(aLayerSize, aBgPositioningArea,
4195 : unscaledWidth, haveWidth,
4196 : unscaledHeight, haveHeight,
4197 0 : ratio);
4198 0 : mSize.width = haveWidth ? unscaledWidth : drawnSize.width;
4199 0 : mSize.height = haveHeight ? unscaledHeight : drawnSize.height;
4200 : return drawnSize;
4201 : }
4202 :
4203 : void
4204 0 : ImageRenderer::Draw(nsPresContext* aPresContext,
4205 : nsRenderingContext& aRenderingContext,
4206 : const nsRect& aDest,
4207 : const nsRect& aFill,
4208 : const nsPoint& aAnchor,
4209 : const nsRect& aDirty)
4210 : {
4211 0 : if (!mIsReady) {
4212 0 : NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
4213 0 : return;
4214 : }
4215 :
4216 0 : if (aDest.IsEmpty() || aFill.IsEmpty() ||
4217 : mSize.width <= 0 || mSize.height <= 0)
4218 0 : return;
4219 :
4220 : gfxPattern::GraphicsFilter graphicsFilter =
4221 0 : nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
4222 :
4223 0 : switch (mType) {
4224 : case eStyleImageType_Image:
4225 : {
4226 : PRUint32 drawFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
4227 : ? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
4228 0 : : (PRUint32) imgIContainer::FLAG_NONE;
4229 : nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer,
4230 : nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
4231 : nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
4232 : graphicsFilter,
4233 0 : aDest, aFill, aAnchor, aDirty, drawFlags);
4234 0 : break;
4235 : }
4236 : case eStyleImageType_Gradient:
4237 : nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
4238 0 : mGradientData, aDirty, aDest, aFill);
4239 0 : break;
4240 : case eStyleImageType_Element:
4241 0 : if (mPaintServerFrame) {
4242 : nsSVGIntegrationUtils::DrawPaintServer(
4243 : &aRenderingContext, mForFrame, mPaintServerFrame, graphicsFilter,
4244 0 : aDest, aFill, aAnchor, aDirty, mSize);
4245 : } else {
4246 0 : NS_ASSERTION(mImageElementSurface.mSurface, "Surface should be ready.");
4247 : nsRefPtr<gfxDrawable> surfaceDrawable =
4248 : new gfxSurfaceDrawable(mImageElementSurface.mSurface,
4249 0 : mImageElementSurface.mSize);
4250 : nsLayoutUtils::DrawPixelSnapped(
4251 : &aRenderingContext, surfaceDrawable, graphicsFilter,
4252 0 : aDest, aFill, aAnchor, aDirty);
4253 : }
4254 0 : break;
4255 : case eStyleImageType_Null:
4256 : default:
4257 0 : break;
4258 : }
4259 : }
4260 :
4261 : #define MAX_BLUR_RADIUS 300
4262 : #define MAX_SPREAD_RADIUS 50
4263 :
4264 : static inline gfxIntSize
4265 0 : ComputeBlurRadius(nscoord aBlurRadius, PRInt32 aAppUnitsPerDevPixel)
4266 : {
4267 : // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
4268 : // standard deviation of the blur should be half the given blur value.
4269 : gfxFloat blurStdDev =
4270 : NS_MIN(gfxFloat(aBlurRadius) / gfxFloat(aAppUnitsPerDevPixel),
4271 0 : gfxFloat(MAX_BLUR_RADIUS))
4272 0 : / 2.0;
4273 : return
4274 0 : gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(blurStdDev, blurStdDev));
4275 : }
4276 :
4277 : // -----
4278 : // nsContextBoxBlur
4279 : // -----
4280 : gfxContext*
4281 0 : nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
4282 : nscoord aBlurRadius,
4283 : PRInt32 aAppUnitsPerDevPixel,
4284 : gfxContext* aDestinationCtx,
4285 : const nsRect& aDirtyRect,
4286 : const gfxRect* aSkipRect,
4287 : PRUint32 aFlags)
4288 : {
4289 0 : if (aRect.IsEmpty()) {
4290 0 : mContext = nsnull;
4291 0 : return nsnull;
4292 : }
4293 :
4294 0 : gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
4295 : PRInt32 spreadRadius = NS_MIN(PRInt32(aSpreadRadius / aAppUnitsPerDevPixel),
4296 0 : PRInt32(MAX_SPREAD_RADIUS));
4297 0 : mDestinationCtx = aDestinationCtx;
4298 :
4299 : // If not blurring, draw directly onto the destination device
4300 0 : if (blurRadius.width <= 0 && blurRadius.height <= 0 && spreadRadius <= 0 &&
4301 0 : !(aFlags & FORCE_MASK)) {
4302 0 : mContext = aDestinationCtx;
4303 0 : return mContext;
4304 : }
4305 :
4306 : // Convert from app units to device pixels
4307 0 : gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
4308 :
4309 : gfxRect dirtyRect =
4310 0 : nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
4311 0 : dirtyRect.RoundOut();
4312 :
4313 : // Create the temporary surface for blurring
4314 : mContext = blur.Init(rect, gfxIntSize(spreadRadius, spreadRadius),
4315 0 : blurRadius, &dirtyRect, aSkipRect);
4316 0 : return mContext;
4317 : }
4318 :
4319 : void
4320 0 : nsContextBoxBlur::DoPaint()
4321 : {
4322 0 : if (mContext == mDestinationCtx)
4323 0 : return;
4324 :
4325 0 : blur.Paint(mDestinationCtx);
4326 : }
4327 :
4328 : gfxContext*
4329 0 : nsContextBoxBlur::GetContext()
4330 : {
4331 0 : return mContext;
4332 : }
4333 :
4334 : /* static */ nsMargin
4335 0 : nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
4336 : PRInt32 aAppUnitsPerDevPixel)
4337 : {
4338 0 : gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
4339 :
4340 0 : nsMargin result;
4341 0 : result.top = blurRadius.height * aAppUnitsPerDevPixel;
4342 0 : result.right = blurRadius.width * aAppUnitsPerDevPixel;
4343 0 : result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
4344 0 : result.left = blurRadius.width * aAppUnitsPerDevPixel;
4345 : return result;
4346 : }
|