Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* utility functions for drawing borders and backgrounds */
8 :
9 : #include <ctime>
10 :
11 : #include "gfx2DGlue.h"
12 : #include "gfxContext.h"
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/DebugOnly.h"
15 : #include "mozilla/gfx/2D.h"
16 : #include "mozilla/gfx/Helpers.h"
17 : #include "mozilla/gfx/PathHelpers.h"
18 : #include "mozilla/HashFunctions.h"
19 : #include "mozilla/MathAlgorithms.h"
20 :
21 : #include "BorderConsts.h"
22 : #include "nsStyleConsts.h"
23 : #include "nsPresContext.h"
24 : #include "nsIFrame.h"
25 : #include "nsIFrameInlines.h"
26 : #include "nsPoint.h"
27 : #include "nsRect.h"
28 : #include "nsIPresShell.h"
29 : #include "nsFrameManager.h"
30 : #include "nsStyleContext.h"
31 : #include "nsGkAtoms.h"
32 : #include "nsCSSAnonBoxes.h"
33 : #include "nsIContent.h"
34 : #include "nsIDocumentInlines.h"
35 : #include "nsIScrollableFrame.h"
36 : #include "imgIRequest.h"
37 : #include "imgIContainer.h"
38 : #include "ImageOps.h"
39 : #include "nsCSSRendering.h"
40 : #include "nsCSSColorUtils.h"
41 : #include "nsITheme.h"
42 : #include "nsThemeConstants.h"
43 : #include "nsLayoutUtils.h"
44 : #include "nsBlockFrame.h"
45 : #include "nsStyleStructInlines.h"
46 : #include "nsCSSFrameConstructor.h"
47 : #include "nsCSSProps.h"
48 : #include "nsContentUtils.h"
49 : #include "nsSVGEffects.h"
50 : #include "nsSVGIntegrationUtils.h"
51 : #include "gfxDrawable.h"
52 : #include "GeckoProfiler.h"
53 : #include "nsCSSRenderingBorders.h"
54 : #include "mozilla/css/ImageLoader.h"
55 : #include "ImageContainer.h"
56 : #include "mozilla/Telemetry.h"
57 : #include "gfxUtils.h"
58 : #include "gfxGradientCache.h"
59 : #include "nsInlineFrame.h"
60 : #include "nsRubyTextContainerFrame.h"
61 : #include <algorithm>
62 : #include "SVGImageContext.h"
63 : #include "mozilla/layers/WebRenderDisplayItemLayer.h"
64 :
65 : using namespace mozilla;
66 : using namespace mozilla::css;
67 : using namespace mozilla::gfx;
68 : using namespace mozilla::image;
69 : using mozilla::CSSSizeOrRatio;
70 :
71 : static int gFrameTreeLockCount = 0;
72 :
73 : // To avoid storing this data on nsInlineFrame (bloat) and to avoid
74 : // recalculating this for each frame in a continuation (perf), hold
75 : // a cache of various coordinate information that we need in order
76 : // to paint inline backgrounds.
77 : struct InlineBackgroundData
78 : {
79 3 : InlineBackgroundData()
80 3 : : mFrame(nullptr), mLineContainer(nullptr)
81 : {
82 3 : }
83 :
84 0 : ~InlineBackgroundData()
85 0 : {
86 0 : }
87 :
88 35 : void Reset()
89 : {
90 35 : mBoundingBox.SetRect(0,0,0,0);
91 35 : mContinuationPoint = mLineContinuationPoint = mUnbrokenMeasure = 0;
92 35 : mFrame = mLineContainer = nullptr;
93 35 : mPIStartBorderData.Reset();
94 35 : }
95 :
96 : /**
97 : * Return a continuous rect for (an inline) aFrame relative to the
98 : * continuation that draws the left-most part of the background.
99 : * This is used when painting backgrounds.
100 : */
101 0 : nsRect GetContinuousRect(nsIFrame* aFrame)
102 : {
103 0 : MOZ_ASSERT(static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)));
104 :
105 0 : SetFrame(aFrame);
106 :
107 : nscoord pos; // an x coordinate if writing-mode is horizontal;
108 : // y coordinate if vertical
109 0 : if (mBidiEnabled) {
110 0 : pos = mLineContinuationPoint;
111 :
112 : // Scan continuations on the same line as aFrame and accumulate the widths
113 : // of frames that are to the left (if this is an LTR block) or right
114 : // (if it's RTL) of the current one.
115 0 : bool isRtlBlock = (mLineContainer->StyleVisibility()->mDirection ==
116 0 : NS_STYLE_DIRECTION_RTL);
117 0 : nscoord curOffset = mVertical ? aFrame->GetOffsetTo(mLineContainer).y
118 0 : : aFrame->GetOffsetTo(mLineContainer).x;
119 :
120 : // If the continuation is fluid we know inlineFrame is not on the same line.
121 : // If it's not fluid, we need to test further to be sure.
122 0 : nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
123 0 : while (inlineFrame && !inlineFrame->GetNextInFlow() &&
124 0 : AreOnSameLine(aFrame, inlineFrame)) {
125 0 : nscoord frameOffset = mVertical
126 0 : ? inlineFrame->GetOffsetTo(mLineContainer).y
127 0 : : inlineFrame->GetOffsetTo(mLineContainer).x;
128 0 : if (isRtlBlock == (frameOffset >= curOffset)) {
129 0 : pos += mVertical
130 0 : ? inlineFrame->GetSize().height
131 0 : : inlineFrame->GetSize().width;
132 : }
133 0 : inlineFrame = inlineFrame->GetPrevContinuation();
134 : }
135 :
136 0 : inlineFrame = aFrame->GetNextContinuation();
137 0 : while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
138 0 : AreOnSameLine(aFrame, inlineFrame)) {
139 0 : nscoord frameOffset = mVertical
140 0 : ? inlineFrame->GetOffsetTo(mLineContainer).y
141 0 : : inlineFrame->GetOffsetTo(mLineContainer).x;
142 0 : if (isRtlBlock == (frameOffset >= curOffset)) {
143 0 : pos += mVertical
144 0 : ? inlineFrame->GetSize().height
145 0 : : inlineFrame->GetSize().width;
146 : }
147 0 : inlineFrame = inlineFrame->GetNextContinuation();
148 : }
149 0 : if (isRtlBlock) {
150 : // aFrame itself is also to the right of its left edge, so add its width.
151 0 : pos += mVertical ? aFrame->GetSize().height : aFrame->GetSize().width;
152 : // pos is now the distance from the left [top] edge of aFrame to the right [bottom] edge
153 : // of the unbroken content. Change it to indicate the distance from the
154 : // left [top] edge of the unbroken content to the left [top] edge of aFrame.
155 0 : pos = mUnbrokenMeasure - pos;
156 : }
157 : } else {
158 0 : pos = mContinuationPoint;
159 : }
160 :
161 : // Assume background-origin: border and return a rect with offsets
162 : // relative to (0,0). If we have a different background-origin,
163 : // then our rect should be deflated appropriately by our caller.
164 0 : return mVertical
165 0 : ? nsRect(0, -pos, mFrame->GetSize().width, mUnbrokenMeasure)
166 0 : : nsRect(-pos, 0, mUnbrokenMeasure, mFrame->GetSize().height);
167 : }
168 :
169 : /**
170 : * Return a continuous rect for (an inline) aFrame relative to the
171 : * continuation that should draw the left[top]-border. This is used when painting
172 : * borders and clipping backgrounds. This may NOT be the same continuous rect
173 : * as for drawing backgrounds; the continuation with the left[top]-border might be
174 : * somewhere in the middle of that rect (e.g. BIDI), in those cases we need
175 : * the reverse background order starting at the left[top]-border continuation.
176 : */
177 0 : nsRect GetBorderContinuousRect(nsIFrame* aFrame, nsRect aBorderArea)
178 : {
179 : // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
180 : // resets our mPIStartBorderData so we save it ...
181 0 : PhysicalInlineStartBorderData saved(mPIStartBorderData);
182 0 : nsRect joinedBorderArea = GetContinuousRect(aFrame);
183 0 : if (!saved.mIsValid || saved.mFrame != mPIStartBorderData.mFrame) {
184 0 : if (aFrame == mPIStartBorderData.mFrame) {
185 0 : if (mVertical) {
186 0 : mPIStartBorderData.SetCoord(joinedBorderArea.y);
187 : } else {
188 0 : mPIStartBorderData.SetCoord(joinedBorderArea.x);
189 : }
190 0 : } else if (mPIStartBorderData.mFrame) {
191 0 : if (mVertical) {
192 0 : mPIStartBorderData.SetCoord(GetContinuousRect(mPIStartBorderData.mFrame).y);
193 : } else {
194 0 : mPIStartBorderData.SetCoord(GetContinuousRect(mPIStartBorderData.mFrame).x);
195 : }
196 : }
197 : } else {
198 : // ... and restore it when possible.
199 0 : mPIStartBorderData.mCoord = saved.mCoord;
200 : }
201 0 : if (mVertical) {
202 0 : if (joinedBorderArea.y > mPIStartBorderData.mCoord) {
203 0 : joinedBorderArea.y =
204 0 : -(mUnbrokenMeasure + joinedBorderArea.y - aBorderArea.height);
205 : } else {
206 0 : joinedBorderArea.y -= mPIStartBorderData.mCoord;
207 : }
208 : } else {
209 0 : if (joinedBorderArea.x > mPIStartBorderData.mCoord) {
210 0 : joinedBorderArea.x =
211 0 : -(mUnbrokenMeasure + joinedBorderArea.x - aBorderArea.width);
212 : } else {
213 0 : joinedBorderArea.x -= mPIStartBorderData.mCoord;
214 : }
215 : }
216 0 : return joinedBorderArea;
217 : }
218 :
219 : nsRect GetBoundingRect(nsIFrame* aFrame)
220 : {
221 : SetFrame(aFrame);
222 :
223 : // Move the offsets relative to (0,0) which puts the bounding box into
224 : // our coordinate system rather than our parent's. We do this by
225 : // moving it the back distance from us to the bounding box.
226 : // This also assumes background-origin: border, so our caller will
227 : // need to deflate us if needed.
228 : nsRect boundingBox(mBoundingBox);
229 : nsPoint point = mFrame->GetPosition();
230 : boundingBox.MoveBy(-point.x, -point.y);
231 :
232 : return boundingBox;
233 : }
234 :
235 : protected:
236 : // This is a coordinate on the inline axis, but is not a true logical inline-
237 : // coord because it is always measured from left to right (if horizontal) or
238 : // from top to bottom (if vertical), ignoring any bidi RTL directionality.
239 : // We'll call this "physical inline start", or PIStart for short.
240 : struct PhysicalInlineStartBorderData {
241 : nsIFrame* mFrame; // the continuation that may have a left-border
242 : nscoord mCoord; // cached GetContinuousRect(mFrame).x or .y
243 : bool mIsValid; // true if mCoord is valid
244 35 : void Reset() { mFrame = nullptr; mIsValid = false; }
245 0 : void SetCoord(nscoord aCoord) { mCoord = aCoord; mIsValid = true; }
246 : };
247 :
248 : nsIFrame* mFrame;
249 : nsIFrame* mLineContainer;
250 : nsRect mBoundingBox;
251 : nscoord mContinuationPoint;
252 : nscoord mUnbrokenMeasure;
253 : nscoord mLineContinuationPoint;
254 : PhysicalInlineStartBorderData mPIStartBorderData;
255 : bool mBidiEnabled;
256 : bool mVertical;
257 :
258 0 : void SetFrame(nsIFrame* aFrame)
259 : {
260 0 : NS_PRECONDITION(aFrame, "Need a frame");
261 0 : NS_ASSERTION(gFrameTreeLockCount > 0,
262 : "Can't call this when frame tree is not locked");
263 :
264 0 : if (aFrame == mFrame) {
265 0 : return;
266 : }
267 :
268 0 : nsIFrame *prevContinuation = GetPrevContinuation(aFrame);
269 :
270 0 : if (!prevContinuation || mFrame != prevContinuation) {
271 : // Ok, we've got the wrong frame. We have to start from scratch.
272 0 : Reset();
273 0 : Init(aFrame);
274 0 : return;
275 : }
276 :
277 : // Get our last frame's size and add its width to our continuation
278 : // point before we cache the new frame.
279 0 : mContinuationPoint += mVertical ? mFrame->GetSize().height
280 0 : : mFrame->GetSize().width;
281 :
282 : // If this a new line, update mLineContinuationPoint.
283 0 : if (mBidiEnabled &&
284 0 : (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
285 0 : mLineContinuationPoint = mContinuationPoint;
286 : }
287 :
288 0 : mFrame = aFrame;
289 : }
290 :
291 0 : nsIFrame* GetPrevContinuation(nsIFrame* aFrame)
292 : {
293 0 : nsIFrame* prevCont = aFrame->GetPrevContinuation();
294 0 : if (!prevCont &&
295 0 : (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
296 : nsIFrame* block =
297 0 : aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
298 0 : if (block) {
299 : // The {ib} properties are only stored on first continuations
300 0 : NS_ASSERTION(!block->GetPrevContinuation(),
301 : "Incorrect value for IBSplitPrevSibling");
302 : prevCont =
303 0 : block->GetProperty(nsIFrame::IBSplitPrevSibling());
304 0 : NS_ASSERTION(prevCont, "How did that happen?");
305 : }
306 : }
307 0 : return prevCont;
308 : }
309 :
310 0 : nsIFrame* GetNextContinuation(nsIFrame* aFrame)
311 : {
312 0 : nsIFrame* nextCont = aFrame->GetNextContinuation();
313 0 : if (!nextCont &&
314 0 : (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
315 : // The {ib} properties are only stored on first continuations
316 0 : aFrame = aFrame->FirstContinuation();
317 0 : nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitSibling());
318 0 : if (block) {
319 0 : nextCont = block->GetProperty(nsIFrame::IBSplitSibling());
320 0 : NS_ASSERTION(nextCont, "How did that happen?");
321 : }
322 : }
323 0 : return nextCont;
324 : }
325 :
326 0 : void Init(nsIFrame* aFrame)
327 : {
328 0 : mPIStartBorderData.Reset();
329 0 : mBidiEnabled = aFrame->PresContext()->BidiEnabled();
330 0 : if (mBidiEnabled) {
331 : // Find the line container frame
332 0 : mLineContainer = aFrame;
333 0 : while (mLineContainer &&
334 0 : mLineContainer->IsFrameOfType(nsIFrame::eLineParticipant)) {
335 0 : mLineContainer = mLineContainer->GetParent();
336 : }
337 :
338 0 : MOZ_ASSERT(mLineContainer, "Cannot find line containing frame.");
339 0 : MOZ_ASSERT(mLineContainer != aFrame, "line container frame "
340 : "should be an ancestor of the target frame.");
341 : }
342 :
343 0 : mVertical = aFrame->GetWritingMode().IsVertical();
344 :
345 : // Start with the previous flow frame as our continuation point
346 : // is the total of the widths of the previous frames.
347 0 : nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
348 0 : while (inlineFrame) {
349 0 : if (!mPIStartBorderData.mFrame &&
350 0 : !(mVertical ? inlineFrame->GetSkipSides().Top()
351 0 : : inlineFrame->GetSkipSides().Left())) {
352 0 : mPIStartBorderData.mFrame = inlineFrame;
353 : }
354 0 : nsRect rect = inlineFrame->GetRect();
355 0 : mContinuationPoint += mVertical ? rect.height : rect.width;
356 0 : if (mBidiEnabled && !AreOnSameLine(aFrame, inlineFrame)) {
357 0 : mLineContinuationPoint += mVertical ? rect.height : rect.width;
358 : }
359 0 : mUnbrokenMeasure += mVertical ? rect.height : rect.width;
360 0 : mBoundingBox.UnionRect(mBoundingBox, rect);
361 0 : inlineFrame = GetPrevContinuation(inlineFrame);
362 : }
363 :
364 : // Next add this frame and subsequent frames to the bounding box and
365 : // unbroken width.
366 0 : inlineFrame = aFrame;
367 0 : while (inlineFrame) {
368 0 : if (!mPIStartBorderData.mFrame &&
369 0 : !(mVertical ? inlineFrame->GetSkipSides().Top()
370 0 : : inlineFrame->GetSkipSides().Left())) {
371 0 : mPIStartBorderData.mFrame = inlineFrame;
372 : }
373 0 : nsRect rect = inlineFrame->GetRect();
374 0 : mUnbrokenMeasure += mVertical ? rect.height : rect.width;
375 0 : mBoundingBox.UnionRect(mBoundingBox, rect);
376 0 : inlineFrame = GetNextContinuation(inlineFrame);
377 : }
378 :
379 0 : mFrame = aFrame;
380 0 : }
381 :
382 0 : bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
383 0 : if (nsBlockFrame* blockFrame = do_QueryFrame(mLineContainer)) {
384 : bool isValid1, isValid2;
385 0 : nsBlockInFlowLineIterator it1(blockFrame, aFrame1, &isValid1);
386 0 : nsBlockInFlowLineIterator it2(blockFrame, aFrame2, &isValid2);
387 0 : return isValid1 && isValid2 &&
388 : // Make sure aFrame1 and aFrame2 are in the same continuation of
389 : // blockFrame.
390 0 : it1.GetContainer() == it2.GetContainer() &&
391 : // And on the same line in it
392 0 : it1.GetLine() == it2.GetLine();
393 : }
394 0 : if (nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(mLineContainer)) {
395 0 : nsBlockFrame* block = nsLayoutUtils::FindNearestBlockAncestor(rtcFrame);
396 : // Ruby text container can only hold one line of text, so if they
397 : // are in the same continuation, they are in the same line. Since
398 : // ruby text containers are bidi isolate, they are never split for
399 : // bidi reordering, which means being in different continuation
400 : // indicates being in different lines.
401 0 : for (nsIFrame* frame = rtcFrame->FirstContinuation();
402 0 : frame; frame = frame->GetNextContinuation()) {
403 : bool isDescendant1 =
404 0 : nsLayoutUtils::IsProperAncestorFrame(frame, aFrame1, block);
405 : bool isDescendant2 =
406 0 : nsLayoutUtils::IsProperAncestorFrame(frame, aFrame2, block);
407 0 : if (isDescendant1 && isDescendant2) {
408 0 : return true;
409 : }
410 0 : if (isDescendant1 || isDescendant2) {
411 0 : return false;
412 : }
413 : }
414 0 : MOZ_ASSERT_UNREACHABLE("None of the frames is a descendant of this rtc?");
415 : }
416 0 : MOZ_ASSERT_UNREACHABLE("Do we have any other type of line container?");
417 : return false;
418 : }
419 : };
420 :
421 : /* Local functions */
422 : static nscolor MakeBevelColor(mozilla::Side whichSide, uint8_t style,
423 : nscolor aBackgroundColor,
424 : nscolor aBorderColor);
425 :
426 : static InlineBackgroundData* gInlineBGData = nullptr;
427 :
428 : // Initialize any static variables used by nsCSSRendering.
429 3 : void nsCSSRendering::Init()
430 : {
431 3 : NS_ASSERTION(!gInlineBGData, "Init called twice");
432 3 : gInlineBGData = new InlineBackgroundData();
433 3 : }
434 :
435 : // Clean up any global variables used by nsCSSRendering.
436 0 : void nsCSSRendering::Shutdown()
437 : {
438 0 : delete gInlineBGData;
439 0 : gInlineBGData = nullptr;
440 0 : }
441 :
442 : /**
443 : * Make a bevel color
444 : */
445 : static nscolor
446 0 : MakeBevelColor(mozilla::Side whichSide, uint8_t style,
447 : nscolor aBackgroundColor, nscolor aBorderColor)
448 : {
449 :
450 : nscolor colors[2];
451 : nscolor theColor;
452 :
453 : // Given a background color and a border color
454 : // calculate the color used for the shading
455 0 : NS_GetSpecial3DColors(colors, aBackgroundColor, aBorderColor);
456 :
457 0 : if ((style == NS_STYLE_BORDER_STYLE_OUTSET) ||
458 : (style == NS_STYLE_BORDER_STYLE_RIDGE)) {
459 : // Flip colors for these two border styles
460 0 : switch (whichSide) {
461 0 : case eSideBottom: whichSide = eSideTop; break;
462 0 : case eSideRight: whichSide = eSideLeft; break;
463 0 : case eSideTop: whichSide = eSideBottom; break;
464 0 : case eSideLeft: whichSide = eSideRight; break;
465 : }
466 : }
467 :
468 0 : switch (whichSide) {
469 : case eSideBottom:
470 0 : theColor = colors[1];
471 0 : break;
472 : case eSideRight:
473 0 : theColor = colors[1];
474 0 : break;
475 : case eSideTop:
476 0 : theColor = colors[0];
477 0 : break;
478 : case eSideLeft:
479 : default:
480 0 : theColor = colors[0];
481 0 : break;
482 : }
483 0 : return theColor;
484 : }
485 :
486 : static bool
487 391 : GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
488 : const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
489 : nscoord aRadii[8])
490 : {
491 : bool haveRoundedCorners;
492 391 : nsSize sz = aBorderArea.Size();
493 391 : nsSize frameSize = aForFrame->GetSize();
494 1564 : if (&aBorder == aForFrame->StyleBorder() &&
495 1564 : frameSize == aOrigBorderArea.Size()) {
496 391 : haveRoundedCorners = aForFrame->GetBorderRadii(sz, sz, Sides(), aRadii);
497 : } else {
498 : haveRoundedCorners =
499 0 : nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius, frameSize, sz, Sides(), aRadii);
500 : }
501 :
502 391 : return haveRoundedCorners;
503 : }
504 :
505 : static bool
506 34 : GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
507 : const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
508 : RectCornerRadii* aBgRadii)
509 : {
510 : nscoord radii[8];
511 34 : bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aOrigBorderArea, aBorderArea, radii);
512 :
513 34 : if (haveRoundedCorners) {
514 17 : auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
515 17 : nsCSSRendering::ComputePixelRadii(radii, d2a, aBgRadii);
516 : }
517 34 : return haveRoundedCorners;
518 : }
519 :
520 : static nsRect
521 0 : JoinBoxesForBlockAxisSlice(nsIFrame* aFrame, const nsRect& aBorderArea)
522 : {
523 : // Inflate the block-axis size as if our continuations were laid out
524 : // adjacent in that axis. Note that we don't touch the inline size.
525 0 : nsRect borderArea = aBorderArea;
526 0 : nscoord bSize = 0;
527 0 : auto wm = aFrame->GetWritingMode();
528 0 : nsIFrame* f = aFrame->GetNextContinuation();
529 0 : for (; f; f = f->GetNextContinuation()) {
530 0 : MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
531 : "anonymous ib-split block shouldn't have border/background");
532 0 : bSize += f->BSize(wm);
533 : }
534 0 : (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
535 0 : bSize = 0;
536 0 : f = aFrame->GetPrevContinuation();
537 0 : for (; f; f = f->GetPrevContinuation()) {
538 0 : MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
539 : "anonymous ib-split block shouldn't have border/background");
540 0 : bSize += f->BSize(wm);
541 : }
542 0 : (wm.IsVertical() ? borderArea.x : borderArea.y) -= bSize;
543 0 : (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
544 0 : return borderArea;
545 : }
546 :
547 : /**
548 : * Inflate aBorderArea which is relative to aFrame's origin to calculate
549 : * a hypothetical non-split frame area for all the continuations.
550 : * See "Joining Boxes for 'slice'" in
551 : * http://dev.w3.org/csswg/css-break/#break-decoration
552 : */
553 : enum InlineBoxOrder { eForBorder, eForBackground };
554 : static nsRect
555 0 : JoinBoxesForSlice(nsIFrame* aFrame, const nsRect& aBorderArea,
556 : InlineBoxOrder aOrder)
557 : {
558 0 : if (static_cast<nsInlineFrame*>(do_QueryFrame(aFrame))) {
559 : return (aOrder == eForBorder
560 0 : ? gInlineBGData->GetBorderContinuousRect(aFrame, aBorderArea)
561 : : gInlineBGData->GetContinuousRect(aFrame)) +
562 0 : aBorderArea.TopLeft();
563 : }
564 0 : return JoinBoxesForBlockAxisSlice(aFrame, aBorderArea);
565 : }
566 :
567 : static bool
568 769 : IsBoxDecorationSlice(const nsStyleBorder& aStyleBorder)
569 : {
570 769 : return aStyleBorder.mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
571 : }
572 :
573 : static nsRect
574 479 : BoxDecorationRectForBorder(nsIFrame* aFrame, const nsRect& aBorderArea,
575 : Sides aSkipSides,
576 : const nsStyleBorder* aStyleBorder = nullptr)
577 : {
578 479 : if (!aStyleBorder) {
579 29 : aStyleBorder = aFrame->StyleBorder();
580 : }
581 : // If aSkipSides.IsEmpty() then there are no continuations, or it's
582 : // a ::first-letter that wants all border sides on the first continuation.
583 958 : return ::IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
584 479 : ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBorder)
585 0 : : aBorderArea;
586 : }
587 :
588 : static nsRect
589 253 : BoxDecorationRectForBackground(nsIFrame* aFrame, const nsRect& aBorderArea,
590 : Sides aSkipSides,
591 : const nsStyleBorder* aStyleBorder = nullptr)
592 : {
593 253 : if (!aStyleBorder) {
594 194 : aStyleBorder = aFrame->StyleBorder();
595 : }
596 : // If aSkipSides.IsEmpty() then there are no continuations, or it's
597 : // a ::first-letter that wants all border sides on the first continuation.
598 506 : return ::IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
599 253 : ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBackground)
600 0 : : aBorderArea;
601 : }
602 :
603 : //----------------------------------------------------------------------
604 : // Thebes Border Rendering Code Start
605 :
606 : /*
607 : * Compute the float-pixel radii that should be used for drawing
608 : * this border/outline, given the various input bits.
609 : */
610 : /* static */ void
611 119 : nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
612 : nscoord aAppUnitsPerPixel,
613 : RectCornerRadii *oBorderRadii)
614 : {
615 : Float radii[8];
616 1071 : NS_FOR_CSS_HALF_CORNERS(corner)
617 952 : radii[corner] = Float(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
618 :
619 119 : (*oBorderRadii)[C_TL] = Size(radii[eCornerTopLeftX],
620 : radii[eCornerTopLeftY]);
621 119 : (*oBorderRadii)[C_TR] = Size(radii[eCornerTopRightX],
622 : radii[eCornerTopRightY]);
623 119 : (*oBorderRadii)[C_BR] = Size(radii[eCornerBottomRightX],
624 : radii[eCornerBottomRightY]);
625 119 : (*oBorderRadii)[C_BL] = Size(radii[eCornerBottomLeftX],
626 : radii[eCornerBottomLeftY]);
627 119 : }
628 :
629 : DrawResult
630 37 : nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
631 : gfxContext& aRenderingContext,
632 : nsIFrame* aForFrame,
633 : const nsRect& aDirtyRect,
634 : const nsRect& aBorderArea,
635 : nsStyleContext* aStyleContext,
636 : PaintBorderFlags aFlags,
637 : Sides aSkipSides)
638 : {
639 74 : AUTO_PROFILER_LABEL("nsCSSRendering::PaintBorder", GRAPHICS);
640 :
641 37 : nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
642 37 : const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
643 : // Don't check RelevantLinkVisited here, since we want to take the
644 : // same amount of time whether or not it's true.
645 37 : if (!styleIfVisited) {
646 : return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
647 : aDirtyRect, aBorderArea, *styleBorder,
648 37 : aStyleContext, aFlags, aSkipSides);
649 : }
650 :
651 0 : nsStyleBorder newStyleBorder(*styleBorder);
652 :
653 0 : NS_FOR_CSS_SIDES(side) {
654 : nscolor color = aStyleContext->
655 0 : GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
656 0 : newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
657 : }
658 : return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
659 : aDirtyRect, aBorderArea, newStyleBorder,
660 0 : aStyleContext, aFlags, aSkipSides);
661 : }
662 :
663 : Maybe<nsCSSBorderRenderer>
664 0 : nsCSSRendering::CreateBorderRenderer(nsPresContext* aPresContext,
665 : DrawTarget* aDrawTarget,
666 : nsIFrame* aForFrame,
667 : const nsRect& aDirtyRect,
668 : const nsRect& aBorderArea,
669 : nsStyleContext* aStyleContext,
670 : Sides aSkipSides)
671 : {
672 0 : nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
673 0 : const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
674 : // Don't check RelevantLinkVisited here, since we want to take the
675 : // same amount of time whether or not it's true.
676 0 : if (!styleIfVisited) {
677 : return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
678 : aForFrame, aDirtyRect,
679 : aBorderArea, *styleBorder,
680 0 : aStyleContext, aSkipSides);
681 : }
682 :
683 0 : nsStyleBorder newStyleBorder(*styleBorder);
684 :
685 0 : NS_FOR_CSS_SIDES(side) {
686 : nscolor color = aStyleContext->
687 0 : GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
688 0 : newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
689 : }
690 : return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
691 : aForFrame, aDirtyRect, aBorderArea,
692 : newStyleBorder, aStyleContext,
693 0 : aSkipSides);
694 : }
695 :
696 : nsCSSBorderRenderer
697 34 : ConstructBorderRenderer(nsPresContext* aPresContext,
698 : nsStyleContext* aStyleContext,
699 : DrawTarget* aDrawTarget,
700 : nsIFrame* aForFrame,
701 : const nsRect& aDirtyRect,
702 : const nsRect& aBorderArea,
703 : const nsStyleBorder& aStyleBorder,
704 : Sides aSkipSides,
705 : bool* aNeedsClip)
706 : {
707 34 : nsMargin border = aStyleBorder.GetComputedBorder();
708 :
709 : // Get our style context's color struct.
710 34 : const nsStyleColor* ourColor = aStyleContext->StyleColor();
711 :
712 : // In NavQuirks mode we want to use the parent's context as a starting point
713 : // for determining the background color.
714 34 : bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
715 34 : nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame, quirks);
716 34 : nsStyleContext* bgContext = bgFrame->StyleContext();
717 : nscolor bgColor = bgContext->
718 34 : GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
719 :
720 : // Compute the outermost boundary of the area that might be painted.
721 : // Same coordinate space as aBorderArea & aBGClipRect.
722 : nsRect joinedBorderArea =
723 68 : ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
724 34 : RectCornerRadii bgRadii;
725 34 : ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
726 :
727 34 : PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x, joinedBorderArea.y,
728 34 : joinedBorderArea.width, joinedBorderArea.height);
729 :
730 : // start drawing
731 34 : if (::IsBoxDecorationSlice(aStyleBorder)) {
732 34 : if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
733 : // No need for a clip, just skip the sides we don't want.
734 34 : border.ApplySkipSides(aSkipSides);
735 : } else {
736 : // We're drawing borders around the joined continuation boxes so we need
737 : // to clip that to the slice that we want for this frame.
738 0 : *aNeedsClip = true;
739 : }
740 : } else {
741 0 : MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
742 : "Should use aBorderArea for box-decoration-break:clone");
743 0 : MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty() ||
744 : IS_TRUE_OVERFLOW_CONTAINER(aForFrame),
745 : "Should not skip sides for box-decoration-break:clone except "
746 : "::first-letter/line continuations or other frame types that "
747 : "don't have borders but those shouldn't reach this point. "
748 : "Overflow containers do reach this point though.");
749 0 : border.ApplySkipSides(aSkipSides);
750 : }
751 :
752 : // Convert to dev pixels.
753 34 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
754 34 : Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, twipsPerPixel);
755 34 : Float borderWidths[4] = { Float(border.top) / twipsPerPixel,
756 34 : Float(border.right) / twipsPerPixel,
757 34 : Float(border.bottom) / twipsPerPixel,
758 102 : Float(border.left) / twipsPerPixel };
759 34 : Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
760 :
761 : uint8_t borderStyles[4];
762 : nscolor borderColors[4];
763 : nsBorderColors* compositeColors[4];
764 :
765 : // pull out styles, colors, composite colors
766 170 : NS_FOR_CSS_SIDES (i) {
767 136 : borderStyles[i] = aStyleBorder.GetBorderStyle(i);
768 136 : borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
769 136 : aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
770 : }
771 :
772 34 : PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
773 :
774 34 : nsIDocument* document = nullptr;
775 34 : nsIContent* content = aForFrame->GetContent();
776 34 : if (content) {
777 34 : document = content->OwnerDoc();
778 : }
779 :
780 : return nsCSSBorderRenderer(aPresContext,
781 : document,
782 : aDrawTarget,
783 : dirtyRect,
784 : joinedBorderAreaPx,
785 : borderStyles,
786 : borderWidths,
787 : bgRadii,
788 : borderColors,
789 : compositeColors,
790 68 : bgColor);
791 : }
792 :
793 :
794 : DrawResult
795 37 : nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
796 : gfxContext& aRenderingContext,
797 : nsIFrame* aForFrame,
798 : const nsRect& aDirtyRect,
799 : const nsRect& aBorderArea,
800 : const nsStyleBorder& aStyleBorder,
801 : nsStyleContext* aStyleContext,
802 : PaintBorderFlags aFlags,
803 : Sides aSkipSides)
804 : {
805 37 : DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
806 :
807 37 : PrintAsStringNewline("++ PaintBorder");
808 :
809 : // Check to see if we have an appearance defined. If so, we let the theme
810 : // renderer draw the border. DO not get the data from aForFrame, since the passed in style context
811 : // may be different! Always use |aStyleContext|!
812 37 : const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
813 37 : if (displayData->mAppearance) {
814 0 : nsITheme *theme = aPresContext->GetTheme();
815 0 : if (theme &&
816 0 : theme->ThemeSupportsWidget(aPresContext, aForFrame,
817 0 : displayData->mAppearance)) {
818 0 : return DrawResult::SUCCESS; // Let the theme handle it.
819 : }
820 : }
821 :
822 37 : if (!aStyleBorder.mBorderImageSource.IsEmpty()) {
823 3 : DrawResult result = DrawResult::SUCCESS;
824 :
825 3 : uint32_t irFlags = 0;
826 3 : if (aFlags & PaintBorderFlags::SYNC_DECODE_IMAGES) {
827 0 : irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
828 : }
829 :
830 : // Creating the border image renderer will request a decode, and we rely on
831 : // that happening.
832 : Maybe<nsCSSBorderImageRenderer> renderer =
833 : nsCSSBorderImageRenderer::CreateBorderImageRenderer(aPresContext, aForFrame, aBorderArea,
834 : aStyleBorder, aDirtyRect, aSkipSides,
835 3 : irFlags, &result);
836 3 : if (aStyleBorder.IsBorderImageLoaded()) {
837 3 : if (!renderer) {
838 0 : return result;
839 : }
840 :
841 3 : return renderer->DrawBorderImage(aPresContext, aRenderingContext,
842 3 : aForFrame, aDirtyRect);
843 : }
844 : }
845 :
846 34 : DrawResult result = DrawResult::SUCCESS;
847 :
848 : // If we had a border-image, but it wasn't loaded, then we should return
849 : // DrawResult::NOT_READY; we'll want to try again if we do a paint with sync
850 : // decoding enabled.
851 34 : if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
852 0 : result = DrawResult::NOT_READY;
853 : }
854 :
855 34 : nsMargin border = aStyleBorder.GetComputedBorder();
856 41 : if (0 == border.left && 0 == border.right &&
857 9 : 0 == border.top && 0 == border.bottom) {
858 : // Empty border area
859 0 : return result;
860 : }
861 :
862 34 : bool needsClip = false;
863 : nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
864 : aStyleContext,
865 : &aDrawTarget,
866 : aForFrame,
867 : aDirtyRect,
868 : aBorderArea,
869 : aStyleBorder,
870 : aSkipSides,
871 34 : &needsClip);
872 34 : if (needsClip) {
873 : aDrawTarget.PushClipRect(
874 0 : NSRectToSnappedRect(aBorderArea,
875 0 : aForFrame->PresContext()->AppUnitsPerDevPixel(),
876 0 : aDrawTarget));
877 : }
878 :
879 34 : br.DrawBorders();
880 :
881 34 : if (needsClip) {
882 0 : aDrawTarget.PopClip();
883 : }
884 :
885 34 : PrintAsStringNewline();
886 :
887 34 : return result;
888 : }
889 :
890 : Maybe<nsCSSBorderRenderer>
891 0 : nsCSSRendering::CreateBorderRendererWithStyleBorder(nsPresContext* aPresContext,
892 : DrawTarget* aDrawTarget,
893 : nsIFrame* aForFrame,
894 : const nsRect& aDirtyRect,
895 : const nsRect& aBorderArea,
896 : const nsStyleBorder& aStyleBorder,
897 : nsStyleContext* aStyleContext,
898 : Sides aSkipSides)
899 : {
900 0 : const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
901 0 : if (displayData->mAppearance) {
902 0 : nsITheme *theme = aPresContext->GetTheme();
903 0 : if (theme &&
904 0 : theme->ThemeSupportsWidget(aPresContext, aForFrame,
905 0 : displayData->mAppearance)) {
906 0 : return Nothing();
907 : }
908 : }
909 :
910 0 : if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
911 0 : return Nothing();
912 : }
913 :
914 0 : nsMargin border = aStyleBorder.GetComputedBorder();
915 0 : if (0 == border.left && 0 == border.right &&
916 0 : 0 == border.top && 0 == border.bottom) {
917 : // Empty border area
918 0 : return Nothing();
919 : }
920 :
921 0 : bool needsClip = false;
922 : nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
923 : aStyleContext,
924 : aDrawTarget,
925 : aForFrame,
926 : aDirtyRect,
927 : aBorderArea,
928 : aStyleBorder,
929 : aSkipSides,
930 0 : &needsClip);
931 0 : if (needsClip) {
932 0 : return Nothing();
933 : }
934 0 : return Some(br);
935 : }
936 :
937 : static nsRect
938 0 : GetOutlineInnerRect(nsIFrame* aFrame)
939 : {
940 : nsRect* savedOutlineInnerRect =
941 0 : aFrame->GetProperty(nsIFrame::OutlineInnerRectProperty());
942 0 : if (savedOutlineInnerRect)
943 0 : return *savedOutlineInnerRect;
944 0 : NS_NOTREACHED("we should have saved a frame property");
945 0 : return nsRect(nsPoint(0, 0), aFrame->GetSize());
946 : }
947 :
948 : Maybe<nsCSSBorderRenderer>
949 0 : nsCSSRendering::CreateBorderRendererForOutline(nsPresContext* aPresContext,
950 : gfxContext* aRenderingContext,
951 : nsIFrame* aForFrame,
952 : const nsRect& aDirtyRect,
953 : const nsRect& aBorderArea,
954 : nsStyleContext* aStyleContext)
955 : {
956 : nscoord twipsRadii[8];
957 :
958 : // Get our style context's color struct.
959 0 : const nsStyleOutline* ourOutline = aStyleContext->StyleOutline();
960 :
961 0 : if (!ourOutline->ShouldPaintOutline()) {
962 : // Empty outline
963 0 : return Nothing();
964 : }
965 :
966 : nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
967 0 : (aForFrame, false);
968 0 : nsStyleContext* bgContext = bgFrame->StyleContext();
969 : nscolor bgColor = bgContext->
970 0 : GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
971 :
972 0 : nsRect innerRect;
973 0 : if (
974 : #ifdef MOZ_XUL
975 0 : aStyleContext->GetPseudoType() == CSSPseudoElementType::XULTree
976 : #else
977 : false
978 : #endif
979 : ) {
980 0 : innerRect = aBorderArea;
981 : } else {
982 0 : innerRect = GetOutlineInnerRect(aForFrame) + aBorderArea.TopLeft();
983 : }
984 0 : nscoord offset = ourOutline->mOutlineOffset;
985 0 : innerRect.Inflate(offset, offset);
986 : // If the dirty rect is completely inside the border area (e.g., only the
987 : // content is being painted), then we can skip out now
988 : // XXX this isn't exactly true for rounded borders, where the inside curves may
989 : // encroach into the content area. A safer calculation would be to
990 : // shorten insideRect by the radius one each side before performing this test.
991 0 : if (innerRect.Contains(aDirtyRect))
992 0 : return Nothing();
993 :
994 0 : nscoord width = ourOutline->GetOutlineWidth();
995 :
996 0 : nsRect outerRect = innerRect;
997 0 : outerRect.Inflate(width, width);
998 :
999 : // get the radius for our outline
1000 0 : nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(),
1001 0 : outerRect.Size(), Sides(), twipsRadii);
1002 :
1003 : // Get our conversion values
1004 0 : nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1005 :
1006 : // get the outer rectangles
1007 0 : Rect oRect(NSRectToRect(outerRect, twipsPerPixel));
1008 :
1009 : // convert the radii
1010 0 : nsMargin outlineMargin(width, width, width, width);
1011 0 : RectCornerRadii outlineRadii;
1012 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii);
1013 :
1014 0 : uint8_t outlineStyle = ourOutline->mOutlineStyle;
1015 0 : if (outlineStyle == NS_STYLE_BORDER_STYLE_AUTO) {
1016 0 : if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
1017 0 : nsITheme* theme = aPresContext->GetTheme();
1018 0 : if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
1019 0 : NS_THEME_FOCUS_OUTLINE)) {
1020 : theme->DrawWidgetBackground(aRenderingContext, aForFrame,
1021 : NS_THEME_FOCUS_OUTLINE, innerRect,
1022 0 : aDirtyRect);
1023 0 : return Nothing();
1024 : }
1025 : }
1026 0 : if (width == 0) {
1027 0 : return Nothing(); // empty outline
1028 : }
1029 : // http://dev.w3.org/csswg/css-ui/#outline
1030 : // "User agents may treat 'auto' as 'solid'."
1031 0 : outlineStyle = NS_STYLE_BORDER_STYLE_SOLID;
1032 : }
1033 :
1034 : uint8_t outlineStyles[4] = { outlineStyle, outlineStyle,
1035 0 : outlineStyle, outlineStyle };
1036 :
1037 : // This handles treating the initial color as 'currentColor'; if we
1038 : // ever want 'invert' back we'll need to do a bit of work here too.
1039 : nscolor outlineColor =
1040 0 : aStyleContext->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
1041 : nscolor outlineColors[4] = { outlineColor,
1042 : outlineColor,
1043 : outlineColor,
1044 0 : outlineColor };
1045 :
1046 : // convert the border widths
1047 0 : Float outlineWidths[4] = { Float(width) / twipsPerPixel,
1048 0 : Float(width) / twipsPerPixel,
1049 0 : Float(width) / twipsPerPixel,
1050 0 : Float(width) / twipsPerPixel };
1051 0 : Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
1052 :
1053 0 : nsIDocument* document = nullptr;
1054 0 : nsIContent* content = aForFrame->GetContent();
1055 0 : if (content) {
1056 0 : document = content->OwnerDoc();
1057 : }
1058 :
1059 0 : DrawTarget* dt = aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr;
1060 : nsCSSBorderRenderer br(aPresContext,
1061 : document,
1062 : dt,
1063 : dirtyRect,
1064 : oRect,
1065 : outlineStyles,
1066 : outlineWidths,
1067 : outlineRadii,
1068 : outlineColors,
1069 : nullptr,
1070 0 : bgColor);
1071 :
1072 0 : return Some(br);
1073 : }
1074 :
1075 : void
1076 0 : nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
1077 : gfxContext& aRenderingContext,
1078 : nsIFrame* aForFrame,
1079 : const nsRect& aDirtyRect,
1080 : const nsRect& aBorderArea,
1081 : nsStyleContext* aStyleContext)
1082 : {
1083 : Maybe<nsCSSBorderRenderer> br = CreateBorderRendererForOutline(aPresContext,
1084 : &aRenderingContext,
1085 : aForFrame,
1086 : aDirtyRect,
1087 : aBorderArea,
1088 0 : aStyleContext);
1089 0 : if (!br) {
1090 0 : return;
1091 : }
1092 :
1093 : // start drawing
1094 0 : br->DrawBorders();
1095 :
1096 0 : PrintAsStringNewline();
1097 : }
1098 :
1099 : void
1100 0 : nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
1101 : DrawTarget* aDrawTarget,
1102 : const nsRect& aFocusRect,
1103 : nscolor aColor)
1104 : {
1105 0 : nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
1106 0 : nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
1107 :
1108 0 : Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel));
1109 :
1110 0 : RectCornerRadii focusRadii;
1111 : {
1112 0 : nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
1113 0 : ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
1114 : }
1115 0 : Float focusWidths[4] = { Float(oneCSSPixel) / oneDevPixel,
1116 0 : Float(oneCSSPixel) / oneDevPixel,
1117 0 : Float(oneCSSPixel) / oneDevPixel,
1118 0 : Float(oneCSSPixel) / oneDevPixel };
1119 :
1120 : uint8_t focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
1121 : NS_STYLE_BORDER_STYLE_DOTTED,
1122 : NS_STYLE_BORDER_STYLE_DOTTED,
1123 0 : NS_STYLE_BORDER_STYLE_DOTTED };
1124 0 : nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
1125 :
1126 : // Because this renders a dotted border, the background color
1127 : // should not be used. Therefore, we provide a value that will
1128 : // be blatantly wrong if it ever does get used. (If this becomes
1129 : // something that CSS can style, this function will then have access
1130 : // to a style context and can use the same logic that PaintBorder
1131 : // and PaintOutline do.)
1132 : nsCSSBorderRenderer br(aPresContext,
1133 : nullptr,
1134 : aDrawTarget,
1135 : focusRect,
1136 : focusRect,
1137 : focusStyles,
1138 : focusWidths,
1139 : focusRadii,
1140 : focusColors,
1141 : nullptr,
1142 0 : NS_RGB(255, 0, 0));
1143 0 : br.DrawBorders();
1144 :
1145 0 : PrintAsStringNewline();
1146 0 : }
1147 :
1148 : // Thebes Border Rendering Code End
1149 : //----------------------------------------------------------------------
1150 :
1151 :
1152 : //----------------------------------------------------------------------
1153 :
1154 : /**
1155 : * Helper for ComputeObjectAnchorPoint; parameters are the same as for
1156 : * that function, except they're for a single coordinate / a single size
1157 : * dimension. (so, x/width vs. y/height)
1158 : */
1159 : static void
1160 912 : ComputeObjectAnchorCoord(const Position::Coord& aCoord,
1161 : const nscoord aOriginBounds,
1162 : const nscoord aImageSize,
1163 : nscoord* aTopLeftCoord,
1164 : nscoord* aAnchorPointCoord)
1165 : {
1166 912 : *aAnchorPointCoord = aCoord.mLength;
1167 912 : *aTopLeftCoord = aCoord.mLength;
1168 :
1169 912 : if (aCoord.mHasPercent) {
1170 : // Adjust aTopLeftCoord by the specified % of the extra space.
1171 912 : nscoord extraSpace = aOriginBounds - aImageSize;
1172 912 : *aTopLeftCoord += NSToCoordRound(aCoord.mPercent * extraSpace);
1173 :
1174 : // The anchor-point doesn't care about our image's size; just the size
1175 : // of the region we're rendering into.
1176 912 : *aAnchorPointCoord += NSToCoordRound(aCoord.mPercent * aOriginBounds);
1177 : }
1178 912 : }
1179 :
1180 : void
1181 456 : nsImageRenderer::ComputeObjectAnchorPoint(
1182 : const Position& aPos,
1183 : const nsSize& aOriginBounds,
1184 : const nsSize& aImageSize,
1185 : nsPoint* aTopLeft,
1186 : nsPoint* aAnchorPoint)
1187 : {
1188 912 : ComputeObjectAnchorCoord(aPos.mXPosition,
1189 456 : aOriginBounds.width, aImageSize.width,
1190 456 : &aTopLeft->x, &aAnchorPoint->x);
1191 :
1192 912 : ComputeObjectAnchorCoord(aPos.mYPosition,
1193 456 : aOriginBounds.height, aImageSize.height,
1194 456 : &aTopLeft->y, &aAnchorPoint->y);
1195 456 : }
1196 :
1197 : nsIFrame*
1198 35 : nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
1199 : bool aStartAtParent /*= false*/)
1200 : {
1201 35 : NS_ASSERTION(aFrame, "Cannot find NonTransparentBackgroundFrame in a null frame");
1202 :
1203 35 : nsIFrame* frame = nullptr;
1204 35 : if (aStartAtParent) {
1205 0 : frame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
1206 : }
1207 35 : if (!frame) {
1208 35 : frame = aFrame;
1209 : }
1210 :
1211 63 : while (frame) {
1212 : // No need to call GetVisitedDependentColor because it always uses
1213 : // this alpha component anyway.
1214 49 : if (NS_GET_A(frame->StyleBackground()->BackgroundColor(frame)) > 0) {
1215 35 : break;
1216 : }
1217 :
1218 14 : if (frame->IsThemed())
1219 0 : break;
1220 :
1221 14 : nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
1222 14 : if (!parent)
1223 0 : break;
1224 :
1225 14 : frame = parent;
1226 : }
1227 35 : return frame;
1228 : }
1229 :
1230 : // Returns true if aFrame is a canvas frame.
1231 : // We need to treat the viewport as canvas because, even though
1232 : // it does not actually paint a background, we need to get the right
1233 : // background style so we correctly detect transparent documents.
1234 : bool
1235 1427 : nsCSSRendering::IsCanvasFrame(nsIFrame* aFrame)
1236 : {
1237 1427 : LayoutFrameType frameType = aFrame->Type();
1238 1387 : return frameType == LayoutFrameType::Canvas ||
1239 1288 : frameType == LayoutFrameType::Root ||
1240 2715 : frameType == LayoutFrameType::PageContent ||
1241 1427 : frameType == LayoutFrameType::Viewport;
1242 : }
1243 :
1244 : nsIFrame*
1245 304 : nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame)
1246 : {
1247 304 : const nsStyleBackground* result = aForFrame->StyleBackground();
1248 :
1249 : // Check if we need to do propagation from BODY rather than HTML.
1250 304 : if (!result->IsTransparent(aForFrame)) {
1251 264 : return aForFrame;
1252 : }
1253 :
1254 40 : nsIContent* content = aForFrame->GetContent();
1255 : // The root element content can't be null. We wouldn't know what
1256 : // frame to create for aFrame.
1257 : // Use |OwnerDoc| so it works during destruction.
1258 40 : if (!content) {
1259 0 : return aForFrame;
1260 : }
1261 :
1262 40 : nsIDocument* document = content->OwnerDoc();
1263 :
1264 40 : dom::Element* bodyContent = document->GetBodyElement();
1265 : // We need to null check the body node (bug 118829) since
1266 : // there are cases, thanks to the fix for bug 5569, where we
1267 : // will reflow a document with no body. In particular, if a
1268 : // SCRIPT element in the head blocks the parser and then has a
1269 : // SCRIPT that does "document.location.href = 'foo'", then
1270 : // nsParser::Terminate will call |DidBuildModel| methods
1271 : // through to the content sink, which will call |StartLayout|
1272 : // and thus |Initialize| on the pres shell. See bug 119351
1273 : // for the ugly details.
1274 40 : if (!bodyContent) {
1275 36 : return aForFrame;
1276 : }
1277 :
1278 4 : nsIFrame *bodyFrame = bodyContent->GetPrimaryFrame();
1279 4 : if (!bodyFrame) {
1280 0 : return aForFrame;
1281 : }
1282 :
1283 4 : return nsLayoutUtils::GetStyleFrame(bodyFrame);
1284 : }
1285 :
1286 : /**
1287 : * |FindBackground| finds the correct style data to use to paint the
1288 : * background. It is responsible for handling the following two
1289 : * statements in section 14.2 of CSS2:
1290 : *
1291 : * The background of the box generated by the root element covers the
1292 : * entire canvas.
1293 : *
1294 : * For HTML documents, however, we recommend that authors specify the
1295 : * background for the BODY element rather than the HTML element. User
1296 : * agents should observe the following precedence rules to fill in the
1297 : * background: if the value of the 'background' property for the HTML
1298 : * element is different from 'transparent' then use it, else use the
1299 : * value of the 'background' property for the BODY element. If the
1300 : * resulting value is 'transparent', the rendering is undefined.
1301 : *
1302 : * Thus, in our implementation, it is responsible for ensuring that:
1303 : * + we paint the correct background on the |nsCanvasFrame|,
1304 : * |nsRootBoxFrame|, or |nsPageFrame|,
1305 : * + we don't paint the background on the root element, and
1306 : * + we don't paint the background on the BODY element in *some* cases,
1307 : * and for SGML-based HTML documents only.
1308 : *
1309 : * |FindBackground| returns true if a background should be painted, and
1310 : * the resulting style context to use for the background information
1311 : * will be filled in to |aBackground|.
1312 : */
1313 : nsStyleContext*
1314 106 : nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
1315 : {
1316 106 : return FindBackgroundStyleFrame(aForFrame)->StyleContext();
1317 : }
1318 :
1319 : inline bool
1320 466 : FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
1321 : nsStyleContext** aBackgroundSC)
1322 : {
1323 466 : if (aForFrame == aRootElementFrame) {
1324 : // We must have propagated our background to the viewport or canvas. Abort.
1325 0 : return false;
1326 : }
1327 :
1328 466 : *aBackgroundSC = aForFrame->StyleContext();
1329 :
1330 : // Return true unless the frame is for a BODY element whose background
1331 : // was propagated to the viewport.
1332 :
1333 466 : nsIContent* content = aForFrame->GetContent();
1334 466 : if (!content || content->NodeInfo()->NameAtom() != nsGkAtoms::body)
1335 466 : return true; // not frame for a "body" element
1336 : // It could be a non-HTML "body" element but that's OK, we'd fail the
1337 : // bodyContent check below
1338 :
1339 0 : if (aForFrame->StyleContext()->GetPseudo())
1340 0 : return true; // A pseudo-element frame.
1341 :
1342 : // We should only look at the <html> background if we're in an HTML document
1343 0 : nsIDocument* document = content->OwnerDoc();
1344 :
1345 0 : dom::Element* bodyContent = document->GetBodyElement();
1346 0 : if (bodyContent != content)
1347 0 : return true; // this wasn't the background that was propagated
1348 :
1349 : // This can be called even when there's no root element yet, during frame
1350 : // construction, via nsLayoutUtils::FrameHasTransparency and
1351 : // nsContainerFrame::SyncFrameViewProperties.
1352 0 : if (!aRootElementFrame)
1353 0 : return true;
1354 :
1355 0 : const nsStyleBackground* htmlBG = aRootElementFrame->StyleBackground();
1356 0 : return !htmlBG->IsTransparent(aRootElementFrame);
1357 : }
1358 :
1359 : bool
1360 519 : nsCSSRendering::FindBackground(nsIFrame* aForFrame,
1361 : nsStyleContext** aBackgroundSC)
1362 : {
1363 : nsIFrame* rootElementFrame =
1364 519 : aForFrame->PresContext()->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
1365 519 : if (IsCanvasFrame(aForFrame)) {
1366 53 : *aBackgroundSC = FindCanvasBackground(aForFrame, rootElementFrame);
1367 53 : return true;
1368 : } else {
1369 466 : return FindElementBackground(aForFrame, rootElementFrame, aBackgroundSC);
1370 : }
1371 : }
1372 :
1373 : void
1374 53 : nsCSSRendering::BeginFrameTreesLocked()
1375 : {
1376 53 : ++gFrameTreeLockCount;
1377 53 : }
1378 :
1379 : void
1380 53 : nsCSSRendering::EndFrameTreesLocked()
1381 : {
1382 53 : NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
1383 53 : --gFrameTreeLockCount;
1384 53 : if (gFrameTreeLockCount == 0) {
1385 35 : gInlineBGData->Reset();
1386 : }
1387 53 : }
1388 :
1389 : bool
1390 9 : nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
1391 : bool& aMaybeHasBorderRadius)
1392 : {
1393 9 : const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
1394 : nsITheme::Transparency transparency;
1395 9 : if (aFrame->IsThemed(styleDisplay, &transparency)) {
1396 0 : aMaybeHasBorderRadius = false;
1397 : // For opaque (rectangular) theme widgets we can take the generic
1398 : // border-box path with border-radius disabled.
1399 0 : return transparency != nsITheme::eOpaque;
1400 : }
1401 :
1402 9 : aMaybeHasBorderRadius = true;
1403 9 : return false;
1404 : }
1405 :
1406 : gfx::Color
1407 19 : nsCSSRendering::GetShadowColor(nsCSSShadowItem* aShadow,
1408 : nsIFrame* aFrame,
1409 : float aOpacity)
1410 : {
1411 : // Get the shadow color; if not specified, use the foreground color
1412 : nscolor shadowColor;
1413 19 : if (aShadow->mHasColor)
1414 19 : shadowColor = aShadow->mColor;
1415 : else
1416 0 : shadowColor = aFrame->StyleColor()->mColor;
1417 :
1418 19 : Color color = Color::FromABGR(shadowColor);
1419 19 : color.a *= aOpacity;
1420 19 : return color;
1421 : }
1422 :
1423 : nsRect
1424 9 : nsCSSRendering::GetShadowRect(const nsRect aFrameArea,
1425 : bool aNativeTheme,
1426 : nsIFrame* aForFrame)
1427 : {
1428 : nsRect frameRect = aNativeTheme ?
1429 9 : aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
1430 18 : aFrameArea;
1431 9 : Sides skipSides = aForFrame->GetSkipSides();
1432 9 : frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
1433 :
1434 : // Explicitly do not need to account for the spread radius here
1435 : // Webrender does it for us or PaintBoxShadow will for non-WR
1436 9 : return frameRect;
1437 : }
1438 :
1439 : bool
1440 10 : nsCSSRendering::GetBorderRadii(const nsRect& aFrameRect,
1441 : const nsRect& aBorderRect,
1442 : nsIFrame* aFrame,
1443 : RectCornerRadii& aOutRadii)
1444 : {
1445 10 : const nscoord twipsPerPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
1446 : nscoord twipsRadii[8];
1447 10 : NS_ASSERTION(aBorderRect.Size() == aFrame->VisualBorderRectRelativeToSelf().Size(),
1448 : "unexpected size");
1449 10 : nsSize sz = aFrameRect.Size();
1450 10 : bool hasBorderRadius = aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
1451 10 : if (hasBorderRadius) {
1452 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &aOutRadii);
1453 : }
1454 :
1455 10 : return hasBorderRadius;
1456 : }
1457 :
1458 : void
1459 9 : nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
1460 : gfxContext& aRenderingContext,
1461 : nsIFrame* aForFrame,
1462 : const nsRect& aFrameArea,
1463 : const nsRect& aDirtyRect,
1464 : float aOpacity)
1465 : {
1466 9 : DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
1467 9 : nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow;
1468 9 : if (!shadows)
1469 0 : return;
1470 :
1471 : bool hasBorderRadius;
1472 : // mutually exclusive with hasBorderRadius
1473 9 : bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
1474 9 : const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
1475 :
1476 18 : nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);
1477 :
1478 : // Get any border radius, since box-shadow must also have rounded corners if
1479 : // the frame does.
1480 9 : RectCornerRadii borderRadii;
1481 9 : const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1482 9 : if (hasBorderRadius) {
1483 : nscoord twipsRadii[8];
1484 9 : NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
1485 : "unexpected size");
1486 9 : nsSize sz = frameRect.Size();
1487 9 : hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
1488 9 : if (hasBorderRadius) {
1489 9 : ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
1490 : }
1491 : }
1492 :
1493 :
1494 : // We don't show anything that intersects with the frame we're blurring on. So tell the
1495 : // blurrer not to do unnecessary work there.
1496 9 : gfxRect skipGfxRect = ThebesRect(NSRectToRect(frameRect, twipsPerPixel));
1497 9 : skipGfxRect.Round();
1498 9 : bool useSkipGfxRect = true;
1499 9 : if (nativeTheme) {
1500 : // Optimize non-leaf native-themed frames by skipping computing pixels
1501 : // in the padding-box. We assume the padding-box is going to be painted
1502 : // opaquely for non-leaf frames.
1503 : // XXX this may not be a safe assumption; we should make this go away
1504 : // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
1505 0 : useSkipGfxRect = !aForFrame->IsLeaf();
1506 : nsRect paddingRect =
1507 0 : aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
1508 0 : skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
1509 9 : } else if (hasBorderRadius) {
1510 27 : skipGfxRect.Deflate(gfxMargin(
1511 9 : std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
1512 18 : std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
1513 : }
1514 :
1515 :
1516 18 : for (uint32_t i = shadows->Length(); i > 0; --i) {
1517 9 : nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
1518 9 : if (shadowItem->mInset)
1519 0 : continue;
1520 :
1521 18 : nsRect shadowRect = frameRect;
1522 9 : shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
1523 9 : if (!nativeTheme) {
1524 9 : shadowRect.Inflate(shadowItem->mSpread, shadowItem->mSpread);
1525 : }
1526 :
1527 : // shadowRect won't include the blur, so make an extra rect here that includes the blur
1528 : // for use in the even-odd rule below.
1529 18 : nsRect shadowRectPlusBlur = shadowRect;
1530 9 : nscoord blurRadius = shadowItem->mRadius;
1531 : shadowRectPlusBlur.Inflate(
1532 9 : nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel));
1533 :
1534 : Rect shadowGfxRectPlusBlur =
1535 9 : NSRectToRect(shadowRectPlusBlur, twipsPerPixel);
1536 9 : shadowGfxRectPlusBlur.RoundOut();
1537 9 : MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);
1538 :
1539 9 : Color gfxShadowColor = GetShadowColor(shadowItem, aForFrame, aOpacity);
1540 :
1541 9 : if (nativeTheme) {
1542 0 : nsContextBoxBlur blurringArea;
1543 :
1544 : // When getting the widget shape from the native theme, we're going
1545 : // to draw the widget into the shadow surface to create a mask.
1546 : // We need to ensure that there actually *is* a shadow surface
1547 : // and that we're not going to draw directly into aRenderingContext.
1548 : gfxContext* shadowContext =
1549 0 : blurringArea.Init(shadowRect, shadowItem->mSpread, blurRadius,
1550 : twipsPerPixel, &aRenderingContext, aDirtyRect,
1551 : useSkipGfxRect ? &skipGfxRect : nullptr,
1552 0 : nsContextBoxBlur::FORCE_MASK);
1553 0 : if (!shadowContext)
1554 0 : continue;
1555 :
1556 0 : MOZ_ASSERT(shadowContext == blurringArea.GetContext());
1557 :
1558 0 : aRenderingContext.Save();
1559 0 : aRenderingContext.SetColor(gfxShadowColor);
1560 :
1561 : // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
1562 : // doesn't make any temporary surfaces if blur is 0 and it just returns the original
1563 : // surface? If we have no blur, we're painting this fill on the actual content surface
1564 : // (aRenderingContext == shadowContext) which is why we set up the color and clip
1565 : // before doing this.
1566 :
1567 : // We don't clip the border-box from the shadow, nor any other box.
1568 : // We assume that the native theme is going to paint over the shadow.
1569 :
1570 : // Draw the widget shape
1571 0 : gfxContextMatrixAutoSaveRestore save(shadowContext);
1572 : gfxPoint devPixelOffset =
1573 0 : nsLayoutUtils::PointToGfxPoint(nsPoint(shadowItem->mXOffset,
1574 : shadowItem->mYOffset),
1575 0 : aPresContext->AppUnitsPerDevPixel());
1576 : shadowContext->SetMatrix(
1577 0 : shadowContext->CurrentMatrix().PreTranslate(devPixelOffset));
1578 :
1579 0 : nsRect nativeRect = aDirtyRect;
1580 0 : nativeRect.MoveBy(-nsPoint(shadowItem->mXOffset, shadowItem->mYOffset));
1581 0 : nativeRect.IntersectRect(frameRect, nativeRect);
1582 0 : aPresContext->GetTheme()->DrawWidgetBackground(shadowContext, aForFrame,
1583 0 : styleDisplay->mAppearance, aFrameArea, nativeRect);
1584 :
1585 0 : blurringArea.DoPaint();
1586 0 : aRenderingContext.Restore();
1587 : } else {
1588 9 : aRenderingContext.Save();
1589 :
1590 : {
1591 9 : Rect innerClipRect = NSRectToRect(frameRect, twipsPerPixel);
1592 9 : if (!MaybeSnapToDevicePixels(innerClipRect, aDrawTarget, true)) {
1593 0 : innerClipRect.Round();
1594 : }
1595 :
1596 : // Clip out the interior of the frame's border edge so that the shadow
1597 : // is only painted outside that area.
1598 : RefPtr<PathBuilder> builder =
1599 18 : aDrawTarget.CreatePathBuilder(FillRule::FILL_EVEN_ODD);
1600 9 : AppendRectToPath(builder, shadowGfxRectPlusBlur);
1601 9 : if (hasBorderRadius) {
1602 9 : AppendRoundedRectToPath(builder, innerClipRect, borderRadii);
1603 : } else {
1604 0 : AppendRectToPath(builder, innerClipRect);
1605 : }
1606 18 : RefPtr<Path> path = builder->Finish();
1607 9 : aRenderingContext.Clip(path);
1608 : }
1609 :
1610 : // Clip the shadow so that we only get the part that applies to aForFrame.
1611 18 : nsRect fragmentClip = shadowRectPlusBlur;
1612 9 : Sides skipSides = aForFrame->GetSkipSides();
1613 9 : if (!skipSides.IsEmpty()) {
1614 0 : if (skipSides.Left()) {
1615 0 : nscoord xmost = fragmentClip.XMost();
1616 0 : fragmentClip.x = aFrameArea.x;
1617 0 : fragmentClip.width = xmost - fragmentClip.x;
1618 : }
1619 0 : if (skipSides.Right()) {
1620 0 : nscoord xmost = fragmentClip.XMost();
1621 0 : nscoord overflow = xmost - aFrameArea.XMost();
1622 0 : if (overflow > 0) {
1623 0 : fragmentClip.width -= overflow;
1624 : }
1625 : }
1626 0 : if (skipSides.Top()) {
1627 0 : nscoord ymost = fragmentClip.YMost();
1628 0 : fragmentClip.y = aFrameArea.y;
1629 0 : fragmentClip.height = ymost - fragmentClip.y;
1630 : }
1631 0 : if (skipSides.Bottom()) {
1632 0 : nscoord ymost = fragmentClip.YMost();
1633 0 : nscoord overflow = ymost - aFrameArea.YMost();
1634 0 : if (overflow > 0) {
1635 0 : fragmentClip.height -= overflow;
1636 : }
1637 : }
1638 : }
1639 9 : fragmentClip = fragmentClip.Intersect(aDirtyRect);
1640 : aRenderingContext.
1641 18 : Clip(NSRectToSnappedRect(fragmentClip,
1642 9 : aForFrame->PresContext()->AppUnitsPerDevPixel(),
1643 18 : aDrawTarget));
1644 :
1645 9 : RectCornerRadii clipRectRadii;
1646 9 : if (hasBorderRadius) {
1647 9 : Float spreadDistance = Float(shadowItem->mSpread) / twipsPerPixel;
1648 :
1649 : Float borderSizes[4];
1650 :
1651 9 : borderSizes[eSideLeft] = spreadDistance;
1652 9 : borderSizes[eSideTop] = spreadDistance;
1653 9 : borderSizes[eSideRight] = spreadDistance;
1654 9 : borderSizes[eSideBottom] = spreadDistance;
1655 :
1656 : nsCSSBorderRenderer::ComputeOuterRadii(borderRadii, borderSizes,
1657 9 : &clipRectRadii);
1658 :
1659 : }
1660 9 : nsContextBoxBlur::BlurRectangle(&aRenderingContext,
1661 : shadowRect,
1662 : twipsPerPixel,
1663 : hasBorderRadius ? &clipRectRadii : nullptr,
1664 : blurRadius,
1665 : gfxShadowColor,
1666 : aDirtyRect,
1667 9 : skipGfxRect);
1668 9 : aRenderingContext.Restore();
1669 : }
1670 :
1671 : }
1672 : }
1673 :
1674 : nsRect
1675 10 : nsCSSRendering::GetBoxShadowInnerPaddingRect(nsIFrame* aFrame,
1676 : const nsRect& aFrameArea)
1677 : {
1678 10 : Sides skipSides = aFrame->GetSkipSides();
1679 : nsRect frameRect =
1680 20 : ::BoxDecorationRectForBorder(aFrame, aFrameArea, skipSides);
1681 :
1682 10 : nsRect paddingRect = frameRect;
1683 10 : nsMargin border = aFrame->GetUsedBorder();
1684 10 : paddingRect.Deflate(border);
1685 20 : return paddingRect;
1686 : }
1687 :
1688 : bool
1689 10 : nsCSSRendering::ShouldPaintBoxShadowInner(nsIFrame* aFrame)
1690 : {
1691 10 : nsCSSShadowArray* shadows = aFrame->StyleEffects()->mBoxShadow;
1692 10 : if (!shadows)
1693 0 : return false;
1694 :
1695 10 : if (aFrame->IsThemed() && aFrame->GetContent() &&
1696 0 : !nsContentUtils::IsChromeDoc(aFrame->GetContent()->GetUncomposedDoc())) {
1697 : // There's no way of getting hold of a shape corresponding to a
1698 : // "padding-box" for native-themed widgets, so just don't draw
1699 : // inner box-shadows for them. But we allow chrome to paint inner
1700 : // box shadows since chrome can be aware of the platform theme.
1701 0 : return false;
1702 : }
1703 :
1704 10 : return true;
1705 : }
1706 :
1707 : bool
1708 10 : nsCSSRendering::GetShadowInnerRadii(nsIFrame* aFrame,
1709 : const nsRect& aFrameArea,
1710 : RectCornerRadii& aOutInnerRadii)
1711 : {
1712 : // Get any border radius, since box-shadow must also have rounded corners
1713 : // if the frame does.
1714 : nscoord twipsRadii[8];
1715 : nsRect frameRect =
1716 20 : ::BoxDecorationRectForBorder(aFrame, aFrameArea, aFrame->GetSkipSides());
1717 10 : nsSize sz = frameRect.Size();
1718 10 : nsMargin border = aFrame->GetUsedBorder();
1719 10 : bool hasBorderRadius = aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
1720 10 : const nscoord twipsPerPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
1721 :
1722 10 : RectCornerRadii borderRadii;
1723 :
1724 10 : hasBorderRadius = GetBorderRadii(frameRect, aFrameArea, aFrame, borderRadii);
1725 10 : if (hasBorderRadius) {
1726 0 : ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
1727 :
1728 : Float borderSizes[4] = {
1729 0 : Float(border.top) / twipsPerPixel,
1730 0 : Float(border.right) / twipsPerPixel,
1731 0 : Float(border.bottom) / twipsPerPixel,
1732 0 : Float(border.left) / twipsPerPixel
1733 0 : };
1734 : nsCSSBorderRenderer::ComputeInnerRadii(borderRadii,
1735 : borderSizes,
1736 0 : &aOutInnerRadii);
1737 : }
1738 :
1739 20 : return hasBorderRadius;
1740 : }
1741 :
1742 : void
1743 10 : nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
1744 : gfxContext& aRenderingContext,
1745 : nsIFrame* aForFrame,
1746 : const nsRect& aFrameArea)
1747 : {
1748 10 : if (!ShouldPaintBoxShadowInner(aForFrame)) {
1749 0 : return;
1750 : }
1751 :
1752 10 : nsCSSShadowArray* shadows = aForFrame->StyleEffects()->mBoxShadow;
1753 10 : NS_ASSERTION(aForFrame->IsFieldSetFrame() ||
1754 : aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
1755 :
1756 20 : nsRect paddingRect = GetBoxShadowInnerPaddingRect(aForFrame, aFrameArea);
1757 :
1758 10 : RectCornerRadii innerRadii;
1759 : bool hasBorderRadius = GetShadowInnerRadii(aForFrame,
1760 : aFrameArea,
1761 10 : innerRadii);
1762 :
1763 10 : const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
1764 :
1765 20 : for (uint32_t i = shadows->Length(); i > 0; --i) {
1766 10 : nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
1767 10 : if (!shadowItem->mInset)
1768 0 : continue;
1769 :
1770 : // shadowPaintRect: the area to paint on the temp surface
1771 : // shadowClipRect: the area on the temporary surface within shadowPaintRect
1772 : // that we will NOT paint in
1773 10 : nscoord blurRadius = shadowItem->mRadius;
1774 : nsMargin blurMargin =
1775 10 : nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel);
1776 20 : nsRect shadowPaintRect = paddingRect;
1777 10 : shadowPaintRect.Inflate(blurMargin);
1778 :
1779 : // Round the spread radius to device pixels (by truncation).
1780 : // This mostly matches what we do for borders, except that we don't round
1781 : // up values between zero and one device pixels to one device pixel.
1782 : // This way of rounding is symmetric around zero, which makes sense for
1783 : // the spread radius.
1784 10 : int32_t spreadDistance = shadowItem->mSpread / twipsPerPixel;
1785 10 : nscoord spreadDistanceAppUnits = aPresContext->DevPixelsToAppUnits(spreadDistance);
1786 :
1787 20 : nsRect shadowClipRect = paddingRect;
1788 10 : shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
1789 10 : shadowClipRect.Deflate(spreadDistanceAppUnits, spreadDistanceAppUnits);
1790 :
1791 10 : Rect shadowClipGfxRect = NSRectToRect(shadowClipRect, twipsPerPixel);
1792 10 : shadowClipGfxRect.Round();
1793 :
1794 10 : RectCornerRadii clipRectRadii;
1795 10 : if (hasBorderRadius) {
1796 : // Calculate the radii the inner clipping rect will have
1797 0 : Float borderSizes[4] = {0, 0, 0, 0};
1798 :
1799 : // See PaintBoxShadowOuter and bug 514670
1800 0 : if (innerRadii[C_TL].width > 0 || innerRadii[C_BL].width > 0) {
1801 0 : borderSizes[eSideLeft] = spreadDistance;
1802 : }
1803 :
1804 0 : if (innerRadii[C_TL].height > 0 || innerRadii[C_TR].height > 0) {
1805 0 : borderSizes[eSideTop] = spreadDistance;
1806 : }
1807 :
1808 0 : if (innerRadii[C_TR].width > 0 || innerRadii[C_BR].width > 0) {
1809 0 : borderSizes[eSideRight] = spreadDistance;
1810 : }
1811 :
1812 0 : if (innerRadii[C_BL].height > 0 || innerRadii[C_BR].height > 0) {
1813 0 : borderSizes[eSideBottom] = spreadDistance;
1814 : }
1815 :
1816 : nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
1817 0 : &clipRectRadii);
1818 : }
1819 :
1820 : // Set the "skip rect" to the area within the frame that we don't paint in,
1821 : // including after blurring.
1822 20 : nsRect skipRect = shadowClipRect;
1823 10 : skipRect.Deflate(blurMargin);
1824 10 : gfxRect skipGfxRect = nsLayoutUtils::RectToGfxRect(skipRect, twipsPerPixel);
1825 10 : if (hasBorderRadius) {
1826 0 : skipGfxRect.Deflate(gfxMargin(
1827 0 : std::max(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height), 0,
1828 0 : std::max(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height), 0));
1829 : }
1830 :
1831 : // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
1832 : // unchanged. And by construction the gfxSkipRect is not touched by the
1833 : // rendered shadow (even after blurring), so those pixels must be completely
1834 : // transparent in the shadow, so drawing them changes nothing.
1835 10 : DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1836 :
1837 : // Clip the context to the area of the frame's padding rect, so no part of the
1838 : // shadow is painted outside. Also cut out anything beyond where the inset shadow
1839 : // will be.
1840 10 : Rect shadowGfxRect = NSRectToRect(paddingRect, twipsPerPixel);
1841 10 : shadowGfxRect.Round();
1842 :
1843 10 : Color shadowColor = GetShadowColor(shadowItem, aForFrame, 1.0);
1844 10 : aRenderingContext.Save();
1845 :
1846 : // This clips the outside border radius.
1847 : // clipRectRadii is the border radius inside the inset shadow.
1848 10 : if (hasBorderRadius) {
1849 : RefPtr<Path> roundedRect =
1850 0 : MakePathForRoundedRect(*drawTarget, shadowGfxRect, innerRadii);
1851 0 : aRenderingContext.Clip(roundedRect);
1852 : } else {
1853 10 : aRenderingContext.Clip(shadowGfxRect);
1854 : }
1855 :
1856 20 : nsContextBoxBlur insetBoxBlur;
1857 10 : gfxRect destRect = nsLayoutUtils::RectToGfxRect(shadowPaintRect, twipsPerPixel);
1858 10 : Point shadowOffset(shadowItem->mXOffset / twipsPerPixel,
1859 20 : shadowItem->mYOffset / twipsPerPixel);
1860 :
1861 10 : insetBoxBlur.InsetBoxBlur(&aRenderingContext, ToRect(destRect),
1862 : shadowClipGfxRect, shadowColor,
1863 : blurRadius, spreadDistanceAppUnits,
1864 : twipsPerPixel, hasBorderRadius,
1865 : clipRectRadii, ToRect(skipGfxRect),
1866 10 : shadowOffset);
1867 10 : aRenderingContext.Restore();
1868 : }
1869 : }
1870 :
1871 : /* static */
1872 : nsCSSRendering::PaintBGParams
1873 0 : nsCSSRendering::PaintBGParams::ForAllLayers(nsPresContext& aPresCtx,
1874 : const nsRect& aDirtyRect,
1875 : const nsRect& aBorderArea,
1876 : nsIFrame *aFrame,
1877 : uint32_t aPaintFlags,
1878 : float aOpacity)
1879 : {
1880 0 : MOZ_ASSERT(aFrame);
1881 :
1882 : PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea,
1883 : aFrame, aPaintFlags, -1, CompositionOp::OP_OVER,
1884 0 : aOpacity);
1885 :
1886 0 : return result;
1887 : }
1888 :
1889 : /* static */
1890 : nsCSSRendering::PaintBGParams
1891 59 : nsCSSRendering::PaintBGParams::ForSingleLayer(nsPresContext& aPresCtx,
1892 : const nsRect& aDirtyRect,
1893 : const nsRect& aBorderArea,
1894 : nsIFrame *aFrame,
1895 : uint32_t aPaintFlags,
1896 : int32_t aLayer,
1897 : CompositionOp aCompositionOp,
1898 : float aOpacity)
1899 : {
1900 59 : MOZ_ASSERT(aFrame && (aLayer != -1));
1901 :
1902 : PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea,
1903 : aFrame, aPaintFlags, aLayer, aCompositionOp,
1904 59 : aOpacity);
1905 :
1906 59 : return result;
1907 : }
1908 :
1909 : DrawResult
1910 56 : nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams,
1911 : gfxContext& aRenderingCtx)
1912 : {
1913 112 : AUTO_PROFILER_LABEL("nsCSSRendering::PaintStyleImageLayer", GRAPHICS);
1914 :
1915 56 : NS_PRECONDITION(aParams.frame,
1916 : "Frame is expected to be provided to PaintStyleImageLayer");
1917 :
1918 : nsStyleContext *sc;
1919 56 : if (!FindBackground(aParams.frame, &sc)) {
1920 : // We don't want to bail out if moz-appearance is set on a root
1921 : // node. If it has a parent content node, bail because it's not
1922 : // a root, otherwise keep going in order to let the theme stuff
1923 : // draw the background. The canvas really should be drawing the
1924 : // bg, but there's no way to hook that up via css.
1925 0 : if (!aParams.frame->StyleDisplay()->mAppearance) {
1926 0 : return DrawResult::SUCCESS;
1927 : }
1928 :
1929 0 : nsIContent* content = aParams.frame->GetContent();
1930 0 : if (!content || content->GetParent()) {
1931 0 : return DrawResult::SUCCESS;
1932 : }
1933 :
1934 0 : sc = aParams.frame->StyleContext();
1935 : }
1936 :
1937 56 : return PaintStyleImageLayerWithSC(aParams, aRenderingCtx, sc, *aParams.frame->StyleBorder());
1938 : }
1939 :
1940 : bool
1941 0 : nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(LayerManager* aManager,
1942 : nsPresContext& aPresCtx,
1943 : nsIFrame *aFrame,
1944 : const nsStyleBackground* aBackgroundStyle,
1945 : int32_t aLayer)
1946 : {
1947 0 : if (!aBackgroundStyle) {
1948 0 : return false;
1949 : }
1950 :
1951 0 : MOZ_ASSERT(aFrame &&
1952 : aLayer >= 0 &&
1953 : (uint32_t)aLayer < aBackgroundStyle->mImage.mLayers.Length());
1954 :
1955 : // We cannot draw native themed backgrounds
1956 0 : const nsStyleDisplay* displayData = aFrame->StyleDisplay();
1957 0 : if (displayData->mAppearance) {
1958 0 : nsITheme *theme = aPresCtx.GetTheme();
1959 0 : if (theme && theme->ThemeSupportsWidget(&aPresCtx,
1960 : aFrame,
1961 0 : displayData->mAppearance)) {
1962 0 : return false;
1963 : }
1964 : }
1965 :
1966 : // We only support painting gradients and image for a single style image layer
1967 0 : const nsStyleImage* styleImage = &aBackgroundStyle->mImage.mLayers[aLayer].mImage;
1968 0 : if (styleImage->GetType() == eStyleImageType_Image) {
1969 0 : if (styleImage->GetCropRect()) {
1970 0 : return false;
1971 : }
1972 :
1973 0 : imgRequestProxy* requestProxy = styleImage->GetImageData();
1974 0 : if (!requestProxy) {
1975 0 : return false;
1976 : }
1977 :
1978 0 : nsCOMPtr<imgIContainer> srcImage;
1979 0 : requestProxy->GetImage(getter_AddRefs(srcImage));
1980 0 : if (!srcImage || !srcImage->IsImageContainerAvailable(aManager, imgIContainer::FLAG_NONE)) {
1981 0 : return false;
1982 : }
1983 :
1984 0 : return true;
1985 : }
1986 :
1987 0 : if (styleImage->GetType() == eStyleImageType_Gradient) {
1988 0 : return true;
1989 : }
1990 :
1991 0 : return false;
1992 : }
1993 :
1994 : DrawResult
1995 0 : nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(const PaintBGParams& aParams,
1996 : mozilla::wr::DisplayListBuilder& aBuilder,
1997 : const mozilla::layers::StackingContextHelper& aSc,
1998 : nsTArray<WebRenderParentCommand>& aParentCommands,
1999 : mozilla::layers::WebRenderDisplayItemLayer* aLayer,
2000 : mozilla::layers::WebRenderLayerManager* aManager,
2001 : nsDisplayItem* aItem)
2002 : {
2003 0 : NS_PRECONDITION(aParams.frame,
2004 : "Frame is expected to be provided to BuildWebRenderDisplayItemsForStyleImageLayer");
2005 :
2006 : nsStyleContext *sc;
2007 0 : if (!FindBackground(aParams.frame, &sc)) {
2008 : // We don't want to bail out if moz-appearance is set on a root
2009 : // node. If it has a parent content node, bail because it's not
2010 : // a root, otherwise keep going in order to let the theme stuff
2011 : // draw the background. The canvas really should be drawing the
2012 : // bg, but there's no way to hook that up via css.
2013 0 : if (!aParams.frame->StyleDisplay()->mAppearance) {
2014 0 : return DrawResult::SUCCESS;
2015 : }
2016 :
2017 0 : nsIContent* content = aParams.frame->GetContent();
2018 0 : if (!content || content->GetParent()) {
2019 0 : return DrawResult::SUCCESS;
2020 : }
2021 :
2022 0 : sc = aParams.frame->StyleContext();
2023 : }
2024 0 : return BuildWebRenderDisplayItemsForStyleImageLayerWithSC(aParams, aBuilder, aSc, aParentCommands,
2025 : aLayer, aManager, aItem,
2026 0 : sc, *aParams.frame->StyleBorder());
2027 : }
2028 :
2029 : static bool
2030 356 : IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::Side aSide)
2031 : {
2032 356 : if (aBorder.GetComputedBorder().Side(aSide) == 0)
2033 70 : return true;
2034 286 : switch (aBorder.GetBorderStyle(aSide)) {
2035 : case NS_STYLE_BORDER_STYLE_SOLID:
2036 : case NS_STYLE_BORDER_STYLE_GROOVE:
2037 : case NS_STYLE_BORDER_STYLE_RIDGE:
2038 : case NS_STYLE_BORDER_STYLE_INSET:
2039 : case NS_STYLE_BORDER_STYLE_OUTSET:
2040 286 : break;
2041 : default:
2042 0 : return false;
2043 : }
2044 :
2045 : // If we're using a border image, assume it's not fully opaque,
2046 : // because we may not even have the image loaded at this point, and
2047 : // even if we did, checking whether the relevant tile is fully
2048 : // opaque would be too much work.
2049 286 : if (aBorder.mBorderImageSource.GetType() != eStyleImageType_Null)
2050 0 : return false;
2051 :
2052 286 : StyleComplexColor color = aBorder.mBorderColor[aSide];
2053 : // We don't know the foreground color here, so if it's being used
2054 : // we must assume it might be transparent.
2055 286 : if (!color.IsNumericColor()) {
2056 0 : return false;
2057 : }
2058 286 : return NS_GET_A(color.mColor) == 255;
2059 : }
2060 :
2061 : /**
2062 : * Returns true if all border edges are either missing or opaque.
2063 : */
2064 : static bool
2065 131 : IsOpaqueBorder(const nsStyleBorder& aBorder)
2066 : {
2067 131 : if (aBorder.mBorderColors)
2068 0 : return false;
2069 415 : NS_FOR_CSS_SIDES(i) {
2070 356 : if (!IsOpaqueBorderEdge(aBorder, i))
2071 72 : return false;
2072 : }
2073 59 : return true;
2074 : }
2075 :
2076 : static inline void
2077 413 : SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect,
2078 : nscoord aAppUnitsPerPixel,
2079 : /* OUT: */
2080 : nsRect* aDirtyRect, gfxRect* aDirtyRectGfx)
2081 : {
2082 413 : aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect);
2083 :
2084 : // Compute the Thebes equivalent of the dirtyRect.
2085 413 : *aDirtyRectGfx = nsLayoutUtils::RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
2086 413 : NS_WARNING_ASSERTION(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(),
2087 : "converted dirty rect should not be empty");
2088 413 : MOZ_ASSERT(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(),
2089 : "second should be empty if first is");
2090 413 : }
2091 :
2092 : static bool
2093 2004 : IsSVGStyleGeometryBox(StyleGeometryBox aBox)
2094 : {
2095 2004 : return (aBox == StyleGeometryBox::FillBox ||
2096 4008 : aBox == StyleGeometryBox::StrokeBox ||
2097 2004 : aBox == StyleGeometryBox::ViewBox);
2098 : }
2099 :
2100 : static bool
2101 0 : IsHTMLStyleGeometryBox(StyleGeometryBox aBox)
2102 : {
2103 0 : return (aBox == StyleGeometryBox::ContentBox ||
2104 0 : aBox == StyleGeometryBox::PaddingBox ||
2105 0 : aBox == StyleGeometryBox::BorderBox ||
2106 0 : aBox == StyleGeometryBox::MarginBox);
2107 : }
2108 :
2109 : static StyleGeometryBox
2110 1002 : ComputeBoxValue(nsIFrame* aForFrame, StyleGeometryBox aBox)
2111 : {
2112 1002 : if (!(aForFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
2113 : // For elements with associated CSS layout box, the values fill-box,
2114 : // stroke-box and view-box compute to the initial value of mask-clip.
2115 1002 : if (IsSVGStyleGeometryBox(aBox)) {
2116 0 : return StyleGeometryBox::BorderBox;
2117 : }
2118 : } else {
2119 : // For SVG elements without associated CSS layout box, the values
2120 : // content-box, padding-box, border-box and margin-box compute to fill-box.
2121 0 : if (IsHTMLStyleGeometryBox(aBox)) {
2122 0 : return StyleGeometryBox::FillBox;
2123 : }
2124 : }
2125 :
2126 1002 : return aBox;
2127 : }
2128 :
2129 : bool
2130 357 : nsCSSRendering::ImageLayerClipState::IsValid() const
2131 : {
2132 : // mDirtyRectInDevPx comes from mDirtyRectInAppUnits. mDirtyRectInAppUnits
2133 : // can not be empty if mDirtyRectInDevPx is not.
2134 357 : if (!mDirtyRectInDevPx.IsEmpty() && mDirtyRectInAppUnits.IsEmpty()) {
2135 0 : return false;
2136 : }
2137 :
2138 357 : if (mHasRoundedCorners == mClippedRadii.IsEmpty()) {
2139 0 : return false;
2140 : }
2141 :
2142 357 : return true;
2143 : }
2144 :
2145 : /* static */ void
2146 357 : nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer,
2147 : nsIFrame* aForFrame, const nsStyleBorder& aBorder,
2148 : const nsRect& aBorderArea, const nsRect& aCallerDirtyRect,
2149 : bool aWillPaintBorder, nscoord aAppUnitsPerPixel,
2150 : /* out */ ImageLayerClipState* aClipState)
2151 : {
2152 357 : aClipState->mHasRoundedCorners = false;
2153 357 : aClipState->mHasAdditionalBGClipArea = false;
2154 357 : aClipState->mAdditionalBGClipArea.SetEmpty();
2155 357 : aClipState->mCustomClip = false;
2156 :
2157 357 : StyleGeometryBox layerClip = ComputeBoxValue(aForFrame, aLayer.mClip);
2158 357 : if (IsSVGStyleGeometryBox(layerClip)) {
2159 0 : MOZ_ASSERT(aForFrame->IsFrameOfType(nsIFrame::eSVG) &&
2160 : !aForFrame->IsSVGOuterSVGFrame());
2161 :
2162 : // The coordinate space of clipArea is svg user space.
2163 : nsRect clipArea =
2164 0 : nsLayoutUtils::ComputeGeometryBox(aForFrame, layerClip);
2165 :
2166 : nsRect strokeBox = (layerClip == StyleGeometryBox::StrokeBox)
2167 : ? clipArea
2168 0 : : nsLayoutUtils::ComputeGeometryBox(aForFrame, StyleGeometryBox::StrokeBox);
2169 0 : nsRect clipAreaRelativeToStrokeBox = clipArea - strokeBox.TopLeft();
2170 :
2171 : // aBorderArea is the stroke-box area in a coordinate space defined by
2172 : // the caller. This coordinate space can be svg user space of aForFrame,
2173 : // the space of aForFrame's reference-frame, or anything else.
2174 : //
2175 : // Which coordinate space chosen for aBorderArea is not matter. What
2176 : // matter is to ensure returning aClipState->mBGClipArea in the consistent
2177 : // coordiante space with aBorderArea. So we evaluate the position of clip
2178 : // area base on the position of aBorderArea here.
2179 0 : aClipState->mBGClipArea =
2180 0 : clipAreaRelativeToStrokeBox + aBorderArea.TopLeft();
2181 :
2182 0 : SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect,
2183 : aAppUnitsPerPixel, &aClipState->mDirtyRectInAppUnits,
2184 0 : &aClipState->mDirtyRectInDevPx);
2185 0 : MOZ_ASSERT(aClipState->IsValid());
2186 0 : return;
2187 : }
2188 :
2189 357 : if (layerClip == StyleGeometryBox::NoClip) {
2190 0 : aClipState->mBGClipArea = aCallerDirtyRect;
2191 :
2192 0 : SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect,
2193 : aAppUnitsPerPixel, &aClipState->mDirtyRectInAppUnits,
2194 0 : &aClipState->mDirtyRectInDevPx);
2195 0 : MOZ_ASSERT(aClipState->IsValid());
2196 0 : return;
2197 : }
2198 :
2199 357 : MOZ_ASSERT(!aForFrame->IsFrameOfType(nsIFrame::eSVG) ||
2200 : aForFrame->IsSVGOuterSVGFrame());
2201 :
2202 : // Compute the outermost boundary of the area that might be painted.
2203 : // Same coordinate space as aBorderArea.
2204 357 : Sides skipSides = aForFrame->GetSkipSides();
2205 : nsRect clipBorderArea =
2206 714 : ::BoxDecorationRectForBorder(aForFrame, aBorderArea, skipSides, &aBorder);
2207 :
2208 357 : bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea,
2209 357 : clipBorderArea, aClipState->mRadii);
2210 :
2211 : bool isSolidBorder =
2212 357 : aWillPaintBorder && IsOpaqueBorder(aBorder);
2213 357 : if (isSolidBorder && layerClip == StyleGeometryBox::BorderBox) {
2214 : // If we have rounded corners, we need to inflate the background
2215 : // drawing area a bit to avoid seams between the border and
2216 : // background.
2217 11 : layerClip = haveRoundedCorners
2218 11 : ? StyleGeometryBox::MozAlmostPadding
2219 : : StyleGeometryBox::PaddingBox;
2220 : }
2221 :
2222 357 : aClipState->mBGClipArea = clipBorderArea;
2223 :
2224 363 : if (aForFrame->IsScrollFrame() &&
2225 6 : NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL == aLayer.mAttachment) {
2226 : // As of this writing, this is still in discussion in the CSS Working Group
2227 : // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
2228 :
2229 : // The rectangle for 'background-clip' scrolls with the content,
2230 : // but the background is also clipped at a non-scrolling 'padding-box'
2231 : // like the content. (See below.)
2232 : // Therefore, only 'content-box' makes a difference here.
2233 0 : if (layerClip == StyleGeometryBox::ContentBox) {
2234 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame);
2235 : // Clip at a rectangle attached to the scrolled content.
2236 0 : aClipState->mHasAdditionalBGClipArea = true;
2237 0 : aClipState->mAdditionalBGClipArea = nsRect(
2238 0 : aClipState->mBGClipArea.TopLeft()
2239 0 : + scrollableFrame->GetScrolledFrame()->GetPosition()
2240 : // For the dir=rtl case:
2241 0 : + scrollableFrame->GetScrollRange().TopLeft(),
2242 0 : scrollableFrame->GetScrolledRect().Size());
2243 0 : nsMargin padding = aForFrame->GetUsedPadding();
2244 : // padding-bottom is ignored on scrollable frames:
2245 : // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
2246 0 : padding.bottom = 0;
2247 0 : padding.ApplySkipSides(skipSides);
2248 0 : aClipState->mAdditionalBGClipArea.Deflate(padding);
2249 : }
2250 :
2251 : // Also clip at a non-scrolling, rounded-corner 'padding-box',
2252 : // same as the scrolled content because of the 'overflow' property.
2253 0 : layerClip = StyleGeometryBox::PaddingBox;
2254 : }
2255 :
2256 : // See the comment of StyleGeometryBox::Margin.
2257 : // Hitting this assertion means we decide to turn on margin-box support for
2258 : // positioned mask from CSS parser and style system. In this case, you
2259 : // should *inflate* mBGClipArea by the margin returning from
2260 : // aForFrame->GetUsedMargin() in the code chunk bellow.
2261 357 : MOZ_ASSERT(layerClip != StyleGeometryBox::MarginBox,
2262 : "StyleGeometryBox::MarginBox rendering is not supported yet.\n");
2263 :
2264 357 : if (layerClip != StyleGeometryBox::BorderBox &&
2265 : layerClip != StyleGeometryBox::Text) {
2266 203 : nsMargin border = aForFrame->GetUsedBorder();
2267 203 : if (layerClip == StyleGeometryBox::MozAlmostPadding) {
2268 : // Reduce |border| by 1px (device pixels) on all sides, if
2269 : // possible, so that we don't get antialiasing seams between the
2270 : // {background|mask} and border.
2271 0 : border.top = std::max(0, border.top - aAppUnitsPerPixel);
2272 0 : border.right = std::max(0, border.right - aAppUnitsPerPixel);
2273 0 : border.bottom = std::max(0, border.bottom - aAppUnitsPerPixel);
2274 0 : border.left = std::max(0, border.left - aAppUnitsPerPixel);
2275 203 : } else if (layerClip != StyleGeometryBox::PaddingBox) {
2276 72 : NS_ASSERTION(layerClip == StyleGeometryBox::ContentBox,
2277 : "unexpected background-clip");
2278 72 : border += aForFrame->GetUsedPadding();
2279 : }
2280 203 : border.ApplySkipSides(skipSides);
2281 203 : aClipState->mBGClipArea.Deflate(border);
2282 :
2283 203 : if (haveRoundedCorners) {
2284 72 : nsIFrame::InsetBorderRadii(aClipState->mRadii, border);
2285 : }
2286 : }
2287 :
2288 357 : if (haveRoundedCorners) {
2289 72 : auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
2290 72 : nsCSSRendering::ComputePixelRadii(aClipState->mRadii, d2a, &aClipState->mClippedRadii);
2291 72 : aClipState->mHasRoundedCorners = !aClipState->mClippedRadii.IsEmpty();
2292 : }
2293 :
2294 :
2295 357 : if (!haveRoundedCorners && aClipState->mHasAdditionalBGClipArea) {
2296 : // Do the intersection here to account for the fast path(?) below.
2297 0 : aClipState->mBGClipArea =
2298 0 : aClipState->mBGClipArea.Intersect(aClipState->mAdditionalBGClipArea);
2299 0 : aClipState->mHasAdditionalBGClipArea = false;
2300 : }
2301 :
2302 357 : SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, aAppUnitsPerPixel,
2303 : &aClipState->mDirtyRectInAppUnits,
2304 357 : &aClipState->mDirtyRectInDevPx);
2305 :
2306 357 : MOZ_ASSERT(aClipState->IsValid());
2307 : }
2308 :
2309 : static void
2310 3 : SetupImageLayerClip(nsCSSRendering::ImageLayerClipState& aClipState,
2311 : gfxContext *aCtx, nscoord aAppUnitsPerPixel,
2312 : gfxContextAutoSaveRestore* aAutoSR)
2313 : {
2314 3 : if (aClipState.mDirtyRectInDevPx.IsEmpty()) {
2315 : // Our caller won't draw anything under this condition, so no need
2316 : // to set more up.
2317 0 : return;
2318 : }
2319 :
2320 3 : if (aClipState.mCustomClip) {
2321 : // We don't support custom clips and rounded corners, arguably a bug, but
2322 : // table painting seems to depend on it.
2323 0 : return;
2324 : }
2325 :
2326 : // If we have rounded corners, clip all subsequent drawing to the
2327 : // rounded rectangle defined by bgArea and bgRadii (we don't know
2328 : // whether the rounded corners intrude on the dirtyRect or not).
2329 : // Do not do this if we have a caller-provided clip rect --
2330 : // as above with bgArea, arguably a bug, but table painting seems
2331 : // to depend on it.
2332 :
2333 3 : if (aClipState.mHasAdditionalBGClipArea) {
2334 : gfxRect bgAreaGfx = nsLayoutUtils::RectToGfxRect(
2335 0 : aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
2336 0 : bgAreaGfx.Round();
2337 0 : gfxUtils::ConditionRect(bgAreaGfx);
2338 :
2339 0 : aAutoSR->EnsureSaved(aCtx);
2340 0 : aCtx->NewPath();
2341 0 : aCtx->Rectangle(bgAreaGfx, true);
2342 0 : aCtx->Clip();
2343 : }
2344 :
2345 3 : if (aClipState.mHasRoundedCorners) {
2346 0 : Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
2347 0 : bgAreaGfx.Round();
2348 :
2349 0 : if (bgAreaGfx.IsEmpty()) {
2350 : // I think it's become possible to hit this since
2351 : // https://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
2352 0 : NS_WARNING("converted background area should not be empty");
2353 : // Make our caller not do anything.
2354 0 : aClipState.mDirtyRectInDevPx.SizeTo(gfxSize(0.0, 0.0));
2355 0 : return;
2356 : }
2357 :
2358 0 : aAutoSR->EnsureSaved(aCtx);
2359 :
2360 : RefPtr<Path> roundedRect =
2361 0 : MakePathForRoundedRect(*aCtx->GetDrawTarget(), bgAreaGfx,
2362 0 : aClipState.mClippedRadii);
2363 0 : aCtx->Clip(roundedRect);
2364 : }
2365 : }
2366 :
2367 : static void
2368 0 : DrawBackgroundColor(nsCSSRendering::ImageLayerClipState& aClipState,
2369 : gfxContext *aCtx, nscoord aAppUnitsPerPixel)
2370 : {
2371 0 : if (aClipState.mDirtyRectInDevPx.IsEmpty()) {
2372 : // Our caller won't draw anything under this condition, so no need
2373 : // to set more up.
2374 0 : return;
2375 : }
2376 :
2377 0 : DrawTarget* drawTarget = aCtx->GetDrawTarget();
2378 :
2379 : // We don't support custom clips and rounded corners, arguably a bug, but
2380 : // table painting seems to depend on it.
2381 0 : if (!aClipState.mHasRoundedCorners || aClipState.mCustomClip) {
2382 0 : aCtx->NewPath();
2383 0 : aCtx->Rectangle(aClipState.mDirtyRectInDevPx, true);
2384 0 : aCtx->Fill();
2385 0 : return;
2386 : }
2387 :
2388 0 : Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
2389 0 : bgAreaGfx.Round();
2390 :
2391 0 : if (bgAreaGfx.IsEmpty()) {
2392 : // I think it's become possible to hit this since
2393 : // https://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
2394 0 : NS_WARNING("converted background area should not be empty");
2395 : // Make our caller not do anything.
2396 0 : aClipState.mDirtyRectInDevPx.SizeTo(gfxSize(0.0, 0.0));
2397 0 : return;
2398 : }
2399 :
2400 0 : aCtx->Save();
2401 0 : gfxRect dirty = ThebesRect(bgAreaGfx).Intersect(aClipState.mDirtyRectInDevPx);
2402 :
2403 0 : aCtx->NewPath();
2404 0 : aCtx->Rectangle(dirty, true);
2405 0 : aCtx->Clip();
2406 :
2407 0 : if (aClipState.mHasAdditionalBGClipArea) {
2408 : gfxRect bgAdditionalAreaGfx = nsLayoutUtils::RectToGfxRect(
2409 0 : aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
2410 0 : bgAdditionalAreaGfx.Round();
2411 0 : gfxUtils::ConditionRect(bgAdditionalAreaGfx);
2412 0 : aCtx->NewPath();
2413 0 : aCtx->Rectangle(bgAdditionalAreaGfx, true);
2414 0 : aCtx->Clip();
2415 : }
2416 :
2417 : RefPtr<Path> roundedRect =
2418 0 : MakePathForRoundedRect(*drawTarget, bgAreaGfx, aClipState.mClippedRadii);
2419 0 : aCtx->SetPath(roundedRect);
2420 0 : aCtx->Fill();
2421 0 : aCtx->Restore();
2422 : }
2423 :
2424 : nscolor
2425 506 : nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
2426 : nsStyleContext* aStyleContext,
2427 : nsIFrame* aFrame,
2428 : bool& aDrawBackgroundImage,
2429 : bool& aDrawBackgroundColor)
2430 : {
2431 506 : aDrawBackgroundImage = true;
2432 506 : aDrawBackgroundColor = true;
2433 :
2434 506 : const nsStyleVisibility* visibility = aStyleContext->StyleVisibility();
2435 :
2436 1012 : if (visibility->mColorAdjust != NS_STYLE_COLOR_ADJUST_EXACT &&
2437 506 : aFrame->HonorPrintBackgroundSettings()) {
2438 386 : aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
2439 386 : aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
2440 : }
2441 :
2442 506 : const nsStyleBackground *bg = aStyleContext->StyleBackground();
2443 : nscolor bgColor;
2444 506 : if (aDrawBackgroundColor) {
2445 : bgColor = aStyleContext->
2446 506 : GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
2447 506 : if (NS_GET_A(bgColor) == 0) {
2448 279 : aDrawBackgroundColor = false;
2449 : }
2450 : } else {
2451 : // If GetBackgroundColorDraw() is false, we are still expected to
2452 : // draw color in the background of any frame that's not completely
2453 : // transparent, but we are expected to use white instead of whatever
2454 : // color was specified.
2455 0 : bgColor = NS_RGB(255, 255, 255);
2456 0 : if (aDrawBackgroundImage || !bg->IsTransparent(aStyleContext)) {
2457 0 : aDrawBackgroundColor = true;
2458 : } else {
2459 0 : bgColor = NS_RGBA(0,0,0,0);
2460 : }
2461 : }
2462 :
2463 : // We can skip painting the background color if a background image is opaque.
2464 506 : nsStyleImageLayers::Repeat repeat = bg->BottomLayer().mRepeat;
2465 538 : bool xFullRepeat = repeat.mXRepeat == StyleImageLayerRepeat::Repeat ||
2466 538 : repeat.mXRepeat == StyleImageLayerRepeat::Round;
2467 578 : bool yFullRepeat = repeat.mYRepeat == StyleImageLayerRepeat::Repeat ||
2468 578 : repeat.mYRepeat == StyleImageLayerRepeat::Round;
2469 1239 : if (aDrawBackgroundColor &&
2470 406 : xFullRepeat && yFullRepeat &&
2471 689 : bg->BottomLayer().mImage.IsOpaque() &&
2472 0 : bg->BottomLayer().mBlendMode == NS_STYLE_BLEND_NORMAL) {
2473 0 : aDrawBackgroundColor = false;
2474 : }
2475 :
2476 506 : return bgColor;
2477 : }
2478 :
2479 : static CompositionOp
2480 59 : DetermineCompositionOp(const nsCSSRendering::PaintBGParams& aParams,
2481 : const nsStyleImageLayers& aLayers,
2482 : uint32_t aLayerIndex)
2483 : {
2484 59 : if (aParams.layer >= 0) {
2485 : // When drawing a single layer, use the specified composition op.
2486 59 : return aParams.compositionOp;
2487 : }
2488 :
2489 0 : const nsStyleImageLayers::Layer& layer = aLayers.mLayers[aLayerIndex];
2490 : // When drawing all layers, get the compositon op from each image layer.
2491 0 : if (aParams.paintFlags & nsCSSRendering::PAINTBG_MASK_IMAGE) {
2492 : // Always using OP_OVER mode while drawing the bottom mask layer.
2493 0 : if (aLayerIndex == (aLayers.mImageCount - 1)) {
2494 0 : return CompositionOp::OP_OVER;
2495 : }
2496 :
2497 0 : return nsCSSRendering::GetGFXCompositeMode(layer.mComposite);
2498 : }
2499 :
2500 0 : return nsCSSRendering::GetGFXBlendMode(layer.mBlendMode);
2501 : }
2502 :
2503 : DrawResult
2504 59 : nsCSSRendering::PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
2505 : gfxContext& aRenderingCtx,
2506 : nsStyleContext *aBackgroundSC,
2507 : const nsStyleBorder& aBorder)
2508 : {
2509 59 : NS_PRECONDITION(aParams.frame,
2510 : "Frame is expected to be provided to PaintStyleImageLayerWithSC");
2511 :
2512 : // If we're drawing all layers, aCompositonOp is ignored, so make sure that
2513 : // it was left at its default value.
2514 59 : MOZ_ASSERT(aParams.layer != -1 ||
2515 : aParams.compositionOp == CompositionOp::OP_OVER);
2516 :
2517 : // Check to see if we have an appearance defined. If so, we let the theme
2518 : // renderer draw the background and bail out.
2519 : // XXXzw this ignores aParams.bgClipRect.
2520 59 : const nsStyleDisplay* displayData = aParams.frame->StyleDisplay();
2521 59 : if (displayData->mAppearance) {
2522 0 : nsITheme *theme = aParams.presCtx.GetTheme();
2523 0 : if (theme && theme->ThemeSupportsWidget(&aParams.presCtx,
2524 0 : aParams.frame,
2525 0 : displayData->mAppearance)) {
2526 0 : nsRect drawing(aParams.borderArea);
2527 0 : theme->GetWidgetOverflow(aParams.presCtx.DeviceContext(),
2528 0 : aParams.frame, displayData->mAppearance,
2529 0 : &drawing);
2530 0 : drawing.IntersectRect(drawing, aParams.dirtyRect);
2531 0 : theme->DrawWidgetBackground(&aRenderingCtx, aParams.frame,
2532 0 : displayData->mAppearance, aParams.borderArea,
2533 0 : drawing);
2534 0 : return DrawResult::SUCCESS;
2535 : }
2536 : }
2537 :
2538 : // For canvas frames (in the CSS sense) we draw the background color using
2539 : // a solid color item that gets added in nsLayoutUtils::PaintFrame,
2540 : // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
2541 : // color may be moved into nsDisplayCanvasBackground by
2542 : // nsPresShell::AddCanvasBackgroundColorItem, and painted by
2543 : // nsDisplayCanvasBackground directly.) Either way we don't need to
2544 : // paint the background color here.
2545 59 : bool isCanvasFrame = IsCanvasFrame(aParams.frame);
2546 :
2547 : // Determine whether we are drawing background images and/or
2548 : // background colors.
2549 : bool drawBackgroundImage;
2550 : bool drawBackgroundColor;
2551 :
2552 59 : nscolor bgColor = DetermineBackgroundColor(&aParams.presCtx,
2553 : aBackgroundSC,
2554 59 : aParams.frame,
2555 : drawBackgroundImage,
2556 59 : drawBackgroundColor);
2557 :
2558 59 : bool paintMask = (aParams.paintFlags & PAINTBG_MASK_IMAGE);
2559 : const nsStyleImageLayers& layers = paintMask ?
2560 3 : aBackgroundSC->StyleSVGReset()->mMask :
2561 62 : aBackgroundSC->StyleBackground()->mImage;
2562 : // If we're drawing a specific layer, we don't want to draw the
2563 : // background color.
2564 59 : if ((drawBackgroundColor && aParams.layer >= 0) || paintMask) {
2565 31 : drawBackgroundColor = false;
2566 : }
2567 :
2568 : // At this point, drawBackgroundImage and drawBackgroundColor are
2569 : // true if and only if we are actually supposed to paint an image or
2570 : // color into aDirtyRect, respectively.
2571 59 : if (!drawBackgroundImage && !drawBackgroundColor)
2572 0 : return DrawResult::SUCCESS;
2573 :
2574 : // The 'bgClipArea' (used only by the image tiling logic, far below)
2575 : // is the caller-provided aParams.bgClipRect if any, or else the area
2576 : // determined by the value of 'background-clip' in
2577 : // SetupCurrentBackgroundClip. (Arguably it should be the
2578 : // intersection, but that breaks the table painter -- in particular,
2579 : // taking the intersection breaks reftests/bugs/403249-1[ab].)
2580 59 : nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
2581 118 : ImageLayerClipState clipState;
2582 59 : if (aParams.bgClipRect) {
2583 56 : clipState.mBGClipArea = *aParams.bgClipRect;
2584 56 : clipState.mCustomClip = true;
2585 56 : clipState.mHasRoundedCorners = false;
2586 56 : SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
2587 : &clipState.mDirtyRectInAppUnits,
2588 56 : &clipState.mDirtyRectInDevPx);
2589 : } else {
2590 6 : GetImageLayerClip(layers.BottomLayer(),
2591 3 : aParams.frame, aBorder, aParams.borderArea,
2592 : aParams.dirtyRect,
2593 3 : (aParams.paintFlags & PAINTBG_WILL_PAINT_BORDER),
2594 : appUnitsPerPixel,
2595 3 : &clipState);
2596 : }
2597 :
2598 : // If we might be using a background color, go ahead and set it now.
2599 59 : if (drawBackgroundColor && !isCanvasFrame) {
2600 0 : aRenderingCtx.SetColor(Color::FromABGR(bgColor));
2601 : }
2602 :
2603 : // If there is no background image, draw a color. (If there is
2604 : // neither a background image nor a color, we wouldn't have gotten
2605 : // this far.)
2606 59 : if (!drawBackgroundImage) {
2607 0 : if (!isCanvasFrame) {
2608 0 : DrawBackgroundColor(clipState, &aRenderingCtx, appUnitsPerPixel);
2609 : }
2610 0 : return DrawResult::SUCCESS;
2611 : }
2612 :
2613 59 : if (layers.mImageCount < 1) {
2614 : // Return if there are no background layers, all work from this point
2615 : // onwards happens iteratively on these.
2616 0 : return DrawResult::SUCCESS;
2617 : }
2618 :
2619 59 : MOZ_ASSERT((aParams.layer < 0) ||
2620 : (layers.mImageCount > uint32_t(aParams.layer)));
2621 59 : bool drawAllLayers = (aParams.layer < 0);
2622 :
2623 : // Ensure we get invalidated for loads of the image. We need to do
2624 : // this here because this might be the only code that knows about the
2625 : // association of the style data with the frame.
2626 59 : if (aBackgroundSC != aParams.frame->StyleContext()) {
2627 0 : uint32_t startLayer = drawAllLayers ? layers.mImageCount - 1
2628 0 : : aParams.layer;
2629 0 : uint32_t count = drawAllLayers ? layers.mImageCount : 1;
2630 0 : NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, layers, startLayer,
2631 : count) {
2632 0 : aParams.frame->AssociateImage(layers.mLayers[i].mImage,
2633 0 : &aParams.presCtx);
2634 : }
2635 : }
2636 :
2637 : // The background color is rendered over the entire dirty area,
2638 : // even if the image isn't.
2639 59 : if (drawBackgroundColor && !isCanvasFrame) {
2640 0 : DrawBackgroundColor(clipState, &aRenderingCtx, appUnitsPerPixel);
2641 : }
2642 :
2643 59 : if (!drawBackgroundImage) {
2644 0 : return DrawResult::SUCCESS; // No need to draw layer image, we can early
2645 : // return now.
2646 : }
2647 :
2648 : // Compute the outermost boundary of the area that might be painted.
2649 : // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
2650 59 : Sides skipSides = aParams.frame->GetSkipSides();
2651 : nsRect paintBorderArea =
2652 59 : ::BoxDecorationRectForBackground(aParams.frame, aParams.borderArea,
2653 177 : skipSides, &aBorder);
2654 : nsRect clipBorderArea =
2655 59 : ::BoxDecorationRectForBorder(aParams.frame, aParams.borderArea,
2656 177 : skipSides, &aBorder);
2657 :
2658 59 : DrawResult result = DrawResult::SUCCESS;
2659 59 : StyleGeometryBox currentBackgroundClip = StyleGeometryBox::BorderBox;
2660 : uint32_t count = drawAllLayers
2661 118 : ? layers.mImageCount // iterate all image layers.
2662 118 : : layers.mImageCount - aParams.layer; // iterate from the bottom layer to
2663 : // the 'aParams.layer-th' layer.
2664 142 : NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, layers,
2665 : layers.mImageCount - 1,
2666 : count) {
2667 : // NOTE: no Save() yet, we do that later by calling autoSR.EnsureSaved(ctx)
2668 : // in the cases we need it.
2669 142 : gfxContextAutoSaveRestore autoSR;
2670 83 : const nsStyleImageLayers::Layer& layer = layers.mLayers[i];
2671 :
2672 83 : if (!aParams.bgClipRect) {
2673 3 : bool isBottomLayer = (i == layers.mImageCount - 1);
2674 3 : if (currentBackgroundClip != layer.mClip || isBottomLayer) {
2675 3 : currentBackgroundClip = layer.mClip;
2676 : // For the bottom layer, we already called GetImageLayerClip above
2677 : // and it stored its results in clipState.
2678 3 : if (!isBottomLayer) {
2679 0 : GetImageLayerClip(layer, aParams.frame,
2680 : aBorder, aParams.borderArea, aParams.dirtyRect,
2681 0 : (aParams.paintFlags & PAINTBG_WILL_PAINT_BORDER),
2682 0 : appUnitsPerPixel, &clipState);
2683 : }
2684 : SetupImageLayerClip(clipState, &aRenderingCtx,
2685 3 : appUnitsPerPixel, &autoSR);
2686 3 : if (!clipBorderArea.IsEqualEdges(aParams.borderArea)) {
2687 : // We're drawing the background for the joined continuation boxes
2688 : // so we need to clip that to the slice that we want for this
2689 : // frame.
2690 : gfxRect clip =
2691 0 : nsLayoutUtils::RectToGfxRect(aParams.borderArea, appUnitsPerPixel);
2692 0 : autoSR.EnsureSaved(&aRenderingCtx);
2693 0 : aRenderingCtx.NewPath();
2694 0 : aRenderingCtx.SnappedRectangle(clip);
2695 0 : aRenderingCtx.Clip();
2696 : }
2697 : }
2698 : }
2699 :
2700 : // Skip the following layer preparing and painting code if the current
2701 : // layer is not selected for drawing.
2702 83 : if (aParams.layer >= 0 && i != (uint32_t)aParams.layer) {
2703 24 : continue;
2704 : }
2705 : nsBackgroundLayerState state =
2706 59 : PrepareImageLayer(&aParams.presCtx, aParams.frame,
2707 59 : aParams.paintFlags, paintBorderArea,
2708 118 : clipState.mBGClipArea, layer, nullptr);
2709 59 : result &= state.mImageRenderer.PrepareResult();
2710 :
2711 : // Skip the layer painting code if we found the dirty region is empty.
2712 59 : if (clipState.mDirtyRectInDevPx.IsEmpty()) {
2713 0 : continue;
2714 : }
2715 :
2716 59 : if (!state.mFillArea.IsEmpty()) {
2717 59 : CompositionOp co = DetermineCompositionOp(aParams, layers, i);
2718 59 : if (co != CompositionOp::OP_OVER) {
2719 0 : NS_ASSERTION(aRenderingCtx.CurrentOp() == CompositionOp::OP_OVER,
2720 : "It is assumed the initial op is OP_OVER, when it is "
2721 : "restored later");
2722 0 : aRenderingCtx.SetOp(co);
2723 : }
2724 :
2725 : result &=
2726 59 : state.mImageRenderer.DrawLayer(&aParams.presCtx,
2727 : aRenderingCtx,
2728 : state.mDestArea, state.mFillArea,
2729 118 : state.mAnchor + paintBorderArea.TopLeft(),
2730 : clipState.mDirtyRectInAppUnits,
2731 118 : state.mRepeatSize, aParams.opacity);
2732 :
2733 59 : if (co != CompositionOp::OP_OVER) {
2734 0 : aRenderingCtx.SetOp(CompositionOp::OP_OVER);
2735 : }
2736 : }
2737 : }
2738 :
2739 59 : return result;
2740 : }
2741 :
2742 : DrawResult
2743 0 : nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayerWithSC(const PaintBGParams& aParams,
2744 : mozilla::wr::DisplayListBuilder& aBuilder,
2745 : const mozilla::layers::StackingContextHelper& aSc,
2746 : nsTArray<WebRenderParentCommand>& aParentCommands,
2747 : mozilla::layers::WebRenderDisplayItemLayer* aLayer,
2748 : mozilla::layers::WebRenderLayerManager* aManager,
2749 : nsDisplayItem* aItem,
2750 : nsStyleContext *aBackgroundSC,
2751 : const nsStyleBorder& aBorder)
2752 : {
2753 0 : MOZ_ASSERT(!(aParams.paintFlags & PAINTBG_MASK_IMAGE));
2754 :
2755 0 : nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
2756 0 : ImageLayerClipState clipState;
2757 :
2758 0 : clipState.mBGClipArea = *aParams.bgClipRect;
2759 0 : clipState.mCustomClip = true;
2760 0 : clipState.mHasRoundedCorners = false;
2761 0 : SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
2762 : &clipState.mDirtyRectInAppUnits,
2763 0 : &clipState.mDirtyRectInDevPx);
2764 :
2765 : // Compute the outermost boundary of the area that might be painted.
2766 : // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
2767 0 : Sides skipSides = aParams.frame->GetSkipSides();
2768 : nsRect paintBorderArea =
2769 0 : ::BoxDecorationRectForBackground(aParams.frame, aParams.borderArea,
2770 0 : skipSides, &aBorder);
2771 :
2772 0 : const nsStyleImageLayers& layers = aBackgroundSC->StyleBackground()->mImage;
2773 0 : const nsStyleImageLayers::Layer& layer = layers.mLayers[aParams.layer];
2774 :
2775 : // Skip the following layer painting code if we found the dirty region is
2776 : // empty or the current layer is not selected for drawing.
2777 0 : if (clipState.mDirtyRectInDevPx.IsEmpty()) {
2778 0 : return DrawResult::SUCCESS;
2779 : }
2780 :
2781 0 : DrawResult result = DrawResult::SUCCESS;
2782 : nsBackgroundLayerState state =
2783 0 : PrepareImageLayer(&aParams.presCtx, aParams.frame,
2784 0 : aParams.paintFlags, paintBorderArea,
2785 0 : clipState.mBGClipArea, layer, nullptr);
2786 0 : result &= state.mImageRenderer.PrepareResult();
2787 0 : if (!state.mFillArea.IsEmpty()) {
2788 0 : return state.mImageRenderer.BuildWebRenderDisplayItemsForLayer(&aParams.presCtx,
2789 : aBuilder, aSc, aParentCommands,
2790 : aLayer, aManager, aItem,
2791 : state.mDestArea, state.mFillArea,
2792 0 : state.mAnchor + paintBorderArea.TopLeft(),
2793 : clipState.mDirtyRectInAppUnits,
2794 0 : state.mRepeatSize, aParams.opacity);
2795 : }
2796 :
2797 0 : return result;
2798 : }
2799 :
2800 : nsRect
2801 645 : nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
2802 : nsIFrame* aForFrame,
2803 : const nsRect& aBorderArea,
2804 : const nsStyleImageLayers::Layer& aLayer,
2805 : nsIFrame** aAttachedToFrame,
2806 : bool* aOutIsTransformedFixed)
2807 : {
2808 : // Compute {background|mask} origin area relative to aBorderArea now as we
2809 : // may need it to compute the effective image size for a CSS gradient.
2810 1290 : nsRect positionArea;
2811 :
2812 : StyleGeometryBox layerOrigin =
2813 645 : ComputeBoxValue(aForFrame, aLayer.mOrigin);
2814 :
2815 645 : if (IsSVGStyleGeometryBox(layerOrigin)) {
2816 0 : MOZ_ASSERT(aForFrame->IsFrameOfType(nsIFrame::eSVG) &&
2817 : !aForFrame->IsSVGOuterSVGFrame());
2818 0 : *aAttachedToFrame = aForFrame;
2819 :
2820 0 : positionArea =
2821 0 : nsLayoutUtils::ComputeGeometryBox(aForFrame, layerOrigin);
2822 :
2823 0 : nsPoint toStrokeBoxOffset = nsPoint(0, 0);
2824 0 : if (layerOrigin != StyleGeometryBox::StrokeBox) {
2825 : nsRect strokeBox =
2826 : nsLayoutUtils::ComputeGeometryBox(aForFrame,
2827 0 : StyleGeometryBox::StrokeBox);
2828 0 : toStrokeBoxOffset = positionArea.TopLeft() - strokeBox.TopLeft();
2829 : }
2830 :
2831 : // For SVG frames, the return value is relative to the stroke box
2832 0 : return nsRect(toStrokeBoxOffset, positionArea.Size());
2833 : }
2834 :
2835 645 : MOZ_ASSERT(!aForFrame->IsFrameOfType(nsIFrame::eSVG) ||
2836 : aForFrame->IsSVGOuterSVGFrame());
2837 :
2838 645 : LayoutFrameType frameType = aForFrame->Type();
2839 645 : nsIFrame* geometryFrame = aForFrame;
2840 645 : if (MOZ_UNLIKELY(frameType == LayoutFrameType::Scroll &&
2841 : NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL == aLayer.mAttachment)) {
2842 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame);
2843 0 : positionArea = nsRect(
2844 0 : scrollableFrame->GetScrolledFrame()->GetPosition()
2845 : // For the dir=rtl case:
2846 0 : + scrollableFrame->GetScrollRange().TopLeft(),
2847 0 : scrollableFrame->GetScrolledRect().Size());
2848 : // The ScrolledRect’s size does not include the borders or scrollbars,
2849 : // reverse the handling of background-origin
2850 : // compared to the common case below.
2851 0 : if (layerOrigin == StyleGeometryBox::BorderBox) {
2852 0 : nsMargin border = geometryFrame->GetUsedBorder();
2853 0 : border.ApplySkipSides(geometryFrame->GetSkipSides());
2854 0 : positionArea.Inflate(border);
2855 0 : positionArea.Inflate(scrollableFrame->GetActualScrollbarSizes());
2856 0 : } else if (layerOrigin != StyleGeometryBox::PaddingBox) {
2857 0 : nsMargin padding = geometryFrame->GetUsedPadding();
2858 0 : padding.ApplySkipSides(geometryFrame->GetSkipSides());
2859 0 : positionArea.Deflate(padding);
2860 0 : NS_ASSERTION(layerOrigin == StyleGeometryBox::ContentBox,
2861 : "unknown background-origin value");
2862 : }
2863 0 : *aAttachedToFrame = aForFrame;
2864 0 : return positionArea;
2865 : }
2866 :
2867 645 : if (MOZ_UNLIKELY(frameType == LayoutFrameType::Canvas)) {
2868 0 : geometryFrame = aForFrame->PrincipalChildList().FirstChild();
2869 : // geometryFrame might be null if this canvas is a page created
2870 : // as an overflow container (e.g. the in-flow content has already
2871 : // finished and this page only displays the continuations of
2872 : // absolutely positioned content).
2873 0 : if (geometryFrame) {
2874 0 : positionArea = geometryFrame->GetRect();
2875 : }
2876 : } else {
2877 645 : positionArea = nsRect(nsPoint(0,0), aBorderArea.Size());
2878 : }
2879 :
2880 : // See the comment of StyleGeometryBox::MarginBox.
2881 : // Hitting this assertion means we decide to turn on margin-box support for
2882 : // positioned mask from CSS parser and style system. In this case, you
2883 : // should *inflate* positionArea by the margin returning from
2884 : // geometryFrame->GetUsedMargin() in the code chunk bellow.
2885 645 : MOZ_ASSERT(aLayer.mOrigin != StyleGeometryBox::MarginBox,
2886 : "StyleGeometryBox::MarginBox rendering is not supported yet.\n");
2887 :
2888 : // {background|mask} images are tiled over the '{background|mask}-clip' area
2889 : // but the origin of the tiling is based on the '{background|mask}-origin'
2890 : // area.
2891 645 : if (layerOrigin != StyleGeometryBox::BorderBox && geometryFrame) {
2892 637 : nsMargin border = geometryFrame->GetUsedBorder();
2893 637 : if (layerOrigin != StyleGeometryBox::PaddingBox) {
2894 0 : border += geometryFrame->GetUsedPadding();
2895 0 : NS_ASSERTION(layerOrigin == StyleGeometryBox::ContentBox,
2896 : "unknown background-origin value");
2897 : }
2898 637 : positionArea.Deflate(border);
2899 : }
2900 :
2901 645 : nsIFrame* attachedToFrame = aForFrame;
2902 645 : if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment) {
2903 : // If it's a fixed background attachment, then the image is placed
2904 : // relative to the viewport, which is the area of the root frame
2905 : // in a screen context or the page content frame in a print context.
2906 0 : attachedToFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
2907 0 : NS_ASSERTION(attachedToFrame, "no root frame");
2908 0 : nsIFrame* pageContentFrame = nullptr;
2909 0 : if (aPresContext->IsPaginated()) {
2910 : pageContentFrame = nsLayoutUtils::GetClosestFrameOfType(
2911 0 : aForFrame, LayoutFrameType::PageContent);
2912 0 : if (pageContentFrame) {
2913 0 : attachedToFrame = pageContentFrame;
2914 : }
2915 : // else this is an embedded shell and its root frame is what we want
2916 : }
2917 :
2918 : // If the background is affected by a transform, treat is as if it
2919 : // wasn't fixed.
2920 0 : if (nsLayoutUtils::IsTransformed(aForFrame, attachedToFrame)) {
2921 0 : attachedToFrame = aForFrame;
2922 0 : *aOutIsTransformedFixed = true;
2923 : } else {
2924 : // Set the background positioning area to the viewport's area
2925 : // (relative to aForFrame)
2926 0 : positionArea =
2927 0 : nsRect(-aForFrame->GetOffsetTo(attachedToFrame), attachedToFrame->GetSize());
2928 :
2929 0 : if (!pageContentFrame) {
2930 : // Subtract the size of scrollbars.
2931 : nsIScrollableFrame* scrollableFrame =
2932 0 : aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
2933 0 : if (scrollableFrame) {
2934 0 : nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes();
2935 0 : positionArea.Deflate(scrollbars);
2936 : }
2937 : }
2938 : }
2939 : }
2940 645 : *aAttachedToFrame = attachedToFrame;
2941 :
2942 645 : return positionArea;
2943 : }
2944 :
2945 : /* static */ nscoord
2946 0 : nsCSSRendering::ComputeRoundedSize(nscoord aCurrentSize, nscoord aPositioningSize)
2947 : {
2948 0 : float repeatCount = NS_roundf(float(aPositioningSize) / float(aCurrentSize));
2949 0 : if (repeatCount < 1.0f) {
2950 0 : return aPositioningSize;
2951 : }
2952 0 : return nscoord(NS_lround(float(aPositioningSize) / repeatCount));
2953 : }
2954 :
2955 : // Apply the CSS image sizing algorithm as it applies to background images.
2956 : // See http://www.w3.org/TR/css3-background/#the-background-size .
2957 : // aIntrinsicSize is the size that the background image 'would like to be'.
2958 : // It can be found by calling nsImageRenderer::ComputeIntrinsicSize.
2959 : static nsSize
2960 452 : ComputeDrawnSizeForBackground(const CSSSizeOrRatio& aIntrinsicSize,
2961 : const nsSize& aBgPositioningArea,
2962 : const nsStyleImageLayers::Size& aLayerSize,
2963 : StyleImageLayerRepeat aXRepeat,
2964 : StyleImageLayerRepeat aYRepeat)
2965 : {
2966 452 : nsSize imageSize;
2967 :
2968 : // Size is dictated by cover or contain rules.
2969 904 : if (aLayerSize.mWidthType == nsStyleImageLayers::Size::eContain ||
2970 452 : aLayerSize.mWidthType == nsStyleImageLayers::Size::eCover) {
2971 : nsImageRenderer::FitType fitType =
2972 0 : aLayerSize.mWidthType == nsStyleImageLayers::Size::eCover
2973 0 : ? nsImageRenderer::COVER
2974 0 : : nsImageRenderer::CONTAIN;
2975 : imageSize = nsImageRenderer::ComputeConstrainedSize(aBgPositioningArea,
2976 : aIntrinsicSize.mRatio,
2977 0 : fitType);
2978 : } else {
2979 : // No cover/contain constraint, use default algorithm.
2980 452 : CSSSizeOrRatio specifiedSize;
2981 452 : if (aLayerSize.mWidthType == nsStyleImageLayers::Size::eLengthPercentage) {
2982 216 : specifiedSize.SetWidth(
2983 216 : aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea));
2984 : }
2985 452 : if (aLayerSize.mHeightType == nsStyleImageLayers::Size::eLengthPercentage) {
2986 328 : specifiedSize.SetHeight(
2987 328 : aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
2988 : }
2989 :
2990 : imageSize = nsImageRenderer::ComputeConcreteSize(specifiedSize,
2991 : aIntrinsicSize,
2992 452 : aBgPositioningArea);
2993 : }
2994 :
2995 : // See https://www.w3.org/TR/css3-background/#background-size .
2996 : // "If 'background-repeat' is 'round' for one (or both) dimensions, there is a second
2997 : // step. The UA must scale the image in that dimension (or both dimensions) so that
2998 : // it fits a whole number of times in the background positioning area."
2999 : // "If 'background-repeat' is 'round' for one dimension only and if 'background-size'
3000 : // is 'auto' for the other dimension, then there is a third step: that other dimension
3001 : // is scaled so that the original aspect ratio is restored."
3002 452 : bool isRepeatRoundInBothDimensions = aXRepeat == StyleImageLayerRepeat::Round &&
3003 452 : aYRepeat == StyleImageLayerRepeat::Round;
3004 :
3005 : // Calculate the rounded size only if the background-size computation
3006 : // returned a correct size for the image.
3007 452 : if (imageSize.width && aXRepeat == StyleImageLayerRepeat::Round) {
3008 0 : imageSize.width =
3009 0 : nsCSSRendering::ComputeRoundedSize(imageSize.width,
3010 0 : aBgPositioningArea.width);
3011 0 : if (!isRepeatRoundInBothDimensions &&
3012 0 : aLayerSize.mHeightType == nsStyleImageLayers::Size::DimensionType::eAuto) {
3013 : // Restore intrinsic rato
3014 0 : if (aIntrinsicSize.mRatio.width) {
3015 0 : float scale = float(aIntrinsicSize.mRatio.height) / aIntrinsicSize.mRatio.width;
3016 0 : imageSize.height = NSCoordSaturatingNonnegativeMultiply(imageSize.width, scale);
3017 : }
3018 : }
3019 : }
3020 :
3021 : // Calculate the rounded size only if the background-size computation
3022 : // returned a correct size for the image.
3023 452 : if (imageSize.height && aYRepeat == StyleImageLayerRepeat::Round) {
3024 0 : imageSize.height =
3025 0 : nsCSSRendering::ComputeRoundedSize(imageSize.height,
3026 0 : aBgPositioningArea.height);
3027 0 : if (!isRepeatRoundInBothDimensions &&
3028 0 : aLayerSize.mWidthType == nsStyleImageLayers::Size::DimensionType::eAuto) {
3029 : // Restore intrinsic rato
3030 0 : if (aIntrinsicSize.mRatio.height) {
3031 0 : float scale = float(aIntrinsicSize.mRatio.width) / aIntrinsicSize.mRatio.height;
3032 0 : imageSize.width = NSCoordSaturatingNonnegativeMultiply(imageSize.height, scale);
3033 : }
3034 : }
3035 : }
3036 :
3037 452 : return imageSize;
3038 : }
3039 :
3040 : /* ComputeSpacedRepeatSize
3041 : * aImageDimension: the image width/height
3042 : * aAvailableSpace: the background positioning area width/height
3043 : * aRepeat: determine whether the image is repeated
3044 : * Returns the image size plus gap size of app units for use as spacing
3045 : */
3046 : static nscoord
3047 0 : ComputeSpacedRepeatSize(nscoord aImageDimension,
3048 : nscoord aAvailableSpace,
3049 : bool& aRepeat) {
3050 0 : float ratio = static_cast<float>(aAvailableSpace) / aImageDimension;
3051 :
3052 0 : if (ratio < 2.0f) { // If you can't repeat at least twice, then don't repeat.
3053 0 : aRepeat = false;
3054 0 : return aImageDimension;
3055 : } else {
3056 0 : aRepeat = true;
3057 0 : return (aAvailableSpace - aImageDimension) / (NSToIntFloor(ratio) - 1);
3058 : }
3059 : }
3060 :
3061 : /* static */ nscoord
3062 0 : nsCSSRendering::ComputeBorderSpacedRepeatSize(nscoord aImageDimension,
3063 : nscoord aAvailableSpace,
3064 : nscoord& aSpace)
3065 : {
3066 0 : int32_t count = aAvailableSpace / aImageDimension;
3067 0 : aSpace = (aAvailableSpace - aImageDimension * count) / (count + 1);
3068 0 : return aSpace + aImageDimension;
3069 : }
3070 :
3071 : nsBackgroundLayerState
3072 454 : nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
3073 : nsIFrame* aForFrame,
3074 : uint32_t aFlags,
3075 : const nsRect& aBorderArea,
3076 : const nsRect& aBGClipRect,
3077 : const nsStyleImageLayers::Layer& aLayer,
3078 : bool* aOutIsTransformedFixed)
3079 : {
3080 : /*
3081 : * The properties we need to keep in mind when drawing style image
3082 : * layers are:
3083 : *
3084 : * background-image/ mask-image
3085 : * background-repeat/ mask-repeat
3086 : * background-attachment
3087 : * background-position/ mask-position
3088 : * background-clip/ mask-clip
3089 : * background-origin/ mask-origin
3090 : * background-size/ mask-size
3091 : * background-blend-mode
3092 : * box-decoration-break
3093 : * mask-mode
3094 : * mask-composite
3095 : *
3096 : * (background-color applies to the entire element and not to individual
3097 : * layers, so it is irrelevant to this method.)
3098 : *
3099 : * These properties have the following dependencies upon each other when
3100 : * determining rendering:
3101 : *
3102 : * background-image/ mask-image
3103 : * no dependencies
3104 : * background-repeat/ mask-repeat
3105 : * no dependencies
3106 : * background-attachment
3107 : * no dependencies
3108 : * background-position/ mask-position
3109 : * depends upon background-size/mask-size (for the image's scaled size)
3110 : * and background-break (for the background positioning area)
3111 : * background-clip/ mask-clip
3112 : * no dependencies
3113 : * background-origin/ mask-origin
3114 : * depends upon background-attachment (only in the case where that value
3115 : * is 'fixed')
3116 : * background-size/ mask-size
3117 : * depends upon box-decoration-break (for the background positioning area
3118 : * for resolving percentages), background-image (for the image's intrinsic
3119 : * size), background-repeat (if that value is 'round'), and
3120 : * background-origin (for the background painting area, when
3121 : * background-repeat is 'round')
3122 : * background-blend-mode
3123 : * no dependencies
3124 : * mask-mode
3125 : * no dependencies
3126 : * mask-composite
3127 : * no dependencies
3128 : * box-decoration-break
3129 : * no dependencies
3130 : *
3131 : * As a result of only-if dependencies we don't strictly do a topological
3132 : * sort of the above properties when processing, but it's pretty close to one:
3133 : *
3134 : * background-clip/mask-clip (by caller)
3135 : * background-image/ mask-image
3136 : * box-decoration-break, background-origin/ mask origin
3137 : * background-attachment (postfix for background-origin if 'fixed')
3138 : * background-size/ mask-size
3139 : * background-position/ mask-position
3140 : * background-repeat/ mask-repeat
3141 : */
3142 :
3143 454 : uint32_t irFlags = 0;
3144 454 : if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
3145 0 : irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
3146 : }
3147 454 : if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
3148 446 : irFlags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
3149 : }
3150 :
3151 454 : nsBackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
3152 454 : if (!state.mImageRenderer.PrepareImage()) {
3153 : // There's no image or it's not ready to be painted.
3154 2 : if (aOutIsTransformedFixed) {
3155 2 : *aOutIsTransformedFixed = false;
3156 : }
3157 2 : return state;
3158 : }
3159 :
3160 : // The frame to which the background is attached
3161 452 : nsIFrame* attachedToFrame = aForFrame;
3162 : // Is the background marked 'fixed', but affected by a transform?
3163 452 : bool transformedFixed = false;
3164 : // Compute background origin area relative to aBorderArea now as we may need
3165 : // it to compute the effective image size for a CSS gradient.
3166 : nsRect positionArea =
3167 : ComputeImageLayerPositioningArea(aPresContext, aForFrame, aBorderArea,
3168 904 : aLayer, &attachedToFrame, &transformedFixed);
3169 452 : if (aOutIsTransformedFixed) {
3170 199 : *aOutIsTransformedFixed = transformedFixed;
3171 : }
3172 :
3173 : // For background-attachment:fixed backgrounds, we'll override the area
3174 : // where the background can be drawn to the viewport.
3175 904 : nsRect bgClipRect = aBGClipRect;
3176 :
3177 452 : if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment &&
3178 0 : !transformedFixed &&
3179 0 : (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW)) {
3180 0 : bgClipRect = positionArea + aBorderArea.TopLeft();
3181 : }
3182 :
3183 452 : StyleImageLayerRepeat repeatX = aLayer.mRepeat.mXRepeat;
3184 452 : StyleImageLayerRepeat repeatY = aLayer.mRepeat.mYRepeat;
3185 :
3186 : // Scale the image as specified for background-size and background-repeat.
3187 : // Also as required for proper background positioning when background-position
3188 : // is defined with percentages.
3189 452 : CSSSizeOrRatio intrinsicSize = state.mImageRenderer.ComputeIntrinsicSize();
3190 452 : nsSize bgPositionSize = positionArea.Size();
3191 : nsSize imageSize = ComputeDrawnSizeForBackground(intrinsicSize,
3192 : bgPositionSize,
3193 : aLayer.mSize,
3194 : repeatX,
3195 452 : repeatY);
3196 :
3197 452 : if (imageSize.width <= 0 || imageSize.height <= 0)
3198 0 : return state;
3199 :
3200 452 : state.mImageRenderer.SetPreferredSize(intrinsicSize,
3201 452 : imageSize);
3202 :
3203 : // Compute the anchor point.
3204 : //
3205 : // relative to aBorderArea.TopLeft() (which is where the top-left
3206 : // of aForFrame's border-box will be rendered)
3207 452 : nsPoint imageTopLeft;
3208 :
3209 : // Compute the position of the background now that the background's size is
3210 : // determined.
3211 452 : nsImageRenderer::ComputeObjectAnchorPoint(aLayer.mPosition,
3212 : bgPositionSize, imageSize,
3213 452 : &imageTopLeft, &state.mAnchor);
3214 452 : state.mRepeatSize = imageSize;
3215 452 : if (repeatX == StyleImageLayerRepeat::Space) {
3216 : bool isRepeat;
3217 0 : state.mRepeatSize.width = ComputeSpacedRepeatSize(imageSize.width,
3218 : bgPositionSize.width,
3219 : isRepeat);
3220 0 : if (isRepeat) {
3221 0 : imageTopLeft.x = 0;
3222 0 : state.mAnchor.x = 0;
3223 : } else {
3224 0 : repeatX = StyleImageLayerRepeat::NoRepeat;
3225 : }
3226 : }
3227 :
3228 452 : if (repeatY == StyleImageLayerRepeat::Space) {
3229 : bool isRepeat;
3230 0 : state.mRepeatSize.height = ComputeSpacedRepeatSize(imageSize.height,
3231 : bgPositionSize.height,
3232 : isRepeat);
3233 0 : if (isRepeat) {
3234 0 : imageTopLeft.y = 0;
3235 0 : state.mAnchor.y = 0;
3236 : } else {
3237 0 : repeatY = StyleImageLayerRepeat::NoRepeat;
3238 : }
3239 : }
3240 :
3241 452 : imageTopLeft += positionArea.TopLeft();
3242 452 : state.mAnchor += positionArea.TopLeft();
3243 452 : state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
3244 452 : state.mFillArea = state.mDestArea;
3245 :
3246 452 : ExtendMode repeatMode = ExtendMode::CLAMP;
3247 452 : if (repeatX == StyleImageLayerRepeat::Repeat ||
3248 58 : repeatX == StyleImageLayerRepeat::Round ||
3249 : repeatX == StyleImageLayerRepeat::Space) {
3250 394 : state.mFillArea.x = bgClipRect.x;
3251 394 : state.mFillArea.width = bgClipRect.width;
3252 394 : repeatMode = ExtendMode::REPEAT_X;
3253 : }
3254 452 : if (repeatY == StyleImageLayerRepeat::Repeat ||
3255 170 : repeatY == StyleImageLayerRepeat::Round ||
3256 : repeatY == StyleImageLayerRepeat::Space) {
3257 282 : state.mFillArea.y = bgClipRect.y;
3258 282 : state.mFillArea.height = bgClipRect.height;
3259 :
3260 : /***
3261 : * We're repeating on the X axis already,
3262 : * so if we have to repeat in the Y axis,
3263 : * we really need to repeat in both directions.
3264 : */
3265 282 : if (repeatMode == ExtendMode::REPEAT_X) {
3266 282 : repeatMode = ExtendMode::REPEAT;
3267 : } else {
3268 0 : repeatMode = ExtendMode::REPEAT_Y;
3269 : }
3270 : }
3271 452 : state.mImageRenderer.SetExtendMode(repeatMode);
3272 452 : state.mImageRenderer.SetMaskOp(aLayer.mMaskMode);
3273 :
3274 452 : state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
3275 :
3276 452 : return state;
3277 : }
3278 :
3279 : nsRect
3280 194 : nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
3281 : nsIFrame* aForFrame,
3282 : const nsRect& aBorderArea,
3283 : const nsRect& aClipRect,
3284 : const nsStyleImageLayers::Layer& aLayer,
3285 : uint32_t aFlags)
3286 : {
3287 194 : Sides skipSides = aForFrame->GetSkipSides();
3288 : nsRect borderArea =
3289 388 : ::BoxDecorationRectForBackground(aForFrame, aBorderArea, skipSides);
3290 : nsBackgroundLayerState state =
3291 : PrepareImageLayer(aPresContext, aForFrame, aFlags, borderArea,
3292 388 : aClipRect, aLayer);
3293 388 : return state.mFillArea;
3294 : }
3295 :
3296 : // Begin table border-collapsing section
3297 : // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
3298 : // At some point, all functions should be unified to include the additional functionality that these provide
3299 :
3300 : static nscoord
3301 0 : RoundIntToPixel(nscoord aValue,
3302 : nscoord aTwipsPerPixel,
3303 : bool aRoundDown = false)
3304 : {
3305 0 : if (aTwipsPerPixel <= 0)
3306 : // We must be rendering to a device that has a resolution greater than Twips!
3307 : // In that case, aValue is as accurate as it's going to get.
3308 0 : return aValue;
3309 :
3310 0 : nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
3311 0 : nscoord extra = aValue % aTwipsPerPixel;
3312 0 : nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
3313 0 : return finalValue;
3314 : }
3315 :
3316 : static nscoord
3317 0 : RoundFloatToPixel(float aValue,
3318 : nscoord aTwipsPerPixel,
3319 : bool aRoundDown = false)
3320 : {
3321 0 : return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
3322 : }
3323 :
3324 0 : static void SetPoly(const Rect& aRect, Point* poly)
3325 : {
3326 0 : poly[0].x = aRect.x;
3327 0 : poly[0].y = aRect.y;
3328 0 : poly[1].x = aRect.x + aRect.width;
3329 0 : poly[1].y = aRect.y;
3330 0 : poly[2].x = aRect.x + aRect.width;
3331 0 : poly[2].y = aRect.y + aRect.height;
3332 0 : poly[3].x = aRect.x;
3333 0 : poly[3].y = aRect.y + aRect.height;
3334 0 : }
3335 :
3336 : static void
3337 0 : DrawDashedSegment(DrawTarget& aDrawTarget,
3338 : nsRect aRect,
3339 : nscoord aDashLength,
3340 : nscolor aColor,
3341 : int32_t aAppUnitsPerDevPixel,
3342 : nscoord aTwipsPerPixel,
3343 : bool aHorizontal)
3344 : {
3345 0 : ColorPattern color(ToDeviceColor(aColor));
3346 0 : DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE);
3347 0 : StrokeOptions strokeOptions;
3348 :
3349 : Float dash[2];
3350 0 : dash[0] = Float(aDashLength) / aAppUnitsPerDevPixel;
3351 0 : dash[1] = dash[0];
3352 :
3353 0 : strokeOptions.mDashPattern = dash;
3354 0 : strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
3355 :
3356 0 : if (aHorizontal) {
3357 0 : nsPoint left = (aRect.TopLeft() + aRect.BottomLeft()) / 2;
3358 0 : nsPoint right = (aRect.TopRight() + aRect.BottomRight()) / 2;
3359 0 : strokeOptions.mLineWidth = Float(aRect.height) / aAppUnitsPerDevPixel;
3360 : StrokeLineWithSnapping(left, right,
3361 : aAppUnitsPerDevPixel, aDrawTarget,
3362 0 : color, strokeOptions, drawOptions);
3363 : } else {
3364 0 : nsPoint top = (aRect.TopLeft() + aRect.TopRight()) / 2;
3365 0 : nsPoint bottom = (aRect.BottomLeft() + aRect.BottomRight()) / 2;
3366 0 : strokeOptions.mLineWidth = Float(aRect.width) / aAppUnitsPerDevPixel;
3367 : StrokeLineWithSnapping(top, bottom,
3368 : aAppUnitsPerDevPixel, aDrawTarget,
3369 0 : color, strokeOptions, drawOptions);
3370 : }
3371 0 : }
3372 :
3373 : static void
3374 0 : DrawSolidBorderSegment(DrawTarget& aDrawTarget,
3375 : nsRect aRect,
3376 : nscolor aColor,
3377 : int32_t aAppUnitsPerDevPixel,
3378 : nscoord aTwipsPerPixel,
3379 : uint8_t aStartBevelSide = 0,
3380 : nscoord aStartBevelOffset = 0,
3381 : uint8_t aEndBevelSide = 0,
3382 : nscoord aEndBevelOffset = 0)
3383 : {
3384 0 : ColorPattern color(ToDeviceColor(aColor));
3385 0 : DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE);
3386 :
3387 : // We don't need to bevel single pixel borders
3388 0 : if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) ||
3389 0 : ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
3390 : // simple rectangle
3391 0 : aDrawTarget.FillRect(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel,
3392 0 : aDrawTarget),
3393 0 : color, drawOptions);
3394 : }
3395 : else {
3396 : // polygon with beveling
3397 0 : Point poly[4];
3398 0 : SetPoly(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, aDrawTarget),
3399 0 : poly);
3400 :
3401 : Float startBevelOffset =
3402 0 : NSAppUnitsToFloatPixels(aStartBevelOffset, aAppUnitsPerDevPixel);
3403 0 : switch(aStartBevelSide) {
3404 : case eSideTop:
3405 0 : poly[0].x += startBevelOffset;
3406 0 : break;
3407 : case eSideBottom:
3408 0 : poly[3].x += startBevelOffset;
3409 0 : break;
3410 : case eSideRight:
3411 0 : poly[1].y += startBevelOffset;
3412 0 : break;
3413 : case eSideLeft:
3414 0 : poly[0].y += startBevelOffset;
3415 : }
3416 :
3417 : Float endBevelOffset =
3418 0 : NSAppUnitsToFloatPixels(aEndBevelOffset, aAppUnitsPerDevPixel);
3419 0 : switch(aEndBevelSide) {
3420 : case eSideTop:
3421 0 : poly[1].x -= endBevelOffset;
3422 0 : break;
3423 : case eSideBottom:
3424 0 : poly[2].x -= endBevelOffset;
3425 0 : break;
3426 : case eSideRight:
3427 0 : poly[2].y -= endBevelOffset;
3428 0 : break;
3429 : case eSideLeft:
3430 0 : poly[3].y -= endBevelOffset;
3431 : }
3432 :
3433 0 : RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
3434 0 : builder->MoveTo(poly[0]);
3435 0 : builder->LineTo(poly[1]);
3436 0 : builder->LineTo(poly[2]);
3437 0 : builder->LineTo(poly[3]);
3438 0 : builder->Close();
3439 0 : RefPtr<Path> path = builder->Finish();
3440 0 : aDrawTarget.Fill(path, color, drawOptions);
3441 : }
3442 0 : }
3443 :
3444 : static void
3445 0 : GetDashInfo(nscoord aBorderLength,
3446 : nscoord aDashLength,
3447 : nscoord aTwipsPerPixel,
3448 : int32_t& aNumDashSpaces,
3449 : nscoord& aStartDashLength,
3450 : nscoord& aEndDashLength)
3451 : {
3452 0 : aNumDashSpaces = 0;
3453 0 : if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
3454 0 : aStartDashLength = aBorderLength;
3455 0 : aEndDashLength = 0;
3456 : }
3457 : else {
3458 0 : aNumDashSpaces = (aBorderLength - aDashLength)/ (2 * aDashLength); // round down
3459 0 : nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
3460 0 : if (extra > 0) {
3461 0 : nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
3462 0 : aStartDashLength += half;
3463 0 : aEndDashLength += (extra - half);
3464 : }
3465 : }
3466 0 : }
3467 :
3468 : void
3469 0 : nsCSSRendering::DrawTableBorderSegment(DrawTarget& aDrawTarget,
3470 : uint8_t aBorderStyle,
3471 : nscolor aBorderColor,
3472 : nscolor aBGColor,
3473 : const nsRect& aBorder,
3474 : int32_t aAppUnitsPerDevPixel,
3475 : int32_t aAppUnitsPerCSSPixel,
3476 : uint8_t aStartBevelSide,
3477 : nscoord aStartBevelOffset,
3478 : uint8_t aEndBevelSide,
3479 : nscoord aEndBevelOffset)
3480 : {
3481 0 : bool horizontal = ((eSideTop == aStartBevelSide) || (eSideBottom == aStartBevelSide));
3482 0 : nscoord twipsPerPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel);
3483 0 : uint8_t ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
3484 :
3485 0 : if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) ||
3486 0 : (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
3487 : // no beveling for 1 pixel border, dash or dot
3488 0 : aStartBevelOffset = 0;
3489 0 : aEndBevelOffset = 0;
3490 : }
3491 :
3492 0 : switch (aBorderStyle) {
3493 : case NS_STYLE_BORDER_STYLE_NONE:
3494 : case NS_STYLE_BORDER_STYLE_HIDDEN:
3495 : //NS_ASSERTION(false, "style of none or hidden");
3496 0 : break;
3497 : case NS_STYLE_BORDER_STYLE_DOTTED:
3498 : case NS_STYLE_BORDER_STYLE_DASHED:
3499 : {
3500 0 : nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
3501 : // make the dash length proportional to the border thickness
3502 0 : dashLength *= (horizontal) ? aBorder.height : aBorder.width;
3503 : // make the min dash length for the ends 1/2 the dash length
3504 : nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle)
3505 0 : ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength;
3506 0 : minDashLength = std::max(minDashLength, twipsPerPixel);
3507 0 : nscoord numDashSpaces = 0;
3508 0 : nscoord startDashLength = minDashLength;
3509 0 : nscoord endDashLength = minDashLength;
3510 0 : if (horizontal) {
3511 0 : GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces,
3512 0 : startDashLength, endDashLength);
3513 0 : nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
3514 0 : DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
3515 0 : aAppUnitsPerDevPixel, twipsPerPixel);
3516 :
3517 0 : rect.x += startDashLength + dashLength;
3518 0 : rect.width = aBorder.width
3519 0 : - (startDashLength + endDashLength + dashLength);
3520 0 : DrawDashedSegment(aDrawTarget, rect, dashLength, aBorderColor,
3521 0 : aAppUnitsPerDevPixel, twipsPerPixel, horizontal);
3522 :
3523 0 : rect.x += rect.width;
3524 0 : rect.width = endDashLength;
3525 0 : DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
3526 0 : aAppUnitsPerDevPixel, twipsPerPixel);
3527 : }
3528 : else {
3529 0 : GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces,
3530 0 : startDashLength, endDashLength);
3531 0 : nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
3532 0 : DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
3533 0 : aAppUnitsPerDevPixel, twipsPerPixel);
3534 :
3535 0 : rect.y += rect.height + dashLength;
3536 0 : rect.height = aBorder.height
3537 0 : - (startDashLength + endDashLength + dashLength);
3538 0 : DrawDashedSegment(aDrawTarget, rect, dashLength, aBorderColor,
3539 0 : aAppUnitsPerDevPixel, twipsPerPixel, horizontal);
3540 :
3541 0 : rect.y += rect.height;
3542 0 : rect.height = endDashLength;
3543 0 : DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
3544 0 : aAppUnitsPerDevPixel, twipsPerPixel);
3545 : }
3546 : }
3547 0 : break;
3548 : case NS_STYLE_BORDER_STYLE_GROOVE:
3549 0 : ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
3550 : MOZ_FALLTHROUGH;
3551 : case NS_STYLE_BORDER_STYLE_RIDGE:
3552 0 : if ((horizontal && (twipsPerPixel >= aBorder.height)) ||
3553 0 : (!horizontal && (twipsPerPixel >= aBorder.width))) {
3554 : // a one pixel border
3555 0 : DrawSolidBorderSegment(aDrawTarget, aBorder, aBorderColor,
3556 : aAppUnitsPerDevPixel, twipsPerPixel,
3557 : aStartBevelSide, aStartBevelOffset,
3558 0 : aEndBevelSide, aEndBevelOffset);
3559 : }
3560 : else {
3561 : nscoord startBevel = (aStartBevelOffset > 0)
3562 0 : ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, true) : 0;
3563 : nscoord endBevel = (aEndBevelOffset > 0)
3564 0 : ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, true) : 0;
3565 0 : mozilla::Side ridgeGrooveSide = (horizontal) ? eSideTop : eSideLeft;
3566 : // FIXME: In theory, this should use the visited-dependent
3567 : // background color, but I don't care.
3568 0 : nscolor bevelColor = MakeBevelColor(ridgeGrooveSide, ridgeGroove,
3569 0 : aBGColor, aBorderColor);
3570 0 : nsRect rect(aBorder);
3571 : nscoord half;
3572 0 : if (horizontal) { // top, bottom
3573 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
3574 0 : rect.height = half;
3575 0 : if (eSideTop == aStartBevelSide) {
3576 0 : rect.x += startBevel;
3577 0 : rect.width -= startBevel;
3578 : }
3579 0 : if (eSideTop == aEndBevelSide) {
3580 0 : rect.width -= endBevel;
3581 : }
3582 0 : DrawSolidBorderSegment(aDrawTarget, rect, bevelColor,
3583 : aAppUnitsPerDevPixel, twipsPerPixel,
3584 : aStartBevelSide, startBevel, aEndBevelSide,
3585 0 : endBevel);
3586 : }
3587 : else { // left, right
3588 0 : half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel);
3589 0 : rect.width = half;
3590 0 : if (eSideLeft == aStartBevelSide) {
3591 0 : rect.y += startBevel;
3592 0 : rect.height -= startBevel;
3593 : }
3594 0 : if (eSideLeft == aEndBevelSide) {
3595 0 : rect.height -= endBevel;
3596 : }
3597 0 : DrawSolidBorderSegment(aDrawTarget, rect, bevelColor,
3598 : aAppUnitsPerDevPixel, twipsPerPixel,
3599 : aStartBevelSide, startBevel, aEndBevelSide,
3600 0 : endBevel);
3601 : }
3602 :
3603 0 : rect = aBorder;
3604 0 : ridgeGrooveSide = (eSideTop == ridgeGrooveSide) ? eSideBottom : eSideRight;
3605 : // FIXME: In theory, this should use the visited-dependent
3606 : // background color, but I don't care.
3607 0 : bevelColor = MakeBevelColor(ridgeGrooveSide, ridgeGroove,
3608 0 : aBGColor, aBorderColor);
3609 0 : if (horizontal) {
3610 0 : rect.y = rect.y + half;
3611 0 : rect.height = aBorder.height - half;
3612 0 : if (eSideBottom == aStartBevelSide) {
3613 0 : rect.x += startBevel;
3614 0 : rect.width -= startBevel;
3615 : }
3616 0 : if (eSideBottom == aEndBevelSide) {
3617 0 : rect.width -= endBevel;
3618 : }
3619 0 : DrawSolidBorderSegment(aDrawTarget, rect, bevelColor,
3620 : aAppUnitsPerDevPixel, twipsPerPixel,
3621 : aStartBevelSide, startBevel, aEndBevelSide,
3622 0 : endBevel);
3623 : }
3624 : else {
3625 0 : rect.x = rect.x + half;
3626 0 : rect.width = aBorder.width - half;
3627 0 : if (eSideRight == aStartBevelSide) {
3628 0 : rect.y += aStartBevelOffset - startBevel;
3629 0 : rect.height -= startBevel;
3630 : }
3631 0 : if (eSideRight == aEndBevelSide) {
3632 0 : rect.height -= endBevel;
3633 : }
3634 0 : DrawSolidBorderSegment(aDrawTarget, rect, bevelColor,
3635 : aAppUnitsPerDevPixel, twipsPerPixel,
3636 : aStartBevelSide, startBevel, aEndBevelSide,
3637 0 : endBevel);
3638 : }
3639 : }
3640 0 : break;
3641 : case NS_STYLE_BORDER_STYLE_DOUBLE:
3642 : // We can only do "double" borders if the thickness of the border
3643 : // is more than 2px. Otherwise, we fall through to painting a
3644 : // solid border.
3645 0 : if ((aBorder.width > 2*twipsPerPixel || horizontal) &&
3646 0 : (aBorder.height > 2*twipsPerPixel || !horizontal)) {
3647 : nscoord startBevel = (aStartBevelOffset > 0)
3648 0 : ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0;
3649 : nscoord endBevel = (aEndBevelOffset > 0)
3650 0 : ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0;
3651 0 : if (horizontal) { // top, bottom
3652 0 : nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel);
3653 :
3654 : // draw the top line or rect
3655 0 : nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
3656 0 : if (eSideTop == aStartBevelSide) {
3657 0 : topRect.x += aStartBevelOffset - startBevel;
3658 0 : topRect.width -= aStartBevelOffset - startBevel;
3659 : }
3660 0 : if (eSideTop == aEndBevelSide) {
3661 0 : topRect.width -= aEndBevelOffset - endBevel;
3662 : }
3663 0 : DrawSolidBorderSegment(aDrawTarget, topRect, aBorderColor,
3664 : aAppUnitsPerDevPixel, twipsPerPixel,
3665 : aStartBevelSide, startBevel, aEndBevelSide,
3666 0 : endBevel);
3667 :
3668 : // draw the botom line or rect
3669 0 : nscoord heightOffset = aBorder.height - thirdHeight;
3670 0 : nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
3671 0 : if (eSideBottom == aStartBevelSide) {
3672 0 : bottomRect.x += aStartBevelOffset - startBevel;
3673 0 : bottomRect.width -= aStartBevelOffset - startBevel;
3674 : }
3675 0 : if (eSideBottom == aEndBevelSide) {
3676 0 : bottomRect.width -= aEndBevelOffset - endBevel;
3677 : }
3678 0 : DrawSolidBorderSegment(aDrawTarget, bottomRect, aBorderColor,
3679 : aAppUnitsPerDevPixel, twipsPerPixel,
3680 : aStartBevelSide, startBevel, aEndBevelSide,
3681 0 : endBevel);
3682 : }
3683 : else { // left, right
3684 0 : nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel);
3685 :
3686 0 : nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
3687 0 : if (eSideLeft == aStartBevelSide) {
3688 0 : leftRect.y += aStartBevelOffset - startBevel;
3689 0 : leftRect.height -= aStartBevelOffset - startBevel;
3690 : }
3691 0 : if (eSideLeft == aEndBevelSide) {
3692 0 : leftRect.height -= aEndBevelOffset - endBevel;
3693 : }
3694 0 : DrawSolidBorderSegment(aDrawTarget, leftRect, aBorderColor,
3695 : aAppUnitsPerDevPixel, twipsPerPixel,
3696 : aStartBevelSide, startBevel, aEndBevelSide,
3697 0 : endBevel);
3698 :
3699 0 : nscoord widthOffset = aBorder.width - thirdWidth;
3700 0 : nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
3701 0 : if (eSideRight == aStartBevelSide) {
3702 0 : rightRect.y += aStartBevelOffset - startBevel;
3703 0 : rightRect.height -= aStartBevelOffset - startBevel;
3704 : }
3705 0 : if (eSideRight == aEndBevelSide) {
3706 0 : rightRect.height -= aEndBevelOffset - endBevel;
3707 : }
3708 0 : DrawSolidBorderSegment(aDrawTarget, rightRect, aBorderColor,
3709 : aAppUnitsPerDevPixel, twipsPerPixel,
3710 : aStartBevelSide, startBevel, aEndBevelSide,
3711 0 : endBevel);
3712 : }
3713 0 : break;
3714 : }
3715 : // else fall through to solid
3716 : MOZ_FALLTHROUGH;
3717 : case NS_STYLE_BORDER_STYLE_SOLID:
3718 0 : DrawSolidBorderSegment(aDrawTarget, aBorder, aBorderColor,
3719 : aAppUnitsPerDevPixel, twipsPerPixel, aStartBevelSide,
3720 0 : aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
3721 0 : break;
3722 : case NS_STYLE_BORDER_STYLE_OUTSET:
3723 : case NS_STYLE_BORDER_STYLE_INSET:
3724 0 : NS_ASSERTION(false, "inset, outset should have been converted to groove, ridge");
3725 0 : break;
3726 : case NS_STYLE_BORDER_STYLE_AUTO:
3727 0 : NS_ASSERTION(false, "Unexpected 'auto' table border");
3728 0 : break;
3729 : }
3730 0 : }
3731 :
3732 : // End table border-collapsing section
3733 :
3734 : Rect
3735 0 : nsCSSRendering::ExpandPaintingRectForDecorationLine(
3736 : nsIFrame* aFrame,
3737 : const uint8_t aStyle,
3738 : const Rect& aClippedRect,
3739 : const Float aICoordInFrame,
3740 : const Float aCycleLength,
3741 : bool aVertical)
3742 : {
3743 0 : switch (aStyle) {
3744 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
3745 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
3746 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
3747 0 : break;
3748 : default:
3749 0 : NS_ERROR("Invalid style was specified");
3750 0 : return aClippedRect;
3751 : }
3752 :
3753 0 : nsBlockFrame* block = nullptr;
3754 : // Note that when we paint the decoration lines in relative positioned
3755 : // box, we should paint them like all of the boxes are positioned as static.
3756 0 : nscoord framePosInBlockAppUnits = 0;
3757 0 : for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
3758 0 : block = do_QueryFrame(f);
3759 0 : if (block) {
3760 0 : break;
3761 : }
3762 0 : framePosInBlockAppUnits += aVertical ?
3763 0 : f->GetNormalPosition().y : f->GetNormalPosition().x;
3764 : }
3765 :
3766 0 : NS_ENSURE_TRUE(block, aClippedRect);
3767 :
3768 0 : nsPresContext *pc = aFrame->PresContext();
3769 0 : Float framePosInBlock = Float(pc->AppUnitsToGfxUnits(framePosInBlockAppUnits));
3770 : int32_t rectPosInBlock =
3771 0 : int32_t(NS_round(framePosInBlock + aICoordInFrame));
3772 : int32_t extraStartEdge =
3773 0 : rectPosInBlock - (rectPosInBlock / int32_t(aCycleLength) * aCycleLength);
3774 0 : Rect rect(aClippedRect);
3775 0 : if (aVertical) {
3776 0 : rect.y -= extraStartEdge;
3777 0 : rect.height += extraStartEdge;
3778 : } else {
3779 0 : rect.x -= extraStartEdge;
3780 0 : rect.width += extraStartEdge;
3781 : }
3782 0 : return rect;
3783 : }
3784 :
3785 : void
3786 0 : nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, DrawTarget& aDrawTarget,
3787 : const PaintDecorationLineParams& aParams)
3788 : {
3789 0 : NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE,
3790 : "aStyle is none");
3791 :
3792 0 : Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
3793 0 : if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
3794 0 : return;
3795 : }
3796 :
3797 0 : if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
3798 0 : aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
3799 0 : aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
3800 0 : NS_ERROR("Invalid decoration value!");
3801 0 : return;
3802 : }
3803 :
3804 0 : Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0);
3805 :
3806 0 : ColorPattern color(ToDeviceColor(aParams.color));
3807 0 : StrokeOptions strokeOptions(lineThickness);
3808 0 : DrawOptions drawOptions;
3809 :
3810 : Float dash[2];
3811 :
3812 0 : AutoPopClips autoPopClips(&aDrawTarget);
3813 :
3814 0 : switch (aParams.style) {
3815 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3816 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
3817 0 : break;
3818 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
3819 0 : autoPopClips.PushClipRect(rect);
3820 0 : Float dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH;
3821 0 : dash[0] = dashWidth;
3822 0 : dash[1] = dashWidth;
3823 0 : strokeOptions.mDashPattern = dash;
3824 0 : strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
3825 0 : strokeOptions.mLineCap = CapStyle::BUTT;
3826 0 : rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style,
3827 0 : rect, aParams.icoordInFrame,
3828 : dashWidth * 2,
3829 0 : aParams.vertical);
3830 : // We should continue to draw the last dash even if it is not in the rect.
3831 0 : rect.width += dashWidth;
3832 0 : break;
3833 : }
3834 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: {
3835 0 : autoPopClips.PushClipRect(rect);
3836 0 : Float dashWidth = lineThickness * DOT_LENGTH;
3837 0 : if (lineThickness > 2.0) {
3838 0 : dash[0] = 0.f;
3839 0 : dash[1] = dashWidth * 2.f;
3840 0 : strokeOptions.mLineCap = CapStyle::ROUND;
3841 : } else {
3842 0 : dash[0] = dashWidth;
3843 0 : dash[1] = dashWidth;
3844 : }
3845 0 : strokeOptions.mDashPattern = dash;
3846 0 : strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
3847 0 : rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style,
3848 0 : rect, aParams.icoordInFrame,
3849 : dashWidth * 2,
3850 0 : aParams.vertical);
3851 : // We should continue to draw the last dot even if it is not in the rect.
3852 0 : rect.width += dashWidth;
3853 0 : break;
3854 : }
3855 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
3856 0 : autoPopClips.PushClipRect(rect);
3857 0 : if (lineThickness > 2.0) {
3858 0 : drawOptions.mAntialiasMode = AntialiasMode::SUBPIXEL;
3859 : } else {
3860 : // Don't use anti-aliasing here. Because looks like lighter color wavy
3861 : // line at this case. And probably, users don't think the
3862 : // non-anti-aliased wavy line is not pretty.
3863 0 : drawOptions.mAntialiasMode = AntialiasMode::NONE;
3864 : }
3865 0 : break;
3866 : default:
3867 0 : NS_ERROR("Invalid style value!");
3868 0 : return;
3869 : }
3870 :
3871 : // The block-direction position should be set to the middle of the line.
3872 0 : if (aParams.vertical) {
3873 0 : rect.x += lineThickness / 2;
3874 : } else {
3875 0 : rect.y += lineThickness / 2;
3876 : }
3877 :
3878 0 : switch (aParams.style) {
3879 : case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
3880 : case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
3881 : case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
3882 0 : Point p1 = rect.TopLeft();
3883 0 : Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
3884 0 : aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
3885 0 : return;
3886 : }
3887 : case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: {
3888 : /**
3889 : * We are drawing double line as:
3890 : *
3891 : * +-------------------------------------------+
3892 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3893 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
3894 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3895 : * | |
3896 : * | |
3897 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
3898 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
3899 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
3900 : * +-------------------------------------------+
3901 : */
3902 0 : Point p1 = rect.TopLeft();
3903 0 : Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
3904 0 : aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
3905 :
3906 0 : if (aParams.vertical) {
3907 0 : rect.width -= lineThickness;
3908 : } else {
3909 0 : rect.height -= lineThickness;
3910 : }
3911 :
3912 0 : p1 = aParams.vertical ? rect.TopRight() : rect.BottomLeft();
3913 0 : p2 = rect.BottomRight();
3914 0 : aDrawTarget.StrokeLine(p1, p2, color, strokeOptions, drawOptions);
3915 0 : return;
3916 : }
3917 : case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
3918 : /**
3919 : * We are drawing wavy line as:
3920 : *
3921 : * P: Path, X: Painted pixel
3922 : *
3923 : * +---------------------------------------+
3924 : * XX|X XXXXXX XXXXXX |
3925 : * PP|PX XPPPPPPX XPPPPPPX | ^
3926 : * XX|XPX XPXXXXXXPX XPXXXXXXPX| |
3927 : * | XPX XPX XPX XPX XP|X |adv
3928 : * | XPXXXXXXPX XPXXXXXXPX X|PX |
3929 : * | XPPPPPPX XPPPPPPX |XPX v
3930 : * | XXXXXX XXXXXX | XX
3931 : * +---------------------------------------+
3932 : * <---><---> ^
3933 : * adv flatLengthAtVertex rightMost
3934 : *
3935 : * 1. Always starts from top-left of the drawing area, however, we need
3936 : * to draw the line from outside of the rect. Because the start
3937 : * point of the line is not good style if we draw from inside it.
3938 : * 2. First, draw horizontal line from outside the rect to top-left of
3939 : * the rect;
3940 : * 3. Goes down to bottom of the area at 45 degrees.
3941 : * 4. Slides to right horizontaly, see |flatLengthAtVertex|.
3942 : * 5. Goes up to top of the area at 45 degrees.
3943 : * 6. Slides to right horizontaly.
3944 : * 7. Repeat from 2 until reached to right-most edge of the area.
3945 : *
3946 : * In the vertical case, swap horizontal and vertical coordinates and
3947 : * directions in the above description.
3948 : */
3949 :
3950 0 : Float& rectICoord = aParams.vertical ? rect.y : rect.x;
3951 0 : Float& rectISize = aParams.vertical ? rect.height : rect.width;
3952 0 : const Float rectBSize = aParams.vertical ? rect.width : rect.height;
3953 :
3954 0 : const Float adv = rectBSize - lineThickness;
3955 : const Float flatLengthAtVertex =
3956 0 : std::max((lineThickness - 1.0) * 2.0, 1.0);
3957 :
3958 : // Align the start of wavy lines to the nearest ancestor block.
3959 0 : const Float cycleLength = 2 * (adv + flatLengthAtVertex);
3960 0 : rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style, rect,
3961 0 : aParams.icoordInFrame,
3962 0 : cycleLength, aParams.vertical);
3963 : // figure out if we can trim whole cycles from the left and right edges
3964 : // of the line, to try and avoid creating an unnecessarily long and
3965 : // complex path
3966 0 : const Float dirtyRectICoord = aParams.vertical ? aParams.dirtyRect.y
3967 0 : : aParams.dirtyRect.x;
3968 0 : int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
3969 0 : if (skipCycles > 0) {
3970 0 : rectICoord += skipCycles * cycleLength;
3971 0 : rectISize -= skipCycles * cycleLength;
3972 : }
3973 :
3974 0 : rectICoord += lineThickness / 2.0;
3975 0 : Point pt(rect.TopLeft());
3976 0 : Float& ptICoord = aParams.vertical ? pt.y : pt.x;
3977 0 : Float& ptBCoord = aParams.vertical ? pt.x : pt.y;
3978 0 : if (aParams.vertical) {
3979 0 : ptBCoord += adv + lineThickness / 2.0;
3980 : }
3981 0 : Float iCoordLimit = ptICoord + rectISize + lineThickness;
3982 :
3983 0 : const Float dirtyRectIMost = aParams.vertical ?
3984 0 : aParams.dirtyRect.YMost() : aParams.dirtyRect.XMost();
3985 0 : skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength);
3986 0 : if (skipCycles > 0) {
3987 0 : iCoordLimit -= skipCycles * cycleLength;
3988 : }
3989 :
3990 0 : RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
3991 0 : RefPtr<Path> path;
3992 :
3993 0 : ptICoord -= lineThickness;
3994 0 : builder->MoveTo(pt); // 1
3995 :
3996 0 : ptICoord = rectICoord;
3997 0 : builder->LineTo(pt); // 2
3998 :
3999 : // In vertical mode, to go "down" relative to the text we need to
4000 : // decrease the block coordinate, whereas in horizontal we increase
4001 : // it. So the sense of this flag is effectively inverted.
4002 0 : bool goDown = aParams.vertical ? false : true;
4003 0 : uint32_t iter = 0;
4004 0 : while (ptICoord < iCoordLimit) {
4005 0 : if (++iter > 1000) {
4006 : // stroke the current path and start again, to avoid pathological
4007 : // behavior in cairo with huge numbers of path segments
4008 0 : path = builder->Finish();
4009 0 : aDrawTarget.Stroke(path, color, strokeOptions, drawOptions);
4010 0 : builder = aDrawTarget.CreatePathBuilder();
4011 0 : builder->MoveTo(pt);
4012 0 : iter = 0;
4013 : }
4014 0 : ptICoord += adv;
4015 0 : ptBCoord += goDown ? adv : -adv;
4016 :
4017 0 : builder->LineTo(pt); // 3 and 5
4018 :
4019 0 : ptICoord += flatLengthAtVertex;
4020 0 : builder->LineTo(pt); // 4 and 6
4021 :
4022 0 : goDown = !goDown;
4023 : }
4024 0 : path = builder->Finish();
4025 0 : aDrawTarget.Stroke(path, color, strokeOptions, drawOptions);
4026 0 : return;
4027 : }
4028 : default:
4029 0 : NS_ERROR("Invalid style value!");
4030 : }
4031 : }
4032 :
4033 : Rect
4034 0 : nsCSSRendering::DecorationLineToPath(const PaintDecorationLineParams& aParams)
4035 : {
4036 0 : NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE,
4037 : "aStyle is none");
4038 :
4039 0 : Rect path; // To benefit from RVO, we return this from all return points
4040 :
4041 0 : Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
4042 0 : if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
4043 0 : return path;
4044 : }
4045 :
4046 0 : if (aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
4047 0 : aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
4048 0 : aParams.decoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
4049 0 : NS_ERROR("Invalid decoration value!");
4050 0 : return path;
4051 : }
4052 :
4053 0 : if (aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
4054 : // For the moment, we support only solid text decorations.
4055 0 : return path;
4056 : }
4057 :
4058 0 : Float lineThickness = std::max(NS_round(aParams.lineSize.height), 1.0);
4059 :
4060 : // The block-direction position should be set to the middle of the line.
4061 0 : if (aParams.vertical) {
4062 0 : rect.x += lineThickness / 2;
4063 0 : path = Rect(rect.TopLeft() - Point(lineThickness / 2, 0.0),
4064 0 : Size(lineThickness, rect.Height()));
4065 : } else {
4066 0 : rect.y += lineThickness / 2;
4067 0 : path = Rect(rect.TopLeft() - Point(0.0, lineThickness / 2),
4068 0 : Size(rect.Width(), lineThickness));
4069 : }
4070 :
4071 0 : return path;
4072 : }
4073 :
4074 : nsRect
4075 0 : nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
4076 : const DecorationRectParams& aParams)
4077 : {
4078 0 : NS_ASSERTION(aPresContext, "aPresContext is null");
4079 0 : NS_ASSERTION(aParams.style != NS_STYLE_TEXT_DECORATION_STYLE_NONE,
4080 : "aStyle is none");
4081 :
4082 0 : gfxRect rect = GetTextDecorationRectInternal(Point(0, 0), aParams);
4083 : // The rect values are already rounded to nearest device pixels.
4084 0 : nsRect r;
4085 0 : r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
4086 0 : r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
4087 0 : r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
4088 0 : r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
4089 0 : return r;
4090 : }
4091 :
4092 : gfxRect
4093 0 : nsCSSRendering::GetTextDecorationRectInternal(const Point& aPt,
4094 : const DecorationRectParams& aParams)
4095 : {
4096 0 : NS_ASSERTION(aParams.style <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
4097 : "Invalid aStyle value");
4098 :
4099 0 : if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
4100 0 : return gfxRect(0, 0, 0, 0);
4101 :
4102 0 : bool canLiftUnderline = aParams.descentLimit >= 0.0;
4103 :
4104 0 : gfxFloat iCoord = aParams.vertical ? aPt.y : aPt.x;
4105 0 : gfxFloat bCoord = aParams.vertical ? aPt.x : aPt.y;
4106 :
4107 : // 'left' and 'right' are relative to the line, so for vertical writing modes
4108 : // they will actually become top and bottom of the rendered line.
4109 : // Similarly, aLineSize.width and .height are actually length and thickness
4110 : // of the line, which runs horizontally or vertically according to aVertical.
4111 0 : const gfxFloat left = floor(iCoord + 0.5),
4112 0 : right = floor(iCoord + aParams.lineSize.width + 0.5);
4113 :
4114 : // We compute |r| as if for a horizontal text run, and then swap vertical
4115 : // and horizontal coordinates at the end if vertical was requested.
4116 0 : gfxRect r(left, 0, right - left, 0);
4117 :
4118 0 : gfxFloat lineThickness = NS_round(aParams.lineSize.height);
4119 0 : lineThickness = std::max(lineThickness, 1.0);
4120 :
4121 0 : gfxFloat ascent = NS_round(aParams.ascent);
4122 0 : gfxFloat descentLimit = floor(aParams.descentLimit);
4123 :
4124 0 : gfxFloat suggestedMaxRectHeight = std::max(std::min(ascent, descentLimit), 1.0);
4125 0 : r.height = lineThickness;
4126 0 : if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
4127 : /**
4128 : * We will draw double line as:
4129 : *
4130 : * +-------------------------------------------+
4131 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4132 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4133 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4134 : * | | ^
4135 : * | | | gap
4136 : * | | v
4137 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
4138 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
4139 : * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
4140 : * +-------------------------------------------+
4141 : */
4142 0 : gfxFloat gap = NS_round(lineThickness / 2.0);
4143 0 : gap = std::max(gap, 1.0);
4144 0 : r.height = lineThickness * 2.0 + gap;
4145 0 : if (canLiftUnderline) {
4146 0 : if (r.Height() > suggestedMaxRectHeight) {
4147 : // Don't shrink the line height, because the thickness has some meaning.
4148 : // We can just shrink the gap at this time.
4149 0 : r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0 + 1.0);
4150 : }
4151 : }
4152 0 : } else if (aParams.style == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
4153 : /**
4154 : * We will draw wavy line as:
4155 : *
4156 : * +-------------------------------------------+
4157 : * |XXXXX XXXXXX XXXXXX | ^
4158 : * |XXXXXX XXXXXXXX XXXXXXXX | | lineThickness
4159 : * |XXXXXXX XXXXXXXXXX XXXXXXXXXX| v
4160 : * | XXX XXX XXX XXX XX|
4161 : * | XXXXXXXXXX XXXXXXXXXX X|
4162 : * | XXXXXXXX XXXXXXXX |
4163 : * | XXXXXX XXXXXX |
4164 : * +-------------------------------------------+
4165 : */
4166 0 : r.height = lineThickness > 2.0 ? lineThickness * 4.0 : lineThickness * 3.0;
4167 0 : if (canLiftUnderline) {
4168 0 : if (r.Height() > suggestedMaxRectHeight) {
4169 : // Don't shrink the line height even if there is not enough space,
4170 : // because the thickness has some meaning. E.g., the 1px wavy line and
4171 : // 2px wavy line can be used for different meaning in IME selections
4172 : // at same time.
4173 0 : r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0);
4174 : }
4175 : }
4176 : }
4177 :
4178 0 : gfxFloat baseline = floor(bCoord + aParams.ascent + 0.5);
4179 0 : gfxFloat offset = 0.0;
4180 0 : switch (aParams.decoration) {
4181 : case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
4182 0 : offset = aParams.offset;
4183 0 : if (canLiftUnderline) {
4184 0 : if (descentLimit < -offset + r.Height()) {
4185 : // If we can ignore the offset and the decoration line is overflowing,
4186 : // we should align the bottom edge of the decoration line rect if it's
4187 : // possible. Otherwise, we should lift up the top edge of the rect as
4188 : // far as possible.
4189 0 : gfxFloat offsetBottomAligned = -descentLimit + r.Height();
4190 0 : gfxFloat offsetTopAligned = 0.0;
4191 0 : offset = std::min(offsetBottomAligned, offsetTopAligned);
4192 : }
4193 : }
4194 0 : break;
4195 : case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
4196 0 : offset = aParams.offset - lineThickness + r.Height();
4197 0 : break;
4198 : case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
4199 0 : gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
4200 0 : extra = std::max(extra, lineThickness);
4201 0 : offset = aParams.offset - lineThickness + extra;
4202 0 : break;
4203 : }
4204 : default:
4205 0 : NS_ERROR("Invalid decoration value!");
4206 : }
4207 :
4208 0 : if (aParams.vertical) {
4209 0 : r.y = baseline + floor(offset + 0.5);
4210 0 : Swap(r.x, r.y);
4211 0 : Swap(r.width, r.height);
4212 : } else {
4213 0 : r.y = baseline - floor(offset + 0.5);
4214 : }
4215 :
4216 0 : return r;
4217 : }
4218 :
4219 : #define MAX_BLUR_RADIUS 300
4220 : #define MAX_SPREAD_RADIUS 50
4221 :
4222 102 : static inline gfxPoint ComputeBlurStdDev(nscoord aBlurRadius,
4223 : int32_t aAppUnitsPerDevPixel,
4224 : gfxFloat aScaleX,
4225 : gfxFloat aScaleY)
4226 : {
4227 : // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
4228 : // standard deviation of the blur should be half the given blur value.
4229 102 : gfxFloat blurStdDev = gfxFloat(aBlurRadius) / gfxFloat(aAppUnitsPerDevPixel);
4230 :
4231 408 : return gfxPoint(std::min((blurStdDev * aScaleX),
4232 306 : gfxFloat(MAX_BLUR_RADIUS)) / 2.0,
4233 306 : std::min((blurStdDev * aScaleY),
4234 408 : gfxFloat(MAX_BLUR_RADIUS)) / 2.0);
4235 : }
4236 :
4237 : static inline IntSize
4238 93 : ComputeBlurRadius(nscoord aBlurRadius,
4239 : int32_t aAppUnitsPerDevPixel,
4240 : gfxFloat aScaleX = 1.0,
4241 : gfxFloat aScaleY = 1.0)
4242 : {
4243 : gfxPoint scaledBlurStdDev = ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel,
4244 93 : aScaleX, aScaleY);
4245 : return
4246 93 : gfxAlphaBoxBlur::CalculateBlurRadius(scaledBlurStdDev);
4247 : }
4248 :
4249 : // -----
4250 : // nsContextBoxBlur
4251 : // -----
4252 : gfxContext*
4253 0 : nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
4254 : nscoord aBlurRadius,
4255 : int32_t aAppUnitsPerDevPixel,
4256 : gfxContext* aDestinationCtx,
4257 : const nsRect& aDirtyRect,
4258 : const gfxRect* aSkipRect,
4259 : uint32_t aFlags)
4260 : {
4261 0 : if (aRect.IsEmpty()) {
4262 0 : mContext = nullptr;
4263 0 : return nullptr;
4264 : }
4265 :
4266 0 : IntSize blurRadius;
4267 0 : IntSize spreadRadius;
4268 0 : GetBlurAndSpreadRadius(aDestinationCtx->GetDrawTarget(), aAppUnitsPerDevPixel,
4269 : aBlurRadius, aSpreadRadius,
4270 0 : blurRadius, spreadRadius);
4271 :
4272 0 : mDestinationCtx = aDestinationCtx;
4273 :
4274 : // If not blurring, draw directly onto the destination device
4275 0 : if (blurRadius.width <= 0 && blurRadius.height <= 0 &&
4276 0 : spreadRadius.width <= 0 && spreadRadius.height <= 0 &&
4277 0 : !(aFlags & FORCE_MASK)) {
4278 0 : mContext = aDestinationCtx;
4279 0 : return mContext;
4280 : }
4281 :
4282 : // Convert from app units to device pixels
4283 0 : gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
4284 :
4285 : gfxRect dirtyRect =
4286 0 : nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
4287 0 : dirtyRect.RoundOut();
4288 :
4289 0 : gfxMatrix transform = aDestinationCtx->CurrentMatrix();
4290 0 : rect = transform.TransformBounds(rect);
4291 :
4292 0 : mPreTransformed = !transform.IsIdentity();
4293 :
4294 : // Create the temporary surface for blurring
4295 0 : dirtyRect = transform.TransformBounds(dirtyRect);
4296 0 : if (aSkipRect) {
4297 0 : gfxRect skipRect = transform.TransformBounds(*aSkipRect);
4298 0 : mContext = mAlphaBoxBlur.Init(aDestinationCtx, rect, spreadRadius,
4299 0 : blurRadius, &dirtyRect, &skipRect);
4300 : } else {
4301 0 : mContext = mAlphaBoxBlur.Init(aDestinationCtx, rect, spreadRadius,
4302 0 : blurRadius, &dirtyRect, nullptr);
4303 : }
4304 :
4305 0 : if (mContext) {
4306 : // we don't need to blur if skipRect is equal to rect
4307 : // and mContext will be nullptr
4308 0 : mContext->Multiply(transform);
4309 : }
4310 0 : return mContext;
4311 : }
4312 :
4313 : void
4314 0 : nsContextBoxBlur::DoPaint()
4315 : {
4316 0 : if (mContext == mDestinationCtx) {
4317 0 : return;
4318 : }
4319 :
4320 0 : gfxContextMatrixAutoSaveRestore saveMatrix(mDestinationCtx);
4321 :
4322 0 : if (mPreTransformed) {
4323 0 : mDestinationCtx->SetMatrix(gfxMatrix());
4324 : }
4325 :
4326 0 : mAlphaBoxBlur.Paint(mDestinationCtx);
4327 : }
4328 :
4329 : gfxContext*
4330 0 : nsContextBoxBlur::GetContext()
4331 : {
4332 0 : return mContext;
4333 : }
4334 :
4335 : /* static */ nsMargin
4336 83 : nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
4337 : int32_t aAppUnitsPerDevPixel)
4338 : {
4339 83 : IntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
4340 :
4341 83 : nsMargin result;
4342 83 : result.top = result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
4343 83 : result.left = result.right = blurRadius.width * aAppUnitsPerDevPixel;
4344 83 : return result;
4345 : }
4346 :
4347 : /* static */ void
4348 9 : nsContextBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
4349 : const nsRect& aRect,
4350 : int32_t aAppUnitsPerDevPixel,
4351 : RectCornerRadii* aCornerRadii,
4352 : nscoord aBlurRadius,
4353 : const Color& aShadowColor,
4354 : const nsRect& aDirtyRect,
4355 : const gfxRect& aSkipRect)
4356 : {
4357 9 : DrawTarget& aDestDrawTarget = *aDestinationCtx->GetDrawTarget();
4358 :
4359 9 : if (aRect.IsEmpty()) {
4360 0 : return;
4361 : }
4362 :
4363 9 : Rect shadowGfxRect = NSRectToRect(aRect, aAppUnitsPerDevPixel);
4364 :
4365 9 : if (aBlurRadius <= 0) {
4366 0 : ColorPattern color(ToDeviceColor(aShadowColor));
4367 0 : if (aCornerRadii) {
4368 0 : RefPtr<Path> roundedRect = MakePathForRoundedRect(aDestDrawTarget,
4369 : shadowGfxRect,
4370 0 : *aCornerRadii);
4371 0 : aDestDrawTarget.Fill(roundedRect, color);
4372 : } else {
4373 0 : aDestDrawTarget.FillRect(shadowGfxRect, color);
4374 : }
4375 0 : return;
4376 : }
4377 :
4378 9 : gfxFloat scaleX = 1;
4379 9 : gfxFloat scaleY = 1;
4380 :
4381 : // Do blurs in device space when possible.
4382 : // Chrome/Skia always does the blurs in device space
4383 : // and will sometimes get incorrect results (e.g. rotated blurs)
4384 9 : gfxMatrix transform = aDestinationCtx->CurrentMatrix();
4385 : // XXX: we could probably handle negative scales but for now it's easier just to fallback
4386 9 : if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 && transform._22 > 0.0) {
4387 9 : scaleX = transform._11;
4388 9 : scaleY = transform._22;
4389 9 : aDestinationCtx->SetMatrix(gfxMatrix());
4390 : } else {
4391 0 : transform = gfxMatrix();
4392 : }
4393 :
4394 9 : gfxPoint blurStdDev = ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
4395 :
4396 : gfxRect dirtyRect =
4397 9 : nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
4398 9 : dirtyRect.RoundOut();
4399 :
4400 9 : gfxRect shadowThebesRect = transform.TransformBounds(ThebesRect(shadowGfxRect));
4401 9 : dirtyRect = transform.TransformBounds(dirtyRect);
4402 9 : gfxRect skipRect = transform.TransformBounds(aSkipRect);
4403 :
4404 9 : if (aCornerRadii) {
4405 9 : aCornerRadii->Scale(scaleX, scaleY);
4406 : }
4407 :
4408 : gfxAlphaBoxBlur::BlurRectangle(aDestinationCtx,
4409 : shadowThebesRect,
4410 : aCornerRadii,
4411 : blurStdDev,
4412 : aShadowColor,
4413 : dirtyRect,
4414 9 : skipRect);
4415 : }
4416 :
4417 : /* static */ void
4418 10 : nsContextBoxBlur::GetBlurAndSpreadRadius(DrawTarget* aDestDrawTarget,
4419 : int32_t aAppUnitsPerDevPixel,
4420 : nscoord aBlurRadius,
4421 : nscoord aSpreadRadius,
4422 : IntSize& aOutBlurRadius,
4423 : IntSize& aOutSpreadRadius,
4424 : bool aConstrainSpreadRadius)
4425 : {
4426 : // Do blurs in device space when possible.
4427 : // Chrome/Skia always does the blurs in device space
4428 : // and will sometimes get incorrect results (e.g. rotated blurs)
4429 10 : Matrix transform = aDestDrawTarget->GetTransform();
4430 : // XXX: we could probably handle negative scales but for now it's easier just to fallback
4431 : gfxFloat scaleX, scaleY;
4432 10 : if (transform.HasNonAxisAlignedTransform() || transform._11 <= 0.0 || transform._22 <= 0.0) {
4433 0 : scaleX = 1;
4434 0 : scaleY = 1;
4435 : } else {
4436 10 : scaleX = transform._11;
4437 10 : scaleY = transform._22;
4438 : }
4439 :
4440 : // compute a large or smaller blur radius
4441 10 : aOutBlurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
4442 10 : aOutSpreadRadius =
4443 20 : IntSize(int32_t(aSpreadRadius * scaleX / aAppUnitsPerDevPixel),
4444 10 : int32_t(aSpreadRadius * scaleY / aAppUnitsPerDevPixel));
4445 :
4446 :
4447 10 : if (aConstrainSpreadRadius) {
4448 0 : aOutSpreadRadius.width = std::min(aOutSpreadRadius.width, int32_t(MAX_SPREAD_RADIUS));
4449 0 : aOutSpreadRadius.height = std::min(aOutSpreadRadius.height, int32_t(MAX_SPREAD_RADIUS));
4450 : }
4451 10 : }
4452 :
4453 : /* static */ bool
4454 10 : nsContextBoxBlur::InsetBoxBlur(gfxContext* aDestinationCtx,
4455 : Rect aDestinationRect,
4456 : Rect aShadowClipRect,
4457 : Color& aShadowColor,
4458 : nscoord aBlurRadiusAppUnits,
4459 : nscoord aSpreadDistanceAppUnits,
4460 : int32_t aAppUnitsPerDevPixel,
4461 : bool aHasBorderRadius,
4462 : RectCornerRadii& aInnerClipRectRadii,
4463 : Rect aSkipRect, Point aShadowOffset)
4464 : {
4465 10 : if (aDestinationRect.IsEmpty()) {
4466 0 : mContext = nullptr;
4467 0 : return false;
4468 : }
4469 :
4470 20 : gfxContextAutoSaveRestore autoRestore(aDestinationCtx);
4471 :
4472 10 : IntSize blurRadius;
4473 10 : IntSize spreadRadius;
4474 : // Convert the blur and spread radius to device pixels
4475 10 : bool constrainSpreadRadius = false;
4476 10 : GetBlurAndSpreadRadius(aDestinationCtx->GetDrawTarget(), aAppUnitsPerDevPixel,
4477 : aBlurRadiusAppUnits, aSpreadDistanceAppUnits,
4478 10 : blurRadius, spreadRadius, constrainSpreadRadius);
4479 :
4480 : // The blur and spread radius are scaled already, so scale all
4481 : // input data to the blur. This way, we don't have to scale the min
4482 : // inset blur to the invert of the dest context, then rescale it back
4483 : // when we draw to the destination surface.
4484 10 : gfxSize scale = aDestinationCtx->CurrentMatrix().ScaleFactors(true);
4485 10 : Matrix transform = ToMatrix(aDestinationCtx->CurrentMatrix());
4486 :
4487 : // XXX: we could probably handle negative scales but for now it's easier just to fallback
4488 10 : if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 && transform._22 > 0.0) {
4489 : // If we don't have a rotation, we're pre-transforming all the rects.
4490 10 : aDestinationCtx->SetMatrix(gfxMatrix());
4491 : } else {
4492 : // Don't touch anything, we have a rotation.
4493 0 : transform = Matrix();
4494 : }
4495 :
4496 10 : Rect transformedDestRect = transform.TransformBounds(aDestinationRect);
4497 10 : Rect transformedShadowClipRect = transform.TransformBounds(aShadowClipRect);
4498 10 : Rect transformedSkipRect = transform.TransformBounds(aSkipRect);
4499 :
4500 10 : transformedDestRect.Round();
4501 10 : transformedShadowClipRect.Round();
4502 10 : transformedSkipRect.RoundIn();
4503 :
4504 50 : for (size_t i = 0; i < 4; i++) {
4505 40 : aInnerClipRectRadii[i].width = std::floor(scale.width * aInnerClipRectRadii[i].width);
4506 40 : aInnerClipRectRadii[i].height = std::floor(scale.height * aInnerClipRectRadii[i].height);
4507 : }
4508 :
4509 10 : mAlphaBoxBlur.BlurInsetBox(aDestinationCtx, transformedDestRect,
4510 : transformedShadowClipRect,
4511 : blurRadius, aShadowColor,
4512 : aHasBorderRadius ? &aInnerClipRectRadii : nullptr,
4513 10 : transformedSkipRect, aShadowOffset);
4514 10 : return true;
4515 : }
|