Coverage Report

Created: 2018-09-25 14:53

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