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